angular / components

Component infrastructure and Material Design components for Angular
https://material.angular.io
MIT License
24.37k stars 6.75k forks source link

bug(stepper): Linear mode does not allow skipping valid steps #19392

Open axos88 opened 4 years ago

axos88 commented 4 years ago

As far as I understand the linear mode of the horizontal / vertical stepper should disallow advancing to steps that have not yet been completed. However when presenting an already prefilled form (such as on an edit page) to the user where most of the info is already filled out, I would expect the stepper to allow the user to skip steps which are valid based on the form validations.

Reproduction

https://stackblitz.com/edit/angular-se74ga

Steps to reproduce:

  1. Open page
  2. (notice that the components in step 1 and step 2 have been autofilled, and reload the page to reset state)
  3. Click on the 3rd step, without going to step

Expected Behavior

The stepper should allow switching to step 3. The stepControls in step 1 and 2 are valid

Actual Behavior

It's not.

Environment

shametim commented 4 years ago

I don't think it's because of invalid form state but rather an unvisited step.

Not sure this will create another issue for you but I suppose you could set the override completed flag of the individual mat-steps in the event you have auto-fill data available. <mat-step completed = "true" [stepControl]="firstFormGroup" [editable]="isEditable">

What I think is happening is that your form control is neither invalid nor pending, but rather that you haven't interacted with step 2. Linear mode seems to prevent navigation to a given step without first having interacted with all the intermediate steps. https://github.com/angular/components/blob/b6358b2e32d6cbd3646411fc576e46dc55bcf512/src/cdk/stepper/stepper.ts#L503

axos88 commented 4 years ago

Thanks for linking in the releavant code. Setting the completed step brings in other issues indeed (such as changing the icon to the crayon instead of the step numbers), but I would argue that the requirement of the !step.interacted is what is causing the issue. And as far as I understand it's not possible to override this value, becuase it's not an Input().

Not sure what the rationale is behind it being there, I feel like it should be removed.

axos88 commented 4 years ago

I am currently working around this like this:

  @ViewChildren(MatStep) steps: QueryList<MatStep>;

  ngOnInit(): void {
    setTimeout(() => { this.steps.forEach(s => s.interacted = true) });
  }
  <mat-vertical-stepper ...>
          <ng-template matStepperIcon="edit" let-index="index">
            {{ index + 1 }}
          </ng-template>
  ...
  </mat-vertical-stepper>
shametim commented 4 years ago

If I had to guess it's a design choice, maybe this is an opportunity to document the linear input better in the Material docs to also say that interaction with the elements is required regardless.

Could simplify that to:

ngAfterViewInit(): void {
    this.steps.forEach(s => s.interacted = true)
}
mmalerba commented 4 years ago

I think this is probably working as intended, but it could use better documentation at least