简体   繁体   中英

Angular Material Stepper custom icons ng-template has undefined context variable

I am trying to use an ng-template with the matStepperIcon property to override the default Angular Material's matStepper's icons. I also want to pass some data, and I tried using ng-container and *ngTemplateOutlet , however it only partially works.

As you can see from the following code, I would expect the print function to always print a defined value, however, after correctly printing the seven IDs of the steps it prints undefined seven times. Why is that and how do I prevent that from happening?

<mat-horizontal-stepper labelPosition="bottom">
  <ng-container *ngFor="let step of form.steps">
    <ng-template #ref let-step="id" matStepperIcon="edit">
      <mat-icon>clear</mat-icon>
      {{ print(step) }}
    </ng-template>
    
    <ng-template #ref let-step="id" matStepperIcon="number">
      <mat-icon>clear</mat-icon>
      {{ print(step) }}
    </ng-template>

    <ng-template #ref let-step="id" matStepperIcon="done">
      <mat-icon>clear</mat-icon>
      {{ print(step) }}
    </ng-template>
    
    <ng-container *ngTemplateOutlet="ref; context: step"></ng-container>

    <mat-step>
      <ng-template matStepLabel>
        {{ step.id }}:
        {{ translateService.currentLang === "it" ? step.name : step.nameEn }}
      </ng-template>

      <!-- Step content-->
      </mat-step>
  </ng-container>
</mat-horizontal-stepper>

Link to StackBlitz example

Changing my code to the following, on the other hand, causes the print function to print all the correct IDs but it also prints the ID of the last step 8 times.

<mat-horizontal-stepper labelPosition="bottom">
  <ng-container *ngFor="let step of form.steps">
    <ng-template #ref matStepperIcon="edit">
      {{ print(step.id) }}
      <mat-icon>clear</mat-icon>
    </ng-template>

    <ng-template #ref matStepperIcon="number">
      {{ print(step.id) }}
      <mat-icon>clear</mat-icon>
    </ng-template>

    <ng-template #ref matStepperIcon="done">
      {{ print(step.id) }}
      <mat-icon>clear</mat-icon>
    </ng-template>

    <ng-container *ngTemplateOutlet="ref; context: step"></ng-container>

    <mat-step>
      <ng-template matStepLabel>
        {{ step.id }}:
        {{ translateService.currentLang === "it" ? step.name : step.nameEn }}
      </ng-template>

      <!-- Step content-->
    </mat-step>
  </ng-container>
</mat-horizontal-stepper>

If you want to use custom icons with MatStepper you should do several things:

  • Teach library to take into account your set of icons.

We could disable displayDefaultIndicatorType like

providers: [{
   provide: STEPPER_GLOBAL_OPTIONS, useValue: {displayDefaultIndicatorType: false}
}]

But it won't help since Angular Material has predefined logic to show specific icon depending on internal step state https://github.com/angular/components/blob/28c36f8a02f72e51a4d6c6a797e2f913e5dede9b/src/cdk/stepper/stepper.ts#L442-L465

So, you can override base logic like here https://github.com/angular/components/issues/18307

@ViewChild(MatHorizontalStepper, { static: true }) stepper: MatHorizontalStepper;

ngOnInit() {
  this.stepper._getIndicatorType = (i, state) => state || 'number';
}
  • Decide which icon to show depending on step

    <mat-step [state]="step.state"> steps: [ { id: 218, name: 'Dati richiedente', nameEn: 'Applicant data', state: 'home', }, { id: 219, name: 'Richiesta Meeting', nameEn: 'Meeting request', state: 'edit' },
  • Use built-in matStepperIcon context to access rendering index . With this index you can access corresponding step :

     <ng-template let-i="index" matStepperIcon="home"> <mat-icon>home</mat-icon> {{ print(form.steps[i]) }} </ng-template>

Forked Stackblitz

I am not sure why it's happening but you can workaround by using bind method

<ng-template  #ref let-step="id" matStepperIcon="number">
      <mat-icon>clear</mat-icon>
      {{ print.bind(step) }}
</ng-template> 

Working Example

Your markup is wrong, I didn't get how it should like, but found 3 mistake:

  • You can't have 3 same template refs.
  • Also you should define your templates outside * ngFor loop.
  • And last - to provide some data to template, you should define it as an object with $implicit key.

Here is stackblitz with changes

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM