Creating an angular2 component with ng-content dynamically
Asked Answered
S

1

18

I would like to set the body of <ng-content> while instantiating a component dynamically using ComponentFactoryResolver.

I see that I can get access to input & output using ComponentRef, but not a way to set <ng-content>.

Please note <ng-content> I'm planning on setting can contain simple text/can span dynamically created components

@Component({
    selector: 'app-component-to-project',
    template: `<ng-content></ng-content>`
})
export class ComponentToProject implements AfterContentInit {

    ngAfterContentInit() {
        // We will do something important with content here
    }

}


@Directive({
    selector: 'appProjectionMarker'
})
export class ProjectionMarkerDirective implements OnInit {

    constructor(private viewContainerRef: ViewContainerRef, private componentFactoryResolver: ComponentFactoryResolver) {
    }

    ngOnInit() {
        const componentFactory: ComponentFactory<ComponentToProject> = this.componentFactoryResolver.resolveComponentFactory(ComponentToProject);
        const componentRef: ComponentRef<ComponentToProject> = this.viewContainerRef.createComponent(componentFactory);
        // Question: How to set content before the child's afterContentInit is invoked
    }

}

@Component({
    selector: 'appTestComponent',
    template: `<div appProjectionMarker></div>`
})
export class TestComponent {}
Slacken answered 31/5, 2017 at 11:50 Comment(7)
Use projectableNodes parameter #41372834Thy
Can I add a dynamic component too as projectableNodes, so the child is available to directive's parent as @ContentChild?Slacken
Since it is a projectableNodes, I assume I can only pass DOM elementsSlacken
No, you can inject component into it or template. But you should manipulate only nodesThy
I'll test that. ThanksSlacken
See from 20.00 youtube.com/watch?v=EMjTp12VbQ8Thy
Can u plz post this video as an answer, I'll accept it. I think it will help a lot of othersSlacken
T
36

There is the projectableNodes parameter for the vcRef.createComponent method

createComponent<C>(componentFactory: ComponentFactory<C>, index?: number, injector?: Injector, projectableNodes?: any[][], ngModule?: NgModuleRef<any>): ComponentRef<C>;

You can use it to dynamically inject one component into another.

Let's say we have the following component

@Component({
    selector: 'card',
    template: `
        <div class="card__top">
            <h2>Creating a angular2 component with ng-content dynamically</h2>
        </div>
        <div class="card__body">
            <ng-content></ng-content>
        </div>
        <div class="card__bottom">
            <ng-content></ng-content>
        </div>
    `
})
export class CardComponent {}

We want to create it dynamically and insert some controls to its ng-content locations. It could be done like follows:

const bodyFactory = this.cfr.resolveComponentFactory(CardBodyComponent);
const footerFactory = this.cfr.resolveComponentFactory(CardFooterComponent);

let bodyRef = this.vcRef.createComponent(bodyFactory);
let footerRef = this.vcRef.createComponent(footerFactory);

const cardFactory = this.cfr.resolveComponentFactory(CardComponent);

const cardRef = this.vcRef.createComponent(
    cardFactory,
    0,
    undefined,
    [
        [bodyRef.location.nativeElement],
        [footerRef.location.nativeElement]
    ]
);

Plunker Example

See also

Thy answered 31/5, 2017 at 16:22 Comment(2)
In this case you know beforehand that there are two ng-content, how could you know how many are there if the object is dynamically pass? I mean, if CardComponent can be any component?Dolor
I just found how to know the number of projections with ComponentFactory.ngContentSelectors so in this case cardFactory.ngContentSelectors.lengthDolor

© 2022 - 2024 — McMap. All rights reserved.