Dynamically loaded Angular 10 component cannot access CommonModule - javascript

I'm currently working on an Angular application. In one of my methods, I dynamically create a component, however I am unable to use ngClass, ngIf and other such directives from the CommonModule in the component.
Below is an example of the error:
Error when I use ngIf or ngClass inside the dynamically loaded logo component
WHAT I DID ALREADY:
I've imported commonModule in my project app.module.ts
I've imported commonModule in my display component and it works as I'm able to use ngIf and ngClass in every other component without any problem
Also I'm able to import any component without errors as long as I'm not using any directive from the CommonModule in my html
I've tried importing an instance of the NgModule through component factory createComponent function as shown in the angular documentation:
Quick view of angular documentation on component factory createcomponent
I have spent hours on this and believe it's related to my use of the createComponent method.
Please Help!
Here's my app.module.ts
import { NgModule } from '#angular/core';
import { BrowserModule, HammerGestureConfig, HammerModule,
HAMMER_GESTURE_CONFIG } from '#angular/platform-browser';
import { GoogleAnalyticsService } from
'./shared/services/googleanalytics'; // import our Google Analytics
service
import { BrowserAnimationsModule } from '#angular/platform-
browser/animations';
import { HttpClientModule } from '#angular/common/http';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { CommonModule } from '#angular/common';
#NgModule({
declarations: [AppComponent],
imports: [
CommonModule,
BrowserModule,
AppRoutingModule,
HttpClientModule,
BrowserAnimationsModule,
],
providers: [GoogleAnalyticsService],
bootstrap: [AppComponent],
})
export class AppModule {}
Here's my Display component (Bdoc-a.component.ts)
import {
Component, OnInit, Input, Output, Renderer2, AfterViewInit,
AfterContentChecked, ViewChild, ElementRef,
ComponentFactoryResolver, ViewContainerRef, ViewChildren, QueryList
} from '#angular/core';
import { StorageService } from
'../../../../shared/services/storage.service';
import { AuthService } from '../../../../auth/auth.service';
#Component({
selector: 'app-bdoc-a',
templateUrl: './bdoc-a.component.html',
styleUrls: ['./bdoc-a.component.scss'],
})
export class BdocAComponent implements OnInit, AfterViewInit,
AfterContentChecked {
#Input() docConfig = { aspectRatio: '4:3', width: 800, height: 500 };
#Input() bgStyles = { shadow: true, bgClr: '#ffcc00' };
#Input() showBtns = false;
fWidth = this.docConfig.width;
fMaxWidth = this.docConfig.width;
fHeight = this.docConfig.height;
fMaxHeight = this.docConfig.height;
#Input() url = '';
#Input() settings = { ... };
...
#ViewChildren('loadDynAssetElEditItms', { read: ViewContainerRef })
biEditEls: QueryList<ViewContainerRef>;
#ViewChildren('loadDynAssetElViewItms', { read: ViewContainerRef })
biViewEls: QueryList<ViewContainerRef>;
loadDynAssetEl: any;
#ViewChild('bieditorFloat', { read: ElementRef }) bieditorFloat:
ElementRef;
#ViewChild('bieditorFloatViewer', { read: ElementRef })
bieditorFloatViewer: ElementRef;
// variable to hold all document page elements
allDocPages: any;
allDocPageComp = [];
constructor(private storage: StorageService,
private resolver: ComponentFactoryResolver,
private authService: AuthService,
private elmRef: ElementRef) {
this.url = 'templates/logo/1/logo1a/logo1a.component';
}
ngAfterViewInit(): void {
this.initBrandAsset();
}
initBrandAsset(): void {
this.allDocPageComp = [];
setTimeout(() => {
for (let i = 0; i < this.pages; i++) {
this.loadDynAsset(this.url, i);
}
this.setDocPageVar();
}, 200);
}
async loadDynAsset(url, pgIndex) {
const impEl = await import( 'src/app/' + url);
const allKeys = Object.keys(impEl);
this.biEditEls.forEach((itm, i) => {
if (i === pgIndex) {
itm.clear();
const newComp = itm.createComponent(
this.resolver.resolveComponentFactory(impEl[allKeys[0]]));
newComp.instance['bol']['test'] = 'LOGO TEST TEXT HERE... ' + pgIndex;
this.allDocPageComp.push(newComp.instance);
}
});
}
}
Here's my Logo1a.component.html
<div class="baItem biLogo logo1a edit">
<div class="null bilNull">
<ng-container>
<div class="bilSymb">
<div class="null">
<div #forTxtLogo class="forTxtLogo" *ngIf="config.symb.mode ===
'txt'">
<div class="symbItm"><div class="nl">B</div></div><div
class="symbItm"><div class="nl">S</div></div>
</div>
<div #forImgSvgLogo class="forImgSvgLogo" *ngIf="config.symb.mode
=== 'svg' || config.symb.mode === 'img'">
<div class="symbItm"><div class="nl"></div></div>
</div>
</div>
</div><!-- end of bilSymb -->
</ng-container>
<ng-container *ngIf="config.body.show">
<div class="bilBody">
<div class="null">
<ng-container *ngIf="config.body.txt.show">
<div #forTxtArea class="forTxtArea">
<div class="null">Logo Body Text Area</div>
</div>
</ng-container>
<ng-container *ngIf="config.body.tag.show">
<div #forTagArea class="forTagArea"><div class="null">Logo Tag
Area...</div></div>
</ng-container>
</div>
</div><!-- end of bilBody -->
</ng-container>
</div><!-- end of biNull -->
</div><!-- end of biLogo-->
Here's my Logo1a.component.ts
import { NgModule, Component, OnInit, Input, Output, Renderer2,
AfterViewInit, AfterContentChecked, ViewChild, ElementRef,
ComponentFactoryResolver, ViewContainerRef, ViewChildren, QueryList
} from '#angular/core';
import { StorageService } from
'../../../../shared/services/storage.service';
import { AuthService } from '../../../../auth/auth.service';
#Component({
selector: "app-logo1a",
templateUrl: "./logo1a.component.html",
styleUrls: ["./logo1a.component.scss"],
})
export class Logo1aComponent implements OnInit, AfterViewInit {
#ViewChild('loadExtra', { read: ViewContainerRef }) loadExtra:
ViewContainerRef;
#Input() bol = { test: 'LOGO 1A LOADED!' }; // breadth of life
public config = {
symb: {
show: true,
mode: 'txt', // 'txt', 'symb', 'img'
},
body: {
show: true,
txt: { show: true },
tag: { show: true }
}
}
constructor(private storage: StorageService,
private resolver: ComponentFactoryResolver,
private vcRef: ViewContainerRef,
private authService: AuthService,
private elmRef: ElementRef) { }
ngOnInit(): void {}
ngAfterViewInit() {}
}
Here's my Logo1a.module.ts
import { NgModule } from '#angular/core';
import { CommonModule } from '#angular/common';
import { Logo1aComponent } from './logo1a.component';
#NgModule({
imports: [CommonModule],
declarations: [Logo1aComponent],
exports: [Logo1aComponent]
})
export class Logo1aModule {}
But this was the error that was thrown after I dynamically imported the module instead of the component in my Angular 10 app as #jburtondev suggested:
ERROR Error: Uncaught (in promise): Error: ASSERTION ERROR: Type passed in is not ComponentType, it does not have 'ɵcmp' property.
....

The component needs to be in its own module which declares the CommonModule. Otherwise, Angular cannot associate it with the CommonModule at runtime.
1. Create a component module
#NgModule({
declarations: [ YourDynamicComponent ],
imports: [ CommonModule ] // THIS IS WHAT WILL TELL ANGULAR TO LOAD IT INTO YOUR COMPONENT
exports: [ YourDynamicComponent ]
})
export class YourDynamicModule { }
2. Load the component
constructor(private compiler: Compiler, private viewContainerRef: ViewContainerRef) {}
createDynamicComponent(): void {
const componentModule = this.compiler.compileModuleAndAllComponentsSync(YourDynamicModule);
const factory = componentModule.componentFactories.find(c => comp.componentType === YourDynamicComponent);
this.viewContainerRef.createComponent(factory);
}
3. It should now be be decorated with the CommonModule

So I finally got a not-so-clean working approach to my question.
I created a new module, LogoModules, where I imported all the logo modules I will be displaying in my display component
import { NgModule } from '#angular/core';
import { Logo1aModule } from '../templates/logo/1/logo1a/logo1a.module';
import { Logo2aModule } from '../templates/logo/2/logo2a/logo2a.module';
import { Logo3aModule } from '../templates/logo/3/logo3a/logo3a.module';
#NgModule({
// imports: [],
// exports: [],
})
export class LogoModules {}
This was modified a bit from #jburtondev's suggestion to pre-import all the modules but I still think there should be a cleaner way to do this part. Dynamically loading the modules and components without pre-importing the modules still works but also fails sometimes. I think this is due to some settings with the compiler and webpack in tsconfig. I need more research to clarify this.
I imported the LogoModules module in my display component
import { LogoModules } from '../templates/logo/logos.module';
...
BUT ...
So I am able to store the relative paths of each logo template in my database and display them dynamically based on user clicks, I updated my dynamic component loader in my Display component (Bdoc-a.component.ts) as shown below:
....
async loadDynAsset(url, pgIndex) {
const mObj = await import('src/app/' + url2);
const mKeys = Object.keys(mObj);
const mName = mKeys[0];
this.compiler.compileModuleAndAllComponentsAsync(mObj[mName])
.then((factories) => {
const f = factories.componentFactories[0];
const newComp = this.testDynComp.createComponent(f);
newComp.instance['bol']['test'] = 'LOGO TEST TEXT HERE... ' + pgIndex;
});
....
}
....
And then I was able to get a consistent result without errors. Thanks again to #jburtondev for some suggestions. I'll update my answer as soon as I discover a better approach to my question.

Related

Can't bind to 'appIfRoles' since it isn't a known property of 'p'

I wanted to implement View Component Based On User Role In Angular 10 , to hide and show component. But I am block with the error above. Does anyone here has an idea about the issue? Help and idea would be much appreciated. Thanks.
#Role service code
import { Injectable } from "#angular/core";
import { HttpClient } from "#angular/common/http";
import { Observable } from "rxjs";
/**
* The Role Service service
*/
#Injectable()
export class RolesService {
private rolesAPi: string = "https://api.npoint.io/97c436983e2fbacffc7f";
constructor(private http: HttpClient) {}
/**
* gets the user role
*/
public roles(): Observable<{ roles: string[] }> {
return this.http.get<{ roles: string[] }>(this.rolesAPi);
}
}
component.html code
<p *appIfRoles='["Admin"]'>
ADMIN CONTENT IS VIEWED
</p>
if-roles.directive.ts directive code
import { Input, OnInit, Directive, ViewContainerRef, TemplateRef, OnDestroy } from "#angular/core";
import { Subject, Subscription } from "rxjs";
import { takeUntil } from "rxjs/operators";
import { RolesService } from "../../core/services/user-role.service"
#Directive({
selector: '[appIfRoles]'
})
export class IfRolesDirective implements OnInit, OnDestroy {
private subscription: Subscription[] = [];
// the role the user must have
#Input() public ifRoles: Array<string>;
/**
* #param {ViewContainerRef} viewContainerRef -- the location where we need to render the templateRef
* #param {TemplateRef<any>} templateRef -- the templateRef to be potentially rendered
* #param {RolesService} rolesService -- will give us access to the roles a user has
*/
constructor(
private viewContainerRef: ViewContainerRef,
private templateRef: TemplateRef<any>,
private rolesService: RolesService
) {}
public ngOnInit(): void {
this.subscription.push(
this.rolesService.roles().subscribe(res => {
if (!res.roles) {
// Remove element from DOM
this.viewContainerRef.clear();
}
// user Role are checked by a Roles mention in DOM
const idx = res.roles.findIndex((element) => this.ifRoles.indexOf(element) !== -1);
if (idx < 0) {
this.viewContainerRef.clear();
} else {
// appends the ref element to DOM
this.viewContainerRef.createEmbeddedView(this.templateRef);
}
})
);
}
/**
* on destroy cancels the API if its fetching.
*/
public ngOnDestroy(): void {
this.subscription.forEach((subscription: Subscription) => subscription.unsubscribe());
}
}
app.module.ts code
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { BrowserAnimationsModule } from '#angular/platform-browser/animations';
import { HttpClientModule, HTTP_INTERCEPTORS } from '#angular/common/http';
import { RouterModule } from '#angular/router';
import { LoginComponent } from './core/pages/login/login.component';
import { DashboardComponent } from './core/pages/dashboard/dashboard.component';
import { LayoutModule } from '#angular/cdk/layout';
import { SideNavComponent } from './core/components/side-nav/side-nav.component';
import { HeaderBarComponent } from './core/components/header-bar/header-bar.component';
import { UserprofileComponent } from './core/components/userprofile/userprofile.component';
import { UserInviteFormDialogComponent } from './core/components/user-invite-form-dialog/user-invite-form-dialog.component';
import { HeaderInterceptor } from './core/interceptors/header.interceptor';
import { RolesService } from './core/services/user-role.service';
import { SharedModule } from './shared/shared.module';
//import { AgmCoreModule } from '#agm/core';
import { ToastrModule } from 'ngx-toastr';
import { NgxMaskModule, IConfig } from 'ngx-mask';
import { IfRolesDirective } from './shared/directives/if-roles.directive';
// import { CoreModule } from './core/core.module';
const ngMaskConfig: Partial<IConfig> = {
validation: false,
};
#NgModule({
declarations: [
AppComponent,
IfRolesDirective,
LoginComponent,
DashboardComponent,
SideNavComponent,
HeaderBarComponent,
UserprofileComponent,
UserInviteFormDialogComponent,
,
],
imports: [
BrowserModule,
AppRoutingModule,
BrowserAnimationsModule,
LayoutModule,
RouterModule,
HttpClientModule,
// CoreModule,
ToastrModule.forRoot({
preventDuplicates: true,
enableHtml: true,
progressBar: true
}),
SharedModule,
NgxMaskModule.forRoot(ngMaskConfig),
],
providers: [{
provide: HTTP_INTERCEPTORS,
useClass: HeaderInterceptor,
multi: true,},
RolesService,],
bootstrap: [AppComponent]
})
export class AppModule { }
You're almost there! Just missing the export in app.module.ts
#NgModule({
declarations: [
...
IfRolesDirective,
...],
imports: [...],
exports: [IfRolesDirective]
providers: [...],
})
Try the following code.
<p *ifRoles='["Admin"]'>
ADMIN CONTENT IS VIEWED
</p>
#Directive({
selector: '[ifRoles]'
})
export class IfRolesDirective implements OnInit, OnDestroy {
private subscription: Subscription[] = [];
// the role the user must have
#Input() public ifRoles: Array<string>;
....
....
}

component can't find provider from lazy-loaded module

I have a lazy-loaded module which has one service and one component.
I would like to use the service in that component but I get:
Error: No provider for EventService!
The module
import { NgModule } from '#angular/core';
import { CommonModule } from '#angular/common';
import { EventRoutingModule } from './event-routing.module';
import { FormsModule } from '#angular/forms';
import { HttpModule } from '#angular/http';
import { EventListModule } from './../event-list/event-list.module';
import { ModuleWithProviders } from '#angular/core';
import { EventComponent } from './event.component';
import { EventService } from './event.service';
#NgModule({
imports: [
CommonModule,
FormsModule,
HttpModule,
EventRoutingModule,
EventListModule
],
declarations: [EventComponent]
})
export class EventModule {
static forRoot(): ModuleWithProviders {
return {
ngModule: EventModule,
providers: [EventService]
};
}
}
the component
import { Component, OnInit } from '#angular/core';
import { EventService } from './event.service';
#Component({
templateUrl: './event.component.html',
styleUrls: ['./event.component.scss']
})
export class EventComponent implements OnInit {
private eventService: EventService;
constructor(eventService: EventService) {
this.eventService = eventService;
}
ngOnInit() {
this.eventService.getEvents().subscribe(data => console.log(data), error => console.log(error));
}
}
the service
import { Injectable } from '#angular/core';
import { Observable } from 'rxjs/Observable';
import { AuthHttp } from 'angular2-jwt';
#Injectable()
export class EventService {
private static readonly URL = 'http://localhost:3000/api/events';
constructor(private authHttp: AuthHttp) { }
public getEvents() {
return this.authHttp.get(EventService.URL);
}
}
I have looked at a couple of posts here but havent been able to get a solution from them.
I know providers in lazy-loaded modules are module-scoped and lazy-loaded modules have their own dependency tree.
But it must be possible to inject the provider into the component, mustn't it?
You need to define how you provide your service.
You can define how it is provided at the module level:
#NgModule({
imports: [
CommonModule,
FormsModule,
HttpModule,
EventRoutingModule,
EventListModule
],
declarations: [EventComponent],
providers: [EventService]
})
export class EventModule { ... }
This means that one EventService instance will be available for the whole module.
Or at the component level:
#Component({
templateUrl: './event.component.html',
styleUrls: ['./event.component.scss'],
providers [EventService]
})
export class EventComponent implements OnInit { ... }
This means that one EventService instance will be available for each component instance. This is due to the hierarchical injectors feature. The component defines its own injector which can hold its own instances that are being made available to its children.
[EventService] is equivalent to [ { provide: EventService, useClass: EventService }]. Which means that the key used to inject the dependency is EventService and the instance is being constructed by using the EventService constructor.

Emit events between nested components grandchild to root component

I have wheels.component nested to car.component.
wheels.component:
export class WheelsComponent {
#Output() onLoaded : EventEmitter<string>() = new EventEmitter<string>();
private downloadAllFiles(url: string) {
this.onLoaded.emit('Hello, World 1!');
//some operations to wait
this.onLoaded.emit('Hello, World 2!');
};
}
Component car.component is not written at html page, but called through routing at car-routing.module.ts:
#NgModule({
imports: [
RouterModule.forChild([
{
path: 'sfactmessage/:id',
component: CarComponent,
resolve: {
card: cardResolver
}
}
])
],
exports: [RouterModule]
})
export class CarRoutingModule {}
What I want is to handle event emitted from wheels.component, not at car.component, but at app.component.
Is it possible to handle event at app.component?
The plunker sample is not working (sorry, this is my first plunkr example), but gives a view how my app is arranged.
Hello_ friend.
So basically if you want to use events globally in your application you can use a Service in combination with EventEmitter
In this case you create a service for example car.service.ts
import { Injectable, EventEmitter } from '#angular/core';
#Injectable()
export class CarService {
onLoaded : EventEmitter<string> = new EventEmitter<string>();
}
Then use this service in a child component to emit events like this wheels.component.ts
import { Component, EventEmitter } from '#angular/core';
import { CarService } from './car.service';
#Component({
selector: 'wheels',
template: '<a (click)="sendValues()"> Click me to send value </a>'
})
export class WheelsComponent {
constructor(private carService:CarService ){}
sendValues() {
/* Use service to emit events that can be used everywhere in the application */
this.carService.onLoaded.emit('Button in WheelsComponent was clicked ...');
};
}
and then capture this event from AppComponent for example app.component.ts
import { Component, OnInit, OnDestroy } from '#angular/core';
import { CarService } from './cars/car.service';
import { Subscription } from 'rxjs';
#Component({
selector: 'my-app',
templateUrl: `src/app.component.html`
})
export class AppComponent implements OnInit, OnDestroy{
private subscription: Subscription;
private loading = true;
name = 'Angular';
constructor(private carService: CarService){}
ngOnInit(){
this.subscription = this.carService.onLoaded.subscribe((message) => {
/*
Here you receive events from anywhere where
carService.onLoaded.emit() is used
**/
alert(`From AppComponent -> ${message}`);
});
}
ngOnDestroy(){
/* Don't forget to unsubscribe when component is destroyed */
this.subscription.unsubscribe();
}
}
I M P O R T A N T______________
If you want your service to work globally you need to declare it in the top level providers for example app.module.ts is a good place:
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { FormsModule } from '#angular/forms';
import { AppComponent } from './app.component';
import { CarComponent} from './cars/car.component';
import { WheelsComponent} from './cars/wheels.component';
import { HomeComponent} from './home.component';
import { routing } from './app.routing';
import { CarService } from './cars/car.service';
#NgModule({
imports: [ BrowserModule, FormsModule, routing ],
declarations: [ AppComponent, CarComponent, WheelsComponent, HomeComponent ],
providers: [ CarService ], // <-------- SEE HERE
bootstrap: [ AppComponent ]
})
export class AppModule { }
CLICK HERE TO SEE THE DEMO

Lazy loaded module create multiples instance of the parent service each time is loaded

Every time I navigate from MainComponent to TestListComponent the TestListComponent constructor is triggered and a new instance of the ObservableServiceis created. When I click the link the console show the duplicated messages. Maybe is an angular issue, any help?
main.module.ts
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import {MainRoutingModule} from "./main-routing.module";
import {MainComponent} from './main.component';
import {ObservableService} from "../../core/services/observable.service";
#NgModule({
imports: [
BrowserModule,
MainRoutingModule,
],
declarations: [MainComponent],
providers: [ObservableService],
bootstrap: [
MainComponent
]
})
export class MainModule { }
main.routing.module.ts
import { NgModule } from '#angular/core';
import { Routes, RouterModule } from '#angular/router';
export const routes: Routes = [
{ path: 'tests', loadChildren: 'angular/app/modules/test-list/test-list.module#TestListModule'},
{ path: '**', redirectTo: '' }
];
#NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class MainRoutingModule {}
observable.service.ts
import { Injectable } from '#angular/core';
import {Subject} from "rxjs/Rx";
import 'rxjs/add/operator/map'
#Injectable()
export class ObservableService {
// Observable string sources
private changeLanguageStatus = new Subject<Object>();
// Observable string streams
changeLanguageStatus$ = this.changeLanguageStatus.asObservable();
constructor(){}
/**
* Change language event
* #param params
*/
changeLanguageEvent(params: Object){
this.changeLanguageStatus.next(params);
}
}
test-list.module.ts
import { NgModule } from '#angular/core';
import {TestListComponent} from "./test-list.component";
#NgModule({
declarations: [
TestListComponent
]
})
export class TestListModule {}
test-list.component.ts
import {Component} from '#angular/core';
import 'rxjs/Rx';
import {ObservableService} from "../../core/services/observable.service";
#Component({
moduleId: module.id,
selector: 'st-test-list',
templateUrl: 'test-list.component.html'
})
export class TestListComponent {
constructor(private observableService:ObservableService) {
observableService.changeLanguageStatus$.subscribe(
data => {
console.log('Test', data);
});
}
}
main.component.ts
import {Component, ViewChild} from '#angular/core';
import 'rxjs/Rx';
import {ObservableService} from "../../core/services/observable.service";
#Component({
moduleId: module.id,
selector: 'st-main',
templateUrl: 'main.component.html'
})
export class MainComponent {
constructor(private observableService:ObservableService) {}
changeLanguage(lang){
this.observableService.changeLanguageEvent({type: lang});
}
}
main.component.html
<!--Dynamic content-->
<router-outlet></router-outlet>
It should be expected behavior that when you navigate to a component via routing it is created and when you navigate back it is destroyed. As far as I know you are experiencing this issue because you are creating what is called an Infinite Observable i.e. you are subscribing to it and waiting for a stream of events, in your case changing language. Because you never unsubscribe from your Observable, the function subscribed to it is kept alive for each new instance of your component. Therefore, rxjs won't handle disposing of your subscription and you will have to do it yourself.
First off I'd suggest you read about Lifecycle hooks. Check out the OnInit and OnDestroy lifecycle hooks.
Use ngOnInit to subscribe to your Observable and use ngOnDestroy to unsubscribe from it as such:
import { Component, OnInit, OnDestroy } from '#angular/core';
import { Subscription } from 'rxjs/Subscription';
#Component({ .... })
export class TestListComponent implements OnInit, OnDestroy
{
private _languageSubscription : Subscription;
ngOnInit(): void
{
this._languageSubscription = observableService.changeLanguageStatus$.subscribe(
data => {
console.log('Test', data);
});
}
ngOnDestroy() : void
{
this._languageSubscription.unsubscribe();
}
}
I hope this will solve your problem.

Creating Dynamic Components Angular Cli

I want to create Dynamic components and previously when i was working with Angular 2, I used this piece of code from another stack-overflow answer which worked fine until i switched to Angular-cli. How can i make it work in angular cli?
import {
Component,
Directive,
NgModule,
Input,
ViewContainerRef,
Compiler
} from '#angular/core';
import { CommonModule } from '#angular/common';
#Directive({
selector: 'html-outlet'
})
export class HtmlOutlet {
#Input() html: string;
constructor(private vcRef: ViewContainerRef, private compiler: Compiler) {}
ngOnChanges() {
const html = this.html;
if (!html) return;
#Component({
selector: 'dynamic-comp',
templateUrl: html
})
class DynamicHtmlComponent { };
#NgModule({
imports: [CommonModule],
declarations: [DynamicHtmlComponent]
})
class DynamicHtmlModule {}
this.compiler.compileModuleAndAllComponentsAsync(DynamicHtmlModule)
.then(factory => {
const compFactory = factory.componentFactories.find(x => x.componentType === DynamicHtmlComponent);
this.vcRef.clear();
const cmpRef = this.vcRef.createComponent(compFactory, 0);
});
}
}

Categories

Resources