5

I'm encountering a problem where if I dynamically load a component, none of the bindings in the template are working for me. As well as this the ngOnInit method is never triggered.

loadView() {
    this._dcl.loadAsRoot(Injected, null, this._injector).then(component => {
      console.info('Component loaded');
    })
  }

Dynamically loaded component

import {Component, ElementRef, OnInit} from 'angular2/core'

declare var $:any

@Component({
    selector: 'tester',
    template: `
      <h1>Dynamically loaded component</h1>
        <span>{{title}}</span>
    `
})

export class Injected implements OnInit {

    public title:string = "Some text"

    constructor(){} 

    ngOnInit() {
      console.info('Injected onInit');
    }

}

This is my first time using dynamically loaded components so I think may be attempting to implement it incorrectly.

Here's a plunkr demonstrating the issue. Any help would be appreciated.

6
  • 2
    There's a known issue with loadAsRoot. Your safest bet for now is to use either loadNextToLocation or loadIntoLocation. Commented Jan 11, 2016 at 18:21
  • @EricMartinez The component I'm trying to load is a modal dialog. The component I'm trying to load it from is inside an element with a fixed css style, so the dialog needs to be loaded pretty much as a first child of the body tag. Can I do this with loadNextToLocation or loadIntoLocation from within a deeply nested component? Commented Jan 11, 2016 at 18:23
  • I think you can fix that with pure CSS. If you set every position to 0 (top, left, right, bottom), position fixed and a high z-index value you can have a modal dialog wherever it is loaded into. Commented Jan 11, 2016 at 18:33
  • @EricMartinez I appreciate that but I don't really want to go messing around with CSS that's working just fine for me because of this bug. I've decided to pass the root AppComponent with forward ref and am now using loadIntoLocation with the AppComponent's ElementRef. Thanks for your help. Commented Jan 11, 2016 at 18:41
  • You could add your solution as an answer, could be helpful for others Commented Jan 11, 2016 at 18:44

2 Answers 2

6

As Eric Martinez pointed out this is a known bug related to the use of loadAsRoot. The suggested workaround is to use loadNextToLocation or loadIntoLocation.

For me this was problematic as the component I was trying to dynamically load was a modal dialog from inside a component with fixed css positioning. I also wanted the ability to load the modal from any component and have it injected into the same position in the DOM regardless of what component it was dynamically loaded from.

My solution was to use forwardRef to inject my root AppComponent into the component which wants to dynamically load my modal.

constructor (
    .........
    .........
    private _dcl: DynamicComponentLoader,
    private _injector: Injector,
    @Inject(forwardRef(() => AppComponent)) appComponent) {

    this.appComponent = appComponent;
}

In my AppComponent I have a method which returns the app's ElementRef

@Component({
    selector: 'app',
    template: `
        <router-outlet></router-outlet>
        <div #modalContainer></div>
    `,
    directives: [RouterOutlet]
})

export class AppComponent {

    constructor(public el:ElementRef) {}

    getElementRef():ElementRef {
        return this.el;
    }

}

Back in my other component (the one that I want to dynamically load the modal from) I can now call:

this._dcl.loadIntoLocation(ModalComponent, this.appComponent.getElementRef(), 'modalContainer').then(component => {
    console.log('Component loaded')
})

Perhaps this will help others with similar problems.

Sign up to request clarification or add additional context in comments.

3 Comments

When I try it, the loadIntoLocation will attach the new component under the referenced tag (in your example, under modalContainer div. The problem is, when I call this twice, the component will be stacked with previously created component (latest component on top). What I want is like loadAsRoot behaviour where calling second time will replace previous component. Do you manually clean the DOM before calling the loadIntoLocation second time?
Where do I find @Inject documentation? Is not available yet in angular 2 docs...
The child component did load but the ngOnInit or ngAfterViewInit methods of the dynamically loaded component were called only when I do a mousemove. Did any of you face this issue ?
0

No need to clean component instance from DOM. use 'componentRef' from angular2/core package to create and dispose component instance. use show() to load the modal component at desired location and hide() to dispose the component instance before calling loadIntoLocation secondtime.

for eg:

@Component({
  selector: 'app',
  template: `
    <router-outlet></router-outlet>
    <div #modalContainer></div>
 `,
  directives: [RouterOutlet]
})
export class AppComponent {
  private component:Promise<ComponentRef>;

  constructor(public el:ElementRef) {}

  getElementRef():ElementRef {
    return this.el;
  }

  show(){
    this.component = this._dcl.loadIntoLocation(ModalComponent,this.appComponent.getElementRef(), 'modalContainer').then(component => {console.log('Component loaded')})
  }

  hide(){
    this.component.then((componentRef:ComponentRef) => {
      componentRef.dispose();
      return componentRef;
    });
  }
}

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.