Dynamically changing the template for an Angular 4 components

Viewed 27871

Using Angular 4.1, I'm trying to dynamically change a module type's template before the module rendered. Is this possible?

We are bootstrapping an unknown number of components on the page (from a known list of component types), and the page may contain multiple components of the same type. I've found out a way to give each of these components a different selector so they can be rendered separately (even if they're of the same type), but I also need to give each one a different template. The template should be the inner HTML of the selected element.

Here's the code:

import { Component, NgModule, Inject, ApplicationRef, ComponentFactoryResolver, OpaqueToken, Type } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { MyNavComponent } from './MyNav.component';
import { MyRotatorComponent } from './MyRotator.component';
import { MySignUpComponent } from './MySignUp.component';

export const BOOTSTRAP_COMPONENTS_TOKEN = new OpaqueToken('bootstrap_components');

@NgModule({
    imports: [BrowserModule],
    declarations: [
        MyNavComponent,
        MyRotatorComponent,
        MySignUpComponent
    ],
    entryComponents: [
        MyNavComponent,
        MyRotatorComponent,
        MySignUpComponent
    ],
    providers: [
        {
            provide: BOOTSTRAP_COMPONENTS_TOKEN,
            useValue: [
                { type: MyNavComponent },
                { type: MyRotatorComponent },
                { type: MySignUpComponent }
            ]
        },
    ]
})
export class AppModule {
    constructor(
        private resolver: ComponentFactoryResolver,
        @Inject(BOOTSTRAP_COMPONENTS_TOKEN) private components: [Component],
    ) { }
    ngDoBootstrap(appRef: ApplicationRef) {
        console.log(this.components);
        this.components.forEach((componentDef: { type: Type<any>, selector: string }) => {
            const factory = this.resolver.resolveComponentFactory(componentDef.type);
            let selector = factory.selector;
            let nodes = document.querySelectorAll(selector);
            for (let i = 0; i < nodes.length; i++) {
                let node = nodes[i];
                (<any>factory).factory.selector = node;

                //The next line doesn't work... how can I dynamically set the template?
                (<any>factory).factory.template = node.innerHTML;

                appRef.bootstrap(factory);
            }
        });
    }
}

As noted near the end of the above code, (<any>factory).factory.template = node.innerHTML; doesn't work. I've also tried modifying the metadata for the type, but that doesn't work, either.

Is what I'm trying to achieve possible by another means? If not, is this worth submitting as a feature request?

(Note: the above code is based in part on the code of others at https://github.com/angular/angular/issues/7136.)

Update:

I'm wondering if in a future update of Angular I'll be able to achieve the same result by setting the template to <ng-content></ng-content> to include the innerHTML of the selected element. This isn't possible now with bootstrapped components, but based on this issue on Git, I'm hopeful it will be soon.

1 Answers
Related