I have a (what I think would be a fairly common) problem that I cannot find a good way to solve with the current Angular 4.x architecture. Maybe there is a method that I haven't found yet, but I have searched pretty extensively.
I would like to insert some dynamic, user generated HTML content into an Angular app. This HTML content my also include some known(included in the module declaration) Angular components that should be rendered. Below is some puesdo-app HTML might help me explain:
<app-component>
<!-- standard angular header and router-outlet -->
<app-header>
<app-nav>
<ul>
<li routerLink="/page-one">First Page</li>
<li routerLink="/page-two">Second Page</li>
<li routerLink="/page-three">Third Page</li>
</ul>
</app-nav>
</app-header>
<router-outlet></router-outlet>
<!--
the dynamic content would be inserted into the <main> element as HTML with angular <app-... tags
the content in the HTML content inserted main tag could contain anything
the angular components within the content should be initalized as normal
-->
<main>
<h1>Title</h1>
<app-component>
<p>this component and its content should be initalized when HTML is inserted into main</p>
</app-component>
<app-component>
<app-sub-component>
angular components here might also contain other angular components
</app-sub-component>
</app-component>
<p>more regular HTML content</p>
</main>
<!-- more traditional Angular components could come after -->
<app-footer></app-footer>
</app-component>
I have tried two methods of achieving this, neither of which truly work.
TLDR; It doesn't work with AOT. The AOT is all or nothing.
I've used a dynamic component pattern like this one. This is very simple to implement. Using the JIT Compiler, make a new Component and Module out of the HTML content, and insert an instance of the new component. This works as long as the project is not compiled AOT. Since Angular 4.x the JitCompilerFactory cannot be included in an AOT compiled build. It seems like it might make sense to allow the JIT Complier into AOT compiled builds so known components can get the performance boost, and use the JIT Compiler just for dynamic components. It would be the best of both worlds. BUT, I don't know the inner workings of Angular and I assume there is a good reason not to allow the JIT Compiler's inclusion.
TLDR; Can't get an entry point to load the components.
After my issues with the JIT Compiler in an AOT build, I thought I could maybe use the ComponentFactoryResolver
. Since I'm only using components already declared in the module, I could just create a component in the place of where it should be in the HTML content. This idea is a little crazy, but I thought it might work. My approach would look like this:
DomSanatizer.bypassSecurityTrustHtml()
, {<app-selector-string> : ComponentClass}
json tableViewContainerRef
in the spot I would like to insert the component (this part is not possible)
ViewContainerRef.createComponent()
, create a new instance of the proper component
projectableNodes
that is the content of the old html componentThis does not work because dynamically creating a ViewContainerRef
in step 3 is not possible.(It may also not be possible to do anything after step 3. IDK. I didn't get that far)
Is there any way to do this? Am I over thinking it?
Thanks!
This is very possible, actually...
Angular's ApplicationRef
has an attachView()
method that will keep track of a new component reference. That component reference can then be inserted anywhere just like a regular component.
Here's a pseudo-code example:
// create a componentRef
const componentFactory = ComponentFactoryResolver.resolveComponentFactory(AngularComponent);
const componentRef = componentFactory.create(Injector);
// attach the componentRef to the angular ApplicationRef
ApplicationRef.attachView(componentRef.hostView);
// insert the root element of the new componentRef into any DOM node
const componentRoot: HTMLElement = (componentRef.hostView as EmbeddedViewRef<any>).rootNodes[0];
const node = document.getElementById('insertionPoint');
Renderer2.insertBefore(node.parentNode, componentRoot, node);
// don't forget to destroy the component when you're done with it
ApplicationRef.detachView(componentRef.hostView);
componentRef.destroy();
Better explanation here
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