Problem Statement: I'm working on one of the angular applications. In this application, I've 4 components named as below,
- common-component
- box-one-component
- box-two-component
- box-three-component
Now, the "common-component" is responsible for rendering the rest of the components inside it by iterating over an array from it's ".ts" file. So we have below array of object which gives us the selector name for those 3 components.
[
{'componentName': "box-one-component", 'selector': "app-box-one"},
{'componentName': "box-two-component", 'selector': "app-box-two"},
{'componentName': "box-three-component", 'selector': "app-box-three"}
]
Now, I used ngFor to iterate over an array as below,
<div *ngFor="let com of components;">
<com.selector> </com.selector> // not giving selector name
</div>
Now, the problem is, <com.selector> </com.selector>
statement is unable to get the selector name from an object and that is obvious because in angular we use string interpolation as {{com.selector}}
to extract the data.
But, the problem is, that we can not use interpolation in the HTML Tag angel bracket. So, if I write something like <{{com.selector}}>
</{{com.selector}}>
then I get the error on the browser console window.
So, how can we extract the selector name in the HTML Tag angel bracket from each object that we are iterating over?
Expected Output after every iteration of for loop:
<div>
<app-box-one> </app-box-one>
// <app-box-two> </app-box-two>
// <app-box-three> </app-box-three>
</div>
CodePudding user response:
It's not possible to do that the way you want. I would suggest you to change the architecture to something that uses just one component to represent all the boxes (parameterized by a state passed to the component using the @Input()
decorator). Check the docs .
But if you still want to keep this architecture, a way to do this is to add the HTML elements directly to the DOM from the typescript of the parent component (inside ngAfterViewInit lifecycle hook). But I don't recommend this. Usually it's always possible to modify the architecture to make it with only one parameterized component. The only case when I modify the DOM from typescript is when I work with canvas.
UPDATE: After some research, I think the easiest way to add the components to the DOM at runtime is by using Angular's ViewContainerRef.
The typescript of the component that will load other components at runtime will be something like this:
import {Component, AfterViewInit, ViewContainerRef} from '@angular/core';
import {Child1Component} from './child1/child1.component';
import {Child2Component} from './child2/child2.component';
@Component({
selector: 'app-root',
templateUrl: 'app.component.html',
styleUrls: ['app.component.scss'],
})
export class AppComponent implements AfterViewInit {
components: any[] = [Child1Component, Child2Component];
constructor(public viewContainerRef: ViewContainerRef) {}
ngAfterViewInit() {
this.loadComponent();
}
loadComponent(){
this.viewContainerRef.clear();
for(const component of this.components){
this.viewContainerRef.createComponent<typeof component>(component);
}
}
}
You import the components you want to load dynamically, you create an array with them and you load them with ViewContainerRef
.
For a full example, check the explanation here.
CodePudding user response:
You can execute for loop into one function and emit component name by using service subject variable, then subscribe it to all components, if the emitted component name will be matched that time render that component
component 1( where you want to render component one by one)
components = [
{ 'componentName': "box-one-component", 'selector': "app-box-one" },
{ 'componentName': "box-two-component", 'selector': "app-box-two" },
{ 'componentName': "box-three-component", 'selector': "app-box-three" }
]
ngOnInit(): void {
for (let comp of this.components) {
this.service.currentComponent.next(comp.selector)
}
}
in service
public currentComponent = new Subject();
in component app-box-one
public subscription: Subscription;
render: boolean = false // if render value is truthy that time you need to show html page
ngOnInit(): void {
this.subscription = this.service.currentComponent.subscribe(name => {
if (name == "app-box-one") {
this.render = true;
}
})
}
ngOnDestroy(): void {
if(this.subscription){
this.subscription.unsubscribe()
}
}
do the same thing into another two