0

I would like to create a ViewRef from markup that is dynamically inserted into a template. Is this possible based on the following code sample?

template.html:

<ng-container *ngTemplateOutlet="dynamic; context: cntx"></ng-container>
<ng-template #dynamic>
  <div [innerHTML]="markup"></div>
</ng-template>

Injected markup from API call to bind to div's innerHTML attribute:

<div>
    <div id="forViewRef"></div>
</div>

component.ts:

@ContentChild('#forViewRef', { read: ViewContainerRef }): someHndl;
private _nativeElem: any;

constructor(
    private sanitizer: DomSanitizer, 
    private _vcRef: ViewContainerRef, 
    private _resolver: ComponentFactoryResolver) {
    // to ensure template has been created, #dynamic
    this._nativeElem = this._vcRef.element.nativeElement;
}

// listen to lifecycle hook
ngAfterContentChecked() {
    if (this._nativeElem !== undefined)
        // childContent ref is undefined
        console.log(this.someHndl);
        // markup is in the DOM
        console.log(this._nativeElem.querySelectorAll('#forViewRef'));
}
Chris
  • 469
  • 12
  • 25
  • You can't create VIewRef. What are you trying to achive? – yurzui May 25 '17 at 19:03
  • The dynamic markup that I am injecting into the template contains divs with Id attributes. I expected to select against these div Ids to generate ViewContainerRef and further inject dynamic components. – Chris May 25 '17 at 20:19
  • You can create component and insert it somewhere by using `appendChild` – yurzui May 25 '17 at 20:20
  • Here is an example https://stackoverflow.com/questions/43448577/display-custom-tag-in-google-maps-infowindow-angular2/43448903#43448903 and here is another https://stackoverflow.com/questions/40922224/angular2-component-into-dynamicaly-created-element – yurzui May 25 '17 at 20:23
  • After reviewing the Renderer2#appendChild(parent any, newChild any) method, are you saying I can dynamically create and append a component? I am appending now. The issue is inserting, rather than appending the component. Reviewing ViewContainerRef#createComponent() method, it furnishes and index based on a node tree. Any suggestion on obtaining the index of each div with Id attribute. Based on the Id, I want to insert the dynamically generated component. – Chris May 25 '17 at 20:28
  • Just saw your SO references. First one looks promising. Giving it a try now... – Chris May 25 '17 at 20:32

1 Answers1

3

To create component dynamically inside <div id="forViewRef"></div> you can do the following:

Let's say we need to load the following component

@Component({
  selector: 'dynamic-comp',
  template: `
   <h2>Dynamic component</h2>
    <button (click)="counter = counter + 1">+</button> {{ counter }}
  `
})
export class DynamicComponent {
  counter = 1;
}

so first add it to declarations and entryComponents array of your @NgModule

  ...
  declarations: [ ..., DynamicComponent ],
  entryComponents: [ DynamicComponent ],
  bootstrap:    [ AppComponent ]
})
export class AppModule { }

after that create

template.html

<button (click)="createComponent()">Create component</button>

<div id="forViewRef"></div>

and finally write

component.ts

export class AppComponent {
  compRef: ComponentRef<DynamicComponent>;

  constructor(private injector: Injector,
              private resolver: ComponentFactoryResolver,
              private appRef: ApplicationRef) {}


  createComponent() {
    const compFactory = this.resolver.resolveComponentFactory(DynamicComponent);
    this.compRef = compFactory.create(this.injector, null, '#forViewRef');

    this.appRef.attachView(this.compRef.hostView);
  }

  ngOnDestroy() {
    if(this.compRef) {
      this.compRef.destroy();
    }
  }
}

I use appRef.attachView in order to include dynamic component to change detection cycle

Plunker Example

See also

yurzui
  • 205,937
  • 32
  • 433
  • 399