I am currently working on a project that I upgraded from Angular 2 beta 13 to RC 4. Before the upgrade, the ability to dynamically add and remove a simple component (a simple spinner) to any page that was in the process of loading data worked fine, using this service:
(SinnerService.ts)
import {Injectable, DynamicComponentLoader, ApplicationRef, ElementRef,
ComponentRef} from '@angular/core';
import {Spinner} from './spinner';
@Injectable()
export class SpinnerService {
spinnerComp: ComponentRef;
constructor(private _componentLoader: DynamicComponentLoader, private _appRef: ApplicationRef) {
}
public start() {
let elementRef: ElementRef = this._appRef['_rootComponents'][0].location;
return this.startInside(elementRef, null);
}
public startInside(elementRef: ElementRef, anchorName: string) {
let spinnerRef = (!anchorName) ?
this._componentLoader.loadNextToLocation(Spinner, elementRef, anchorName) :
this._componentLoader.loadNextToLocation(Spinner, elementRef);
spinnerRef.then((compRef: ComponentRef) => {
this.spinnerComp = compRef;
});
}
public stop() {
if (this.spinnerComp) {
this.spinnerComp.dispose();
}
}
}
During the upgrade, changes were made to the class to match the changes made to Angular 2. The resulting file causes a exception to occur, which is shown underneath the updated code:
(SinnerService.ts changed for RC 4):
import {Injectable, DynamicComponentLoader, ApplicationRef, ViewContainerRef, ComponentRef} from '@angular/core';
import {Spinner} from './spinner';
@Injectable()
export class SpinnerService {
spinnerComp: ComponentRef<any>;
constructor(private _componentLoader: DynamicComponentLoader, private _appRef: ApplicationRef) {
}
public start() {
let elementRef: ViewContainerRef = this._appRef['_rootComponents'][0].location;
return this.startInside(elementRef, null);
}
public startInside(elementRef: ViewContainerRef, anchorName: string) {
let spinnerRef = (!anchorName) ?
this._componentLoader.loadNextToLocation(Spinner, elementRef) :
this._componentLoader.loadNextToLocation(Spinner, elementRef);
spinnerRef.then((compRef: ComponentRef<any>) => {
this.spinnerComp = compRef;
});
}
public stop() {
if (this.spinnerComp) {
this.spinnerComp.destroy();
}
}
}
The error:
EXCEPTION: Error: Uncaught (in promise): TypeError:
location.createComponent is not a function
browser_adapter.ts:82
EXCEPTION: Error: Uncaught (in promise): TypeError:
location.createComponent is not a functionBrowser DomAdapter.logError @ browser_adapter.ts:82
browser_adapter.ts:82
STACKTRACE:BrowserDomAdapter.logError @ browser_adapter.ts:82
browser_adapter.ts:82
Error: Uncaught (in promise): TypeError:
location.createComponent is not a function
at resolvePromise (zone.js:538)
at resolvePromise (zone.js:523)
at zone.js:571
at ZoneDelegate.invokeTask (zone.js:356)
at Object.onInvokeTask (ng_zone_impl.ts:61)
at ZoneDelegate.invokeTask (zone.js:355)
at Zone.runTask (zone.js:256)
at drainMicroTaskQueue (zone.js:474)
at XMLHttpRequest.ZoneTask.invoke (zone.js:426) BrowserDomAdapter.logError @ browser_adapter.ts:82 zone.js:461
Unhandled Promise rejection: location.createComponent is not a
function ; Zone: angular ; Task: Promise.then ; Value: TypeError:
location.createComponent is not a function(…) consoleError @ zone.js:461
zone.js:463
Error: Uncaught (in promise): TypeError: location.createComponent is not a function(…)
So, I read a lot about this topic and tried several ways to use DynamicComponentLoader.loadNextToLocation(...). During that investigation I also noticed that this component seems that it is being deprecated by Google. ( https://angular.io/docs/js/latest/api/core/index/DynamicComponentLoader-class.html )
I the list that Google keeps on Angular 2 changes, it is mentioned that you have to use ViewContainerRef instead of ElementRef, but there is no example of how this should be done.
After a lot more digging, I saw some suggestions to use the ComponentResolver instead of DynamicComponentLoader.loadNextToLocation(...) like so:
(SinnerService.ts changed to use ComponentResolver):
import {Injectable, DynamicComponentLoader, ApplicationRef, ViewContainerRef, Component, ComponentRef, ComponentResolver, ComponentFactory, ViewChild} from '@angular/core';
import {Spinner} from './spinner';
@Injectable()
export class SpinnerService {
spinnerComp: ComponentRef<any>;
constructor(private _componentLoader: DynamicComponentLoader, private _appRef: ApplicationRef,
private _resolver: ComponentResolver) {
}
public start() {
let elementRef: ViewContainerRef = this._appRef['_rootComponents'][0].location;
return this.startInside(elementRef, null);
}
public startInside(elementRef: ViewContainerRef, anchorName: string) {
//let spinnerRef = (!anchorName) ?
// this._componentLoader.loadNextToLocation(Spinner, elementRef) :
// this._componentLoader.loadNextToLocation(Spinner, elementRef);
DynamicComponentLoader
//spinnerRef.then((compRef: ComponentRef<any>) => {
// this.spinnerComp = compRef;
//});
let spinnerRef = this._resolver.resolveComponent(Spinner);
spinnerRef.then((factory: ComponentFactory<any>) => {
this.spinnerComp = elementRef.createComponent(factory)
});
}
public stop() {
if (this.spinnerComp) {
this.spinnerComp.destroy();
}
}
}
However, this also caused an error to be thrown that is almost identical to the previous error:
EXCEPTION: Error: Uncaught (in promise): TypeError:
elementRef.createComponent is not a function
browser_adapter.ts:82
EXCEPTION: Error: Uncaught (in promise): TypeError:
elementRef.createComponent is not a function BrowserDomAdapter.logError @ browser_adapter.ts:82 browser_adapter.ts:82
STACKTRACE:BrowserDomAdapter.logError @ browser_adapter.ts:82
browser_adapter.ts:82
Error: Uncaught (in promise): TypeError:
elementRef.createComponent is not a function
at resolvePromise (zone.js:538)
at zone.js:574
at ZoneDelegate.invokeTask (zone.js:356)
at Object.onInvokeTask (ng_zone_impl.ts:61)
at ZoneDelegate.invokeTask (zone.js:355)
at Zone.runTask (zone.js:256)
at drainMicroTaskQueue (zone.js:474)
at XMLHttpRequest.ZoneTask.invoke (zone.js:426)
BrowserDomAdapter.logError @ browser_adapter.ts:82
zone.js:461
Unhandled Promise rejection: elementRef.createComponent is not a
function ; Zone: angular ; Task: Promise.then ; Value: TypeError:
elementRef.createComponent is not a function(…) consoleError @ zone.js:461
zone.js:463
Error: Uncaught (in promise): TypeError: elementRef.createComponent is
not a function(…) consoleError @ zone.js:463
SignalRService.js:40 Event hub started.
So, it seems that this vital functionality is broken.
Has anyone else used this functionality successfully?
I submitted a bug to Google on their GitHub repository, but I was hoping that someone else may have already ran into this issue and had a valid way to do this or a work around.
Any help would be appreciated. Thanks!
You need to use the ComponentResolver
and ViewContainerRef
classes instead. Here is a simple sample:
@Component({
selector: 'my-app',
template: '<template #target></template>'
})
export class AppComponent {
@ViewChild('target', {read: ViewContainerRef}) target;
componentRef: ComponentRef;
constructor(private componentResolver:ComponentResolver) {}
ngAfterViewInit() {
this.componentResolver.resolveComponent(SomeComponent).then((factory) => {
this.componentRef = this.target.createComponent(factory);
});
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.