3

I'm using Angular Material Stepper in a component. The problem is that when the component loads for the first time it breaks with the error message

ERROR TypeError: Cannot read property 'selectedIndex' of undefined

My Template

<mat-horizontal-stepper #stepper>
        <!-- Steps -->
</mat-horizontal-stepper>    
<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>
</div

My TS

import { MatStepper } from '@angular/material/stepper';

goBack(stepper: MatStepper){
    stepper.previous();
}

goForward(stepper: MatStepper){
    stepper.next();
}

And I also have stepper defined in TS as

@ViewChild('stepper') stepper: MatStepper;

But if I use the [disabled] property as

<div>
    <button (click)="goBack(stepper)" type="button" 
        [disabled]="stepper ? stepper.selectedIndex === 0 : false">Back</button>
    <button (click)="goForward(stepper)" type="button" 
        [disabled]="stepper ? stepper.selectedIndex === stepper._steps.length-1 : false">Next</button>
</div

than the stepper works as expected.

Note:

Angular Version is :5.2.8
Angular Material Version:5.2.5 
tayyab_fareed
  • 559
  • 9
  • 15

2 Answers2

4

Use the Elvis operator :

<button (click)="goBack(stepper)" type="button" 
    [disabled]="stepper?.selectedIndex === 0">Back</button>

It's the equivalent of

<button (click)="goBack(stepper)" type="button" 
    [disabled]="stepper ? stepper.selectedIndex === 0 : false">Back</button>

But cleaner. For the click event, you can do this

<button (click)="stepper && goBack(stepper)" type="button" 
    [disabled]="stepper?.selectedIndex === 0">Back</button>

To prevent the call if the stepper is undefined.

  • Thanks for the reply but my problem is that I need stepper to be defined on first load – tayyab_fareed Jun 17 '19 at 07:30
  • @tayyab_fareed your stepper won't be defined until displayed, You don't have a choice there. You will have to defer your processing to **until the stepper is loaded**. In the template, that's done with the elvis operator. If it still doesn't work, please provide a [mcve] or the TS code that doesn't work. –  Jun 17 '19 at 07:42
3

ViewChild gets it's value after view init hook, but angular try to read it after init hook

https://angular.io/guide/lifecycle-hooks

if you'd like to update your project to angular 8, you'll get tools to define when you need to inflate ViewChild variable

https://v8.angular.io/guide/static-query-migration

Damian Pioś
  • 473
  • 2
  • 5