I have seen a number of similar posts. I wish to be able to use @ngFor to create components to then load into steps of the Material Stepper
I have been following an example of dynamic loading, and some other posts (which are incomplete), and come up with the following.
First I have a number of components I want to load into the steps. They are just empty apart from the string (eg "component is working"
eg
export class StepPage1Component implements OnInit, StepPage {
constructor() { }
ngOnInit() {
}
}
So I have 3 of these.
The "root" component is as follows.
export class WizardStepperComponent implements OnInit {
@ViewChild('stepper') private myStepper: MatStepper;
totalStepsCount: number;
public steps: StepPage[];
constructor(private stepsService: StepPagesService) {
this.steps = [
new StepPage1Component,
new StepPage2Component,
new StepPage3Component
]
}
Markup:
<mat-horizontal-stepper #stepper>
<mat-step *ngFor='let step of steps'>
<app-step-page-wrapper item='step'></app-step-page-wrapper>
</mat-step>
</mat-horizontal-stepper>
<!-- second option -->
<div>
<button (click)="goBack(stepper)" type="button" [disabled]="stepper.selectedIndex === 0">Back</button>
<button (click)="goForward(stepper)" type="button"
[disabled]="stepper.selectedIndex === stepper._steps.length-1">Next</button>
<!-- using totalStepsCount -->
<!-- <button (click)="goForward(stepper)" type="button" [disabled]="stepper.selectedIndex === totalStepsCount-1">Next</button> -->
</div>
So in markup above, I have the ngFor that I am having problems with.
The wrapper is as follows...
import { Component, OnInit, Input, ViewChild, ComponentFactoryResolver } from '@angular/core';
import { directiveDef } from '@angular/core/src/view';
import { PageDirective } from '../page.directive';
@Component({
selector: 'app-step-page-wrapper',
template: '<ng-template pageDirective></ng-template>',
})
export class StepPageWrapperComponent implements OnInit {
@Input() item?: any;
@ViewChild(PageDirective) pageHost: PageDirective;
constructor(private componentFactoryResolver: ComponentFactoryResolver) { }
ngOnInit() {
if (this.item == undefined) {
console.error('Item undefined');
return
}
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.item);
const viewContainerRef = this.pageHost.viewContainerRef;
viewContainerRef.clear();
const componentRef = viewContainerRef.createComponent(componentFactory);
}
}
and finally the directive it is using is..
import { Directive, ViewContainerRef } from '@angular/core';
@Directive({
selector: '[pageDirective]'
})
export class PageDirective {
constructor(public viewContainerRef: ViewContainerRef) { }
}
So, I am trying to use the StepPageWrapperComponent to take each component as an input (item), and then use the componentFactoryResolver to create the conponent and then "attach" it to the directive in the template, ie pageDirective in <ng-template pageDirective></ng-template>
However, when I run this, I get the following error..
ERROR Error: No component factory found for step. Did you add it to @NgModule.entryComponents?
at noComponentFactoryError (core.js:9877)
at CodegenComponentFactoryResolver.push../node_modules/@angular/core/fesm5/core.js.CodegenComponentFactoryResolver.resolveComponentFactory (core.js:9915)
at StepPageWrapperComponent.push../src/app/step-page-wrapper/step-page-wrapper.component.ts.StepPageWrapperComponent.ngOnInit (step-page-wrapper.component.ts:21)
at checkAndUpdateDirectiveInline (core.js:22099)
at checkAndUpdateNodeInline (core.js:23363)
at checkAndUpdateNode (core.js:23325)
at debugCheckAndUpdateNode (core.js:23959)
at debugCheckDirectivesFn (core.js:23919)
at Object.eval [as updateDirectives] (WizardStepperComponent.html:3)
at Object.debugUpdateDirectives [as updateDirectives] (core.js:23911)
If I put a break point in StepPageWrapperComponent, this.item just seems to be a string!
So, this string "step" is meant to be the ngFor variable...
But it is just coming in as a string, rather than the component.
Does anyone have any ideas on what I am doing wrong here, or even if there is a better way of doing this?
For any more details the source for this sample is can be cloned from https://github.com/pjc2007/wizard-stepper1.git
Update 30 Aug 19
The reason the input was not working was I forgot the square brackets when passing the variable to the input.. ie I had
<app-step-page-wrapper item='step'></app-step-page-wrapper>
instead of
<app-step-page-wrapper [item]='step'></app-step-page-wrapper>
I now have the component as the input for StepPageWrapperComponent as expected.
So, my problem now is the following error that is thrown when executing the line const componentFactory=this.componentFactoryResolver.resolveComponentFactory(this.item)
ngComponent: StepPage1Component {}
message: "No component factory found for [object Object]. Did you add it to @NgModule.entryComponents?"
stack: "Error: No component factory found for [object Object]. Did you add it to @NgModule.entryComponents?↵ at noComponentFactoryError (http://localhost:4200/vendor.js:71338:17)↵ at CodegenComponentFactoryResolver.push../node_modules/@angular/core/fesm5/core.js.CodegenComponentFactoryResolver.resolveComponentFactory (http://localhost:4200/vendor.js:71376:19)↵ at StepPageWrapperComponent.push../src/app/step-page-wrapper/step-page-wrapper.component.ts.StepPageWrapperComponent.ngOnInit (http://localhost:4200/main.js:244:66)↵ at checkAndUpdateDirectiveInline (http://localhost:4200/vendor.js:83560:19)↵ at checkAndUpdateNodeInline (http://localhost:4200/vendor.js:84824:20)↵ at checkAndUpdateNode (http://localhost:4200/vendor.js:84786:16)↵ at debugCheckAndUpdateNode (http://localhost:4200/vendor.js:85420:38)↵ at debugCheckDirectivesFn (http://localhost:4200/vendor.js:85380:13)↵ at Object.eval [as updateDirectives] (ng:///AppModule/WizardStepperComponent.ngfactory.js:16:5)↵ at Object.debugUpdateDirectives [as updateDirectives] (http://localhost:4200/vendor.js:85372:21)"
__proto__: Object
I do have this as an entryComponent
ie
@NgModule({
declarations: [
AppComponent,
WizardStepperComponent,
StepPage1Component,
StepPage2Component,
StepPage3Component,
PageDirective,
StepPageWrapperComponent
],
imports: [
BrowserModule,
BrowserAnimationsModule,
MatStepperModule,
MatInputModule,
MatButtonModule,
MatAutocompleteModule
],
providers: [],
bootstrap: [AppComponent],
entryComponents: [
StepPage1Component,
StepPage2Component,
StepPage3Component,
StepPageWrapperComponent
]
Update 30 Aug 19
Stepping into this.componentFactoryResolver.resolveComponentFactory, I see the following...
So the component seems to be in the map, but the map.get is returning undefined?




@Output()to my "step-wrapper" which allows you to bubble up the stepper functions. (I happened to already have built a dynamic Step Heading/Sub Head and Next/Back button component with alternative button options that also wraps my custom component within the step. Allows for granular step validation/customization). Huge thanks! - NSTuttle