I want to create an angular 7 web application that dynamically loads different components, as demonstrated in this official documentation: https://angular.io/guide/dynamic-component-loader
But I am not sure if it's a good idea to use ComponentFactoryResolver
.
I never used it and I don't know if it is stable and I don't know about the performance either.
I would like some opinions about it and if anyone knows any alternatives.
I don't want to use native innerHTML
I am trying to create a custom and generic wizard with dynamic steps. This wizard has
- header component
- wizard steps
- a "container". Right now I am using ng-template to display the content of each step(a separate component, in some cases a complicated component)
- wizard buttons (next & previous) and in the last step action buttons like save etc
The steps are dynamic. Based on some business logic like user's inputs from previous steps.
My current implementation:
I will show only the part where I am using the ComponentFactoryResolver
to make it understandable and readable :)
export class WizComponent implements OnInit {
public wizContentItems: WizContentItem[] = undefined;
public currentContentItem: WizContentItem = undefined;
public currentContentItemNumber: number = -1;
public currentWizContentComponent: WizContentComponent = undefined;
private componentRef: any;
@Output() public onStepChanged = new EventEmitter<StepPosition>();
private _position: StepPosition = StepPosition.First;
constructor(private componentFactoryResolver: ComponentFactoryResolver, private viewContainerRef: ViewContainerRef) { }
public ngOnInit() {
}
public onSelectStep(contentItem: WizContentItem) {
console.log("step was clicked");
console.log(contentItem);
if (this.currentContentItem !== undefined &&
!this.validateStep(this.currentContentItem)) {
return;
}
if (this.currentWizContentComponent !== undefined ) {
this.currentContentItem.stepProgressStatus = this.currentWizContentComponent.stepProgressStatus;
}
contentItem.stepState = StepState.Active;
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(contentItem.component);
this.viewContainerRef.clear();
this.componentRef = this.viewContainerRef.createComponent(componentFactory);
(<WizContentComponent>this.componentRef.instance).data = contentItem.data;
(<WizContentComponent>this.componentRef.instance).stepState = contentItem.stepState;
this.currentWizContentComponent = (<WizContentComponent>this.componentRef.instance);
if (this.currentContentItem != null) {
this.currentContentItem.stepState = StepState.Empty;
}
this.currentContentItem = contentItem;
this.currentContentItem.stepState = StepState.Active;
// Get currentContentItemNumber based currentContentItem
this.currentContentItemNumber = this.wizContentItems.findIndex(wizContentItem => wizContentItem === this.currentContentItem);
this.stepChanged();
}
public onNextClick(event: Event) {
if ((this.currentContentItemNumber + 1) < this.wizContentItems.length) {
let nextContentItem = this.wizContentItems[this.currentContentItemNumber + 1];
if (nextContentItem.stepState === StepState.Disabled) {
nextContentItem = this.getNextActiveItem(this.currentContentItemNumber + 1);
}
if (nextContentItem != null) {
this.onSelectStep(nextContentItem);
}
}
}
public onPreviousClick(event: Event) {
if ((this.currentContentItemNumber - 1) >= 0) {
let previousContentItem = this.wizContentItems[this.currentContentItemNumber - 1];
if (previousContentItem.stepState === StepState.Disabled) {
previousContentItem = this.getPreviousActiveItem(this.currentContentItemNumber - 1);
}
if (previousContentItem !== null) {
this.onSelectStep(previousContentItem);
}
}
}
public getCurrentStepPosition(): StepPosition {
return this._position;
}
private validateStep(contentItem: WizContentItem): boolean {
return (<WizContentImplComponent>this.componentRef.instance).isValid();
}
private stepChanged(): void {
this._position = undefined;
if (this.currentContentItemNumber <= 0) {
this._position = StepPosition.First;
} else if (this.currentContentItemNumber >= this.wizContentItems.length) {
this._position = StepPosition.Last;
} else {
this._position = StepPosition.Middle;
}
if ((<WizContentComponent>this.componentRef.instance).isSummary) {
this._position = StepPosition.Summary;
}
this.onStepChanged.emit(this._position);
}
private getNextActiveItem(itemNumber: number): WizContentItem {
if (this.wizContentItems.length <= (itemNumber + 1)) {
return null;
}
let nextContentItem = null;
for (let i = (itemNumber); i < this.wizContentItems.length; i++) {
if ( this.wizContentItems[i].stepState !== StepState.Disabled ) {
nextContentItem = this.wizContentItems[i];
break;
}
}
return nextContentItem;
}
private getPreviousActiveItem(itemNumber: number): WizContentItem {
if ((itemNumber - 1) < 0 ) {
return null;
}
let previousContentItem = null;
for (let i = (itemNumber - 1); i >= 0; i--) {
if ( this.wizContentItems[i].stepState !== StepState.Disabled ) {
previousContentItem = this.wizContentItems[i];
break;
}
}
return previousContentItem;
}
}
Thank you!!