简体   繁体   中英

Angular 2 RC 4 Dynamically adding & removing components with DynamicComponentLoader.loadNextToLocation(…) is failing

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM