Forget $compile in Angular 2
TL;DR
- Forget about `$compile`
- Use `innerHTML` and DOM APIs
`$compile` in Angular 2
I wrote an article that explain “how to load a HTML template with using dynamic component creation.”
Since Angular 2, `$compile` was dropped. There are no ways to insert HTML fragments into the component view except using innerHTML. But if we could create a component and load it…? Yes, we can do it! This post will explain about dynamic HTML projection in Angular 2 with dynamic component creation.
How did I ?
Before `@NgModule` introduced, I used `ViewContainerRef` and `RuntimeCompiler` with a dynamic component factory.
Dynamic component factory is a just function that calls `Component()` decorator function to create decorated class object dynamically.
private createDynamicComponent(selector, template) {
const metadata = new ComponentMetadata({
selector,
template,
});
const cmpClass = class _ { };
return Component(metadata)(cmpClass);
}
And then, I passed the class to `RuntimeCompiler` and get `ComponentFactory`.
this.compiler.compileComponentAsync(this.createDynamicComponent(selector, template))
.then(factory => {
this.vcRef.clear();
this.vcRef.createComponent(factory, 0, injector);
});
After `NgModule`
In Angular 2.0.0-rc.6, many APIs including `Compiler#compileComponent` were removed as deprecated API. Now, every component is belonging to its NgModules as declarations, and every compilation starts on the module.
Important thing: NgModules are compilation context.
That means I no longer be able to compile components separately because any compilations needs a module.
Of course, then I had an idea; dynamic component compilation with dynamic module creation.
If you are interested in the idea and my challenge, see this code.
https://github.com/laco0416/angular2-component-outlet/blob/master/src/component-outlet.ts#L70-L99
I tried that and it seems succeeded, but it has a big problem: AoT.
RuntimeCompiler is dead
Ahead of Time compilation is a powerful feature of Angular 2 to gain drastic performance improvement. It allows us to compile templates at the build time and reduce dependency on `@angular/compiler` package. It significantly affects to app bundle size if we use tree-shaking. why? Because then our app doesn’t have `RuntimeCompiler`. As all compilation finished in offline, a compiler is not needed in runtime.
AoT compilation brings many benefits to us:
- Small payload bundle
- Fast bootstrapping
- Template error detection
So, I chose AoT and threw away `$compile`.
Use `innerHTML` and DOM APIs
Instead of `$compile`, I decided to use `innerHTML` and DOM APIs to implement something like `directive` or Custom Elements.
Most of our directives may be reproduced with native DOM. For example, we can create pseudo-routerLink in a dynamic HTML.
It’s very grunt code, but works well. We should go on the right way of Angular and Web standards.
Summary
Angular 2 is stable now. We should throw away tricky ways. Keep our applications AoT-friendly and optimizable. Follow the right Angular way.