How can I change an Component based on data that I get from database?
I'm using something like this: stackblitz
But tools are show depending on user roles. (Database returns which tools, order
and so on)
There are two ways I can think of how to do this:
First
Using *ngIf like:
<div class="tools-wrapper controls">
<div class="tools-container">
<app-tool-menu *ngIf="existsInData('menu')"></app-tool-menu>
<mat-divider *ngIf="existsInData('menu')"></mat-divider>
<app-tool-refresh *ngIf="existsInData('refresh')"></app-tool-refresh>
<app-tool-info *ngIf="existsInData('info')"></app-tool-info>
<app-tool-edit *ngIf="existsInData('edit')"></app-tool-edit>
</div>
</div>
this is not dynamically enough.
Second
Using the factoryResolver
@Injectable()
export class InjectorService {
constructor(private factoryResolver: ComponentFactoryResolver) { }
setRootViewContainerRef(viewContainerRef) {
this.rootViewContainer = viewContainerRef;
}
addDynamicComponents(data: any[]) {
const component = factory.create(this.rootViewContainer.parentInjector);
data.forEach((tool) => {
const toolComponent = this.toolMapping(tool);
const factory = this.factoryResolver.resolveComponentFactory(toolComponent);
this.rootViewContainer.insert(component.hostView);
});
}
toolMapping(tool) {
const tools = {
'menu': MenuComponent,
'refresh': RefreshComponent,
'info': InfoComponent,
'edit': EditComponent,
};
return tools[tool];
}
}
@Component({
selector: 'my-app',
template: `
<h1>Hello {{name}}</h1>
<ng-template #dynamic></ng-template>
`
})
export class AppComponent implements OnInit {
name = 'from Angular';
@ViewChild('dynamic', { read: ViewContainerRef })
viewContainerRef: ViewContainerRef;
constructor(private service: InjectorService ) {}
ngOnInit() {
this.service.setRootViewContainerRef(this.viewContainerRef);
this.service.addDynamicComponents(.. data ..);
}
}
Well there is a lot of overhead in this code, like defining a toolMapping or creating a Service for each similar construct I have.
Is there a better and cleaner solution?
Data looks like this (Can be modified if necessary):
[
{type: 'menu', tooltip: '', ...},
{type: 'divider', tooltip: '',...},
{type: 'refresh', tooltip: '', ...},
{type: 'info', tooltip: '',...},
{type: 'edit', tooltip: '',...},
]
How about using the <ng-container> with ngComponentOutlet? It's much less verbose than the ComponentFactoryResolver solution.
import { Component } from '@angular/core';
import { AComponent } from './a.component';
import { BComponent } from './b.component';
import { CComponent } from './c.component';
@Component({
selector: 'my-app',
template: `
<input type="radio" value="A" name="comp" [(ngModel)]="radioValue"> A
<input type="radio" value="B" name="comp" [(ngModel)]="radioValue"> B
<input type="radio" value="C" name="comp" [(ngModel)]="radioValue"> C
<ng-container [ngComponentOutlet]="selected"></ng-container>
`,
styleUrls: ['./app.component.css']
})
export class AppComponent {
radioValue: 'A' | 'B' | 'C';
get selected() {
switch (this.radioValue) {
case 'A': {
return AComponent
}
case 'B': {
return BComponent
}
case 'C': {
return CComponent
}
}
}
}
Live demo
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