I have a parent component as below
<div class="container-fluid p-0">
<div class="row">
<app-tile-render #tileRender [itemMasterId]="itemMasterId" [partName]="partName" [conditionIds]="conditionIds"
[showViewIcon]="true"></app-tile-render>
</div>
</div>
Below is the renderer component where I am using ng-template
to render the dynamically generated components
@Component({
selector: 'app-tile-render',
template: `<ng-template tileHost>
</ng-template>`
})
export class TileRenderComponent implements OnInit {
@Input() tiles: TileItem[] = [];
@Input() itemMasterId: number;
@Input() partName: string;
@Input() conditionIds: any;
@Input() showViewIcon: boolean;
@ViewChild(TileDirective, { static: false }) tileHost!: TileDirective;
constructor(public tileService: TileService,
public componentFactoryResolver: ComponentFactoryResolver,
private renderer: Renderer2) { }
ngOnInit() {
}
loadComponent() {
this.tiles = this.tileService.getTiles();
const viewContainerRef = this.tileHost.viewContainerRef;
viewContainerRef.clear();
for (let i = 0; i < this.tiles.length; i++) {
const tileItem = this.tiles[i];
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(tileItem.component);
const componentRef = viewContainerRef.createComponent<TileComponent>(componentFactory);
componentRef.instance.itemMasterId = this.itemMasterId;
componentRef.instance.partName = this.partName;
componentRef.instance.conditionIds = this.conditionIds;
componentRef.instance.showViewIcon = this.showViewIcon;
}
}
}
tile.service.ts
@Injectable()
export class TileService {
@Input() itemMasterId: any;
getTiles() {
return [
new TileItem(
CommonPurchaseOrderListComponent
),
new TileItem(
CommonRepairOrderListComponent
), new TileItem(
CommonSalesOrderListComponent
)
];
}
}
But here the issue is that it renders all the components (tiles) at once as below:
Instead of that I want to render all the component inside each bootstrap column so that it looks in a proper sequence.
like
<div class="row">
<div class="col-md-4">
want to display each dynamic generated component here..............
</div>
</div>
In loadComponent
function you can initialize a new constant, which will be holding the columns:
const rowDiv = this.renderer.createElement('div');
this.renderer.addClass(rowDiv, 'row');
Then, inside a for loop, after You initialize constant componentRef
You should trigger a new function, which will hold a reference to the rowDiv and also a componentRef, to later on nest it inside wrapping <div class="col-md-4"></div>
const componentRef = viewContainerRef.createComponent<TileComponent>(componentFactory);
this.createColumnForComponent(rowDiv, componentRef);
Function createColumnForComponent:
private createColumnForComponent(
componentRef: ComponentRef<any>,
rowDiv: HTMLElement
) {
// setup a div with class col-md-4
const columnDiv: HTMLElement = this.renderer.createElement('div');
this.renderer.addClass(columnDiv, 'col-md-4');
const componentElement: HTMLElement = componentRef.location.nativeElement;
// append component to column
this.renderer.appendChild(columnDiv, componentElement);
// append column to row
this.renderer.appendChild(rowDiv, columnDiv);
}
After that function our rowDiv is filled with the columns. We need to make use of it, so we trigger it once, after the for loop execution:
for (let i = 0; i < this.tiles.length; i++) {
...
}
this.createRowForColumns(viewContainerRef, rowDiv);
Function createRowForColumns:
private createRowForColumns(
viewContainerRef: ViewContainerRef,
rowDiv: HTMLElement
) {
// append row to our selector
this.renderer.appendChild(
viewContainerRef.element.nativeElement.parentNode,
rowDiv
);
In following code I made a use of renderer but You could also follow more native approach, like:
columnDiv.appendChild(componentElement);
columnDiv.classList.add('col-md-4');