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.)
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.
You cannot set a template for a component factory after it was created. Angular compiler parses templates when generating a factory and creates a view class for each component. After the component factory and its view class are created you cannot modify it. In your example you're using ComponentFactoryResolver
const factory = this.resolver.resolveComponentFactory(componentDef.type);
which returns already created factory.
The only option is to change the template before the compiler generates factories. But I don't think it's possible. You will probably have to take a look at the dynamic generation of components.
Read Here is what you need to know about dynamic components in Angular for more information.