Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular 7 - Stepper component working with routing

I need an checked solution of Stepper component for Angular 7, that works with Angular routing.

Not a Material Design Stepper - it does work well for simple forms, but not routing.

What I tried to do, with <mat-horizontal-stepper>, was something like this:

component.html:

<mat-horizontal-stepper (selectionChange)="selectionChanged($event)" [linear]="true">
    <mat-step *ngFor="let step of steps; let i = index" [label]="step.title">
        <router-outlet *ngIf="i === selectedStepIndex"></router-outlet>
    </mat-step>
</mat-horizontal-stepper>

component.ts:

public selectedStepIndex: number = 0;

selectionChanged(event: any) {
    this.selectedStepIndex= event.selectedIndex;
    const url = '/some-path/' + this.links[this.selectedStepIndex].path;
    this.router.navigate([url]);//using -> private router: Router
}

However, I can not navigate back for some reason. Stepper tries to navigate back, but it shows the same page, unless it is the first page of stepper (i = 0).

I would be greatful for any recommendation (and maybe working example), or a detailed information about how to achive this with <mat-horizontal-stepper>.

like image 543
Wojciech X Avatar asked Sep 12 '25 20:09

Wojciech X


2 Answers

Alright! I have figured it out :)

It looks like <mat-horizontal-stepper> needs some time to reaload showed step, when it comes to <router-outlet> - so I had to manually force him to wait. Do something like this, and it will work good enough:

component.html:

<mat-horizontal-stepper (selectionChange)="selectionChanged($event)" [linear]="true">
    <mat-step *ngFor="let step of steps; let i = index" [label]="step.title">
        <div *ngIf="loadingStep">
            <mat-spinner></mat-spinner>
        </div>
        <div *ngIf="!loadingStep">
            <router-outlet *ngIf="i === selectedStepIndex"></router-outlet>
        </div>
    </mat-step>
</mat-horizontal-stepper>

component.ts:

public selectedStepIndex: number = 0;
public loadingStep: boolean = false;

selectionChanged(event: any) {
    this.selectedStepIndex = event.selectedIndex;
    if (event.previouslySelectedIndex > event.selectedIndex) {
        this.loadingStep = true;
        //Wait 1 sec. before showing the step
        setTimeout(() => {
            this.navigate();
            this.loadingStep = false;
        }, 1000);
    } else {
        this.navigate();
    }
}

private navigate(): void {
    const url = '/some-path/' + this.links[this.selectedStepIndex].path;
    this.router.navigate([url]);//using -> private router: Router
}

It is not perfect, but it is good enough for me :))

like image 65
Wojciech X Avatar answered Sep 14 '25 09:09

Wojciech X


You misunderstand the <router-oulet>. It display the component matching the routes you define in your routing module. So if the <mat-step> does not trigger URL changes or route navigation, the <router-oulet> displays the same component:

You can achieve your intent with logic like this:

Add [routerLink] directive to <mat-step>:

<mat-horizontal-stepper (selectionChange)="selectionChanged($event)" [linear]="true">
    <mat-step *ngFor="let step of steps; let i = index" [routerLink]="['/step' + i]" [label]="step.title">
        <router-outlet *ngIf="i === selectedStepIndex"></router-outlet>
    </mat-step>
</mat-horizontal-stepper>

Define the component's route in your routing module :

const routes: Routes = [
      //Just an example for the logic i used  
      { path: 'step0', component: StepOComponent },
      { path: 'step1', component: Step1Component },
      { path: 'step2', component: Step2Component },
    ]
 }
];
like image 37
Ethan Vu Avatar answered Sep 14 '25 09:09

Ethan Vu