Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

DynamicComponentLoader in Angular2 beta: "no component directive at element"

Tags:

angular

I can't get angular's DynamicComponentLoader to work in beta.6

I've taken the example code for dynamic component loading from their docs, and added it to their working starter code in plnkr, see here.

Here's the code:

import {Component, DynamicComponentLoader, ElementRef} from 'angular2/core';


@Component({
  selector: 'child-component',
  template: 'Child'
})
class ChildComponent {
}


@Component({
    selector: 'my-app',
    template: '<h1>My First Angular 2 App</h1><div #child></div>'
})
export class AppComponent { 
  constructor(dcl: DynamicComponentLoader, elementRef: ElementRef) {
    dcl.loadIntoLocation(ChildComponent, elementRef, 'child');
  }
}

And here is the stacktrace, which says: "There is no component directive at element":

EXCEPTION: Error during instantiation of AppComponent!.BrowserDomAdapter.logError @ angular2.dev.js:23083
angular2.dev.js:23083 ORIGINAL EXCEPTION: There is no component directive at element [object Object]BrowserDomAdapter.logError @ angular2.dev.js:23083
angular2.dev.js:23083 ORIGINAL STACKTRACE:BrowserDomAdapter.logError @ angular2.dev.js:23083
angular2.dev.js:23083 Error: There is no component directive at element [object Object]
    at new BaseException (angular2.dev.js:7351)
    at AppViewManager_.getNamedElementInComponentView (angular2.dev.js:6441)
    at DynamicComponentLoader_.loadIntoLocation (angular2.dev.js:12375)
    at new AppComponent (run.plnkr.co/mk31ybnwEtsiX31r/app/app.component.ts!transpiled:33)
    at angular2.dev.js:1420
    at Injector._instantiate (angular2.dev.js:11459)
    at Injector._instantiateProvider (angular2.dev.js:11395)
    at Injector._new (angular2.dev.js:11385)
    at InjectorInlineStrategy.instantiateProvider (angular2.dev.js:11149)
    at ElementDirectiveInlineStrategy.init (angular2.dev.js:9081)BrowserDomAdapter.logError @ angular2.dev.js:23083
angular2.dev.js:23083 ERROR CONTEXT:BrowserDomAdapter.logError @ angular2.dev.js:23083
angular2.dev.js:23083 _ContextcomponentElement: nullelement: my-appinjector: Injector__proto__: _ContextBrowserDomAdapter.logError @ angular2.dev.js:23083
angular2-polyfills.js:1152 DEPRECATION WARNING: 'dequeueTask' is no longer supported and will be removed in next major release. Use removeTask/removeRepeatingTask/removeMicroTask
like image 256
Hoff Avatar asked Feb 19 '16 15:02

Hoff


1 Answers

Update 4 (and final)

DynamicComponentLoader is being deprecated (see commit). Now the correct way is to use ViewContainerRef and ComponentResolver (see update 3 below) for an example in the constructor.



In beta.1 there was a breaking change. You can't access anymore the view in the constructor. So you can move that example to ngOnInit() and it will work.

Quoting from the changelog

Component view is not yet created when component constructor is called. -> use onInit lifecycle callback to access the view of a component

export class AppComponent { 
  constructor(private dcl: DynamicComponentLoader, private elementRef: ElementRef) {
   
  }
  ngOnInit() {
    this.dcl.loadIntoLocation(ChildComponent, this.elementRef, 'child');
  }
}

Check this plnkr with your example working.

Update

Just for your information I've sent a PR (see #7186) to fix the examples in the source code. So hopefully they will review it and it will pass.

Update 2

Since beta.16 loadIntoLocation was removed and the error is no longer reproducible. loadNextToLocation now takes a ViewContainerRef. I opened a new PR (see #8241) to add a new example of it, everything else is already covered by the original PR.

Code example (update 3)

As mentioned above now there's no loadIntoLocation but the same behavior is achieved by using ViewContainerRef and ComponentResolver.

constructor(cmpResolver: ComponentResolver, viewContainer: ViewContainerRef) {

  cmpResolver.resolveComponent(MyComponent)
    .then((factory: ComponentFactory) => {
        viewContainer.createComponent(factory, 0, viewContainer.injector)
    });
}

Reference

  • ViewContainerRef#createComponent
  • ComponentResolver#resolveComponent

Regarding loadNextToLocation, there are two ways, both use ViewContainerRef.

  • Using ViewContainerRef from the element itself
constructor(private dcl: DynamicComponentLoader, viewContainer: ViewContainerRef) {
    dcl.loadNextToLocation(MyComponent, viewContainer).then(ref => {});
}
  • Using some element on the view
// Assume the next view
template : '<div #container></div>';

class MyClass {
   @ViewChild('container', {read: ViewContainerRef}) container : ViewContainerRef;
    
   constructor(private dcl: DynamicComponentLoader) {}

   ngAfterViewInit() {
      this.dcl.loadNextToLocation(MyDynamicCmp, this.container).then(ref => {});
   }
}

A plnkr with the example above.

like image 51
Eric Martinez Avatar answered Dec 09 '22 09:12

Eric Martinez