Angular material stepper before-selection-change & prevent step-change for some condition
Asked Answered
R

5

12

I am using linear mat-stepper.

It works fine with next. I do api call & if it's success then i call stepper-next event instead of using matStepperNext directive.

Now when user fills all data on step 1 & directly click on some other step in header of next(or any other on edit record mode) then it will redirect to next step since form is valid.

I want to do service call before that & prevent step-change until service call is successful then only it should redirect to next step otherwise it shouldn't.

I can't find any method to implement this functionality in doc.

Can anyone suggest me what to do. I don't want to disable header click.

Rhaetia answered 3/4, 2019 at 14:39 Comment(0)
G
6

Here is how you can have full control on this. Proven, working. for "@angular/cdk": "8.2.3", I'm not sure about any future version, maybe they have some built-in feature for this.

Short answer: Once you've rendered stepper component on the page. You should loop through all steps of the stepper, and override built-in select() method of each step with your own version.

What you will do in your own version?

  • do some calculations
  • make service calls
  • whatever you want
  • change currentStepIndex if you want user to go to the new step

Long answer:

HTML:

<mat-horizontal-stepper [selectedIndex]="selectedStepIndex" #stepper>

In Controller:

@ViewChild('stepper', { static: false }) stepper: MatStepper;

Once your stepper is rendered on the page, you can override select method like this:

// I personally do this in ngAfterViewInit() method
setTimeout(() => {
    this.stepper.steps.forEach((step, idx) => {
        step.select = () => {
            // Your custom code here
            // if you want to change step do execute code below
            this.selectedStepIndex = idx;
        };
    });
});
Groping answered 8/7, 2020 at 3:56 Comment(0)
M
5

The following approach works for me:

  1. Make mat-stepper linear
<mat-vertical-stepper [linear]="true" #stepper>

  ....

</mat-vertical-stepper>
  1. Make steps not editable, not optional and associate a stepControl with a FormGroup
<mat-step [editable]="false" [optional]="false" [stepControl]="desiredServiceFormGroup">
  
    <form [formGroup]="desiredServiceFormGroup">
  
      ...
  
    </form>
  
</mat-step>
  1. add controls to your FormGroup for your form items described in HTML and add a extra control. For an inexistent form control on your html code (in this example, I added the control called 'x', inexistent in my html code)
ngOnInit() {
  
  this.desiredServiceFormGroup = this.formBuilder.group({
    desiredTarget: [ '', Validators.required],
    paymentAgreed: ['', Validators.required],

    ...

    x: ['', Validators.required]
  });
  
}

With this extra validator, your stepControl will always be false. When stepControl is false, step is not optional, not editable and stepper is linear. Direct clicks on step headers do not change the current step.

  1. button will not be associated with formControl (at that case, it will be always disabled). In my case, I verify each Form item manually
<button [disabled]="desiredTarget == null || !paymentAgreed" (click)="createVerification(desiredTarget.targetId, stepper)">NEXT</button>
  1. make your needed operations and when it's done, remove the extra control that makes your form control always invalid. Go programmatically to next step.
async createVerification(targetId: number, stepper?: MatStepper) {
  
  this.verification = await this.dataService.createVerification(targetId);
  
  if (stepper !== undefined) {
    this.desiredServiceFormGroup.removeControl('x');
    stepper.next();
  }
  
}
  1. If you need to make a reset of stepper, add the extra control to FormControl
reset(stepper?: MatStepper) {
  this.desiredServiceFormGroup.addControl('x', new FormControl('', [Validators.required]));
  stepper.reset();
}
Mesocratic answered 19/12, 2019 at 6:0 Comment(0)
J
1

Here a working suggestion based on the answer of Sam Tsai which is the best I found. I simply use a "step" value is the code and only normal buttons for the navigation.

You can go back and forward only if you have the correct buttons (I mean, this not possible to change the step by clicking on steps). This mean you complete the first step then the second step and so on.

The working example is here: https://stackblitz.com/edit/angular-tznwxe

The trick is to call goStep(x) where x is the step starting at 1. You can enhance this by adding a goNext() and a goPrevious() which is trivial.

Note the "selectedIndex" of the stepper must be changed with a setTimeout(): this is known bug discussed here: https://github.com/angular/components/issues/8479#issuecomment-444063732

Jetpropelled answered 29/4, 2020 at 16:12 Comment(0)
C
0

You could try leveraging editable and completed attributes on mat-step.

See https://material.angular.io/components/stepper/overview#editable-step

Without seeing an example, one solution is to:

  1. set editable attribute to false of all other steps while service call is being made
  2. track clicks to mat-step and keep this value so you know where the user intended to go
  3. set editable attribute to true when service call is successful or fails (depending on how you want to handle errors)
  4. programmatically go to the step tracked in #2
Chladek answered 9/4, 2019 at 20:29 Comment(2)
From which event i can do this. When i click on step-header it will redirect to that step.Rhaetia
Right, I misunderstood your question. At this point, you can either try to do something with disabling pointer-events (see #46934453) which I'm not in love with or doing a custom step label via ng-template and making it cover the whole header space (this is also a hack), see stackblitz.com/angular/jbbpbonlbpeChladek
C
0

You can use completed input decorative (boolean), if step1 is marked as completed then it will go to step2.

Once your service call completed then you can marked completed as "true" then it will go to step2 otherwise it will stay in current step.

Reference: https://stackblitz.com/edit/angular-mat-stepper-demo-with-selection-change?file=src%2Fapp%2Fapp.component.html

Crusty answered 23/11, 2021 at 5:15 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Pintle

© 2022 - 2024 — McMap. All rights reserved.