feature request-ish
Each step in the stepper should have the option to be lazily rendered
Currently every step is initialized when the stepper is loaded
Inactive steps can have logic that fetches data or does some work that isn't relevant to the current step.
Currently there isn't any mention of how to achieve this in the stepper. However it can be done via <ng-container *ngIf="condition">
, not sure if there are any side effects.
<mat-horizontal-stepper>
<mat-step>
<ng-container *ngIf="condition">
...
</ng-container>
</mat-step>
</mat-horizontal-stepper>
The expansion panel supports lazy rendering "officially" https://material.angular.io/components/expansion/overview#lazy-rendering
<mat-expansion-panel>
<mat-expansion-panel-header>
This is the expansion title
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
Some deferred content
</ng-template>
</mat-expansion-panel>
Why is there a difference? If the <ng-container>
is a perfectly fine solution why doesn't the expansion panel do it the same way? Why is there no mention of lazy rendering in the stepper doc?
_ _ ____ _ ___
/ \ _ __ __ _ _ _| | __ _ _ __ / ___| | |_ _|
/ â–³ \ | '_ \ / _` | | | | |/ _` | '__| | | | | | |
/ ___ \| | | | (_| | |_| | | (_| | | | |___| |___ | |
/_/ \_\_| |_|\__, |\__,_|_|\__,_|_| \____|_____|___|
|___/
Angular CLI: 6.1.2
Node: 9.10.1
OS: win32 x64
Angular: 6.1.0
... animations, common, compiler, compiler-cli, core, forms
... http, language-service, platform-browser
... platform-browser-dynamic, router
Package Version
-----------------------------------------------------------
@angular-devkit/architect 0.7.1
@angular-devkit/build-angular 0.7.1
@angular-devkit/build-optimizer 0.7.1
@angular-devkit/build-webpack 0.7.1
@angular-devkit/core 0.7.1
@angular-devkit/schematics 0.7.1
@angular/cdk 6.4.2
@angular/cli 6.1.2
@angular/flex-layout 6.0.0-beta.17
@angular/material 6.4.2
@ngtools/webpack 6.1.1
@schematics/angular 0.7.2
@schematics/update 0.7.2
rxjs 6.2.2
typescript 2.9.2
webpack 4.9.2
Yes, I need this as well for a project. I tried a number of approaches but they don't seem to be supported by the current implementation as the compiler throws errors or steps that are added later don't show up.
Here's a nice (related) example of using ng-container *ngTemplateOutlet
inside of *ngIf
and *ngFor
.
It would be really cool if its supported out of the box
Not sure if I am just stating the obvious, but I was able to implement lazy-loading based on @snebjorn 's approach quite easily:
<mat-vertical-stepper #stepper>
<mat-step #first_step>
<ng-container *ngIf="stepper.selected == null || stepper.selected == first_step">
</ng-container>
</mat-step>
<mat-step #second_step>
<ng-container *ngIf="stepper.selected == second_step">
</ng-container>
</mat-step>
<mat-step #third_step>
<ng-container *ngIf="stepper.selected == third_step">
</ng-container>
</mat-step>
</mat-vertical-stepper>
Note that the condition for the first step needs to include the check for null, too. Otherwise you'll receive an ExpressionChangedAfterItHasBeenCheckedError.
Is there any update on when feature will be available?
@matthiaswelz I have a linear form like below. How can i implement your approach in this?
<mat-horizontal-stepper [linear]="true" #stepper [selectedIndex]="0">
<mat-step [stepControl]="step1.stepFormGroup">
<ng-template matStepLabel>Step 1</ng-template>
<app-step-one #step1></app-step-one>
</mat-step>
<mat-step [stepControl]="step2.stepFormGroup">
<ng-template matStepLabel>Step 2</ng-template>
<app-step-two #step2 ></app-step-two>
</mat-step>
<mat-step [stepControl]="step3.stepFormGroup">
<ng-template matStepLabel>Step 3</ng-template>
<app-step-three #step3 ></app-step-three>
</mat-step>
<mat-step>
<ng-template matStepLabel>Done</ng-template>
You are now done.
<div>
<button mat-button matStepperPrevious>Back</button>
</div>
</mat-step>
</mat-horizontal-stepper>
If i try to add <app-step-one #step1></app-step-one>
inside ng-container
with *ngIf
then i am unable to get [stepControl]="step1.stepFormGroup"
from working.
Is there any update?
I tried @matthiaswelz solution, but it still seems to be loading all 3 components. I can verify this by removing the 2nd & 3rd components and watch the load time of the first step go down from 10 seconds to 2 seconds. The components in each of my three steps are not huge, but not trivial either.
@akvaliya A workaround that I haven't tried:
step1FormGroup
, step2FormGroup
, and step3FormGroup
in the parent component you have here.app-step-one
, ... etc.Yup, not so clean. Also: not sure if a FormGroup
must be initialized when constructed or not, but it must, then it's even not-cleaner, as the parent component will now know too much about the children.
You can use use
template file:
<mat-horizontal-stepper #stepper>
<mat-step *ngFor="let step of steps; let i=index;">
<router-outlet *ngIf="stepper.selectedIndex==i"></router-outlet>
</mat-step>
</mat-horizontal-stepper>
routes.ts:
const routes: Routes = [
{ path: 'step0', loadChildren: module for component 1},
]
}
];
@matthiaswelz "CdkStep and TemplateRef have no overlap".
Next level of @matthiaswelz's workaround in order to get the collapse animation right: Render the previous step 225ms longer.
<mat-vertical-stepper (selectionChange)="selectionChange($event)">
<mat-step #step1>
<ng-container *ngIf="renderStep('default') || renderStep(step1)">
...
</ng-container>
</mat-step>
<mat-step #step2>
<ng-container *ngIf="renderStep(step2)">
...
</ng-container>
</mat-step>
private _renderStep: CdkStep[] = [];
renderStep(step: CdkStep | 'default'): boolean {
// workaround for https://github.com/angular/components/issues/12817#issuecomment-457314797
return step === 'default' ? this._renderStep.length === 0 : this._renderStep.includes(step);
}
selectionChange(event: StepperSelectionEvent) {
this._renderStep.push(event.selectedStep);
// see https://github.com/angular/components/blob/master/src/material/stepper/stepper-animations.ts
setTimeout(() => this._renderStep = [event.selectedStep], 225);
}
It looks like a duplicate of #12339, which has been closed due to the merge of the #15817.
@crisbeto, Btw, this feature will only be available in version 11.3.0, right?
Most helpful comment
Not sure if I am just stating the obvious, but I was able to implement lazy-loading based on @snebjorn 's approach quite easily:
Note that the condition for the first step needs to include the check for null, too. Otherwise you'll receive an ExpressionChangedAfterItHasBeenCheckedError.