Kiran Yallabandi Kiran Yallabandi - 1 month ago 25
TypeScript Question

What are projectable nodes in angular2

On reading the documentation of ViewContainerRef and ComponentFactory, where , for example we have the method

ViewContainerRef#createComponent which takes 3 arguments :

componentFactory
,
injector
and
projectableNodes
.

I have tried looking up what
projectableNodes
mean and how they are used in blogs, tutorials and source code but could not find much.

On looking up the diff for for ngComponentOutlet directive, All I could gather was that the string in projectableNodes is "projected" or rendered on to the created components view ( like ng-content may be). If so that is confusing as we have ViewContainerRef#createEmbeddedView for the same.

I would like to know as to what are these
projectedNodes
and have an example of their usage.

Answer

Projectable nodes are the node elements ( i.e elements implementing the node interface ), which are "projected" or in other words, transcluded onto the ng-content that we have in the template of our component.

For example we can create a node element the following way :

var myNode = document.createElement('div');
var text = document.createTextNode('this is my text');
myNode.appendChild(text);

Then we can use the above node, the following way :

constructor(private vcr:ViewContainerRef ) {

}
ngAfterViewInit() {

  let cmpFactory = this.vcr.resolveComponentFactory(MyDynamicComponent);
  let injector = this.vcr.parentInjector;

  var myNode = document.createElement('div');
  var text = document.createTextNode('this is my text');
  myNode.appendChild(text);
  this.vcr.createComponent(cmpFactory,0,injector,[ [ myNode ] ])
}

A two dimensional array is accepted for projected nodes as we can have multi-slot transclusion, that is more than one ng-content.

Here is a full example of how to use the projectable nodes with ViewContainerRef#createComponent. This example dynamically creates a component that has transclsion/ng-content in it:

import { Component, ViewContainerRef, ComponentFactoryResolver } from '@angular/core';
import { ComponentFactory,ComponentRef, Renderer } from '@angular/core';

@Component({
    selector: 'sample',
    template: '<ng-content></ng-content><ng-content></ng-content>'
})
export class Sample { }

@Component({
    selector: 'demo',
    template: '<p>Demo</p>',
    entryComponents: [Sample]
})
export class DemoComponent {

    constructor(private vcr: ViewContainerRef, private cfr: ComponentFactoryResolver, private r: Renderer) { }

    ngAfterViewInit() {
        let cmpFactory = this.cfr.resolveComponentFactory(Sample);
        let injector = this.vcr.parentInjector;
        let demoCompEl = this.vcr.element.nativeElement;

        let projectedElA = this.r.createElement(demoCompEl,'p');
        this.r.createText(projectedElA,'projectable content A');
        this.r.detachView([projectedElA]);

        let projectedElB = this.r.createElement(demoCompEl,'p');
        this.r.createText(projectedElB,'projectable content B');
        this.r.detachView([projectedElB]);

        let projectedC = document.createElement('div');
        let text = document.createTextNode('projectable content C');
        projectedC.appendChild(text);

        let cmpRef = this.vcr.createComponent(cmpFactory,0,injector,[[projectedElA],[projectedElB,projectedC]]);

    }


}

Output :

Demo

projectable content A

projectable content B

projectable content C

The additonal thing to notice is that in the 2d array that we pass for projectable nodes, each 1d array entry has the root elements of a particular view that belong together/ will be rendered together in a single ng-content block