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.
First off, I apologize for writing this as an answer, but it was too long to right as a comment.
It is possible to do what you're asking. In fact, I actually brought up this exact question at ng-conf this year. I spoke to Max Koretskyi (aka the "ng-wizard" author of angularindepth.com) after one of his sessions on this very subject.
I must warn you though the solution he provided was very convoluted, hacky, and you couldn't rely on it not breaking in future Angular releases, because what you're trying to accomplish goes against the Angular framework and is exactly what the Angular team is trying to prevent people from doing. Really it would just be a nightmare to try to maintain and any new Devs would probably pop their top trying to make sense of what I did. Heck I probably wouldn't even know what I did if I looked back on it a year+ later.
Ultimately, I settled on giving up on AOT and deployed my app using JIT and I have not regretted my decision since. If you decide you really want to pursue this further I would suggest reaching out to Max. From what I gathered at ng-conf he is a pretty friendly guy and he openly invites people to reach out to him if they have questions. Hope that helps, best of luck! :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With