I have a problem with building templates "on the fly" from API response but only in AoT build.
I received from backend this kind of response:
<h1>Title...</h1>
<some-component></some-componen>
<p>other content</p>
And I want to parse this like regular Angular template.
The simplified code of my component looks like this:
import {
Compiler,
Component,
ComponentFactory,
ComponentRef,
Injector,
Input,
NgModule,
OnChanges,
OnDestroy,
OnInit,
ViewContainerRef
} from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';
export async function createComponentFactory(compiler: Compiler, metadata: Component): Promise> {
const cmpClass = class DynamicComponent {
};
const decoratedCmp = Component(metadata)(cmpClass);
// IMPORT ALL MODULES HERE!!!
@NgModule({imports: [CommonModule, RouterModule], declarations: [decoratedCmp]})
class DynamicHtmlModule {
}
const moduleWithComponentFactory = await compiler.compileModuleAndAllComponentsAsync(DynamicHtmlModule);
return moduleWithComponentFactory.componentFactories.find(x => x.componentType === decoratedCmp);
}
@Component({
selector: 'html-renderer',
templateUrl: './html-renderer.component.html',
styleUrls: ['./html-renderer.component.scss']
})
export class HtmlRendererComponent implements OnInit, OnChanges, OnDestroy {
@Input() content: string;
cmpRef: ComponentRef;
constructor(private vcRef: ViewContainerRef, private compiler: Compiler) { }
ngOnInit(): void {
console.log('init...')
console.log(this.compiler)
}
ngOnDestroy() {
if (this.cmpRef) {
this.cmpRef.destroy();
}
}
ngOnChanges() {
const html = this.content;
if (!html) { return; }
if (this.cmpRef) {
this.cmpRef.destroy();
}
const compMetadata = new Component({
selector: 'dynamic-selector',
template: this.content,
});
createComponentFactory(this.compiler, compMetadata)
.then(factory => {
const injector = Injector.create({providers: [], parent: this.vcRef.injector});
this.cmpRef = this.vcRef.createComponent(factory, 0, injector, []);
});
}
}
So, I passing whole data in content
input, then compiling all components via compileModuleAndAllComponentsAsync
method ( https://angular.io/api/core/Compiler#compilemoduleandallcomponentssync )
and all works in JIT build.
I want to get this work in AoT compilation because now I getting an error:
Runtime Compiler is not loaded
when building with AoT on the example code
I also tried to provide compiler in app.module.ts in providers[]
like this bu it doesn't work too:
export function createCompiler(compilerFactory: CompilerFactory) {
return compilerFactory.createCompiler();
}
{provide: COMPILER_OPTIONS, useValue: {}, multi: true},
{provide: CompilerFactory, useClass: JitCompilerFactory, deps: [COMPILER_OPTIONS]},
{provide: Compiler, useFactory: createCompiler, deps: [CompilerFactory]},
My question: is there any way to include the lazy loaded module with JIT compiler to access its methods?
I found some related questions but there is no answer:
Error while using @angular compiler in Angular 5 and AOT-Build
EDIT 15.01.2019 Here is a working JIT example on stackblitz.com with interpolation and databindings test: https://stackblitz.com/github/lyczos/angular-dynamic-html-renderer
EDIT 05.01.2020 Currently, I've started using builder.io Steve (author of this project) is using web components to make it work.