Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(stepper): support lazy rendering of steps #12817

Closed
snebjorn opened this issue Aug 24, 2018 · 15 comments
Closed

feat(stepper): support lazy rendering of steps #12817

snebjorn opened this issue Aug 24, 2018 · 15 comments
Labels
area: cdk/stepper feature This issue represents a new feature or feature request rather than a bug or bug fix P4 A relatively minor issue that is not relevant to core functions perf This issue is related to performance

Comments

@snebjorn
Copy link

Bug, feature request, or proposal:

feature request-ish

What is the expected behavior?

Each step in the stepper should have the option to be lazily rendered

What is the current behavior?

Currently every step is initialized when the stepper is loaded

What is the use-case or motivation for changing an existing behavior?

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?

Which versions of Angular, Material, OS, TypeScript, browsers are affected?

     _                      _                 ____ _     ___
    / \   _ __   __ _ _   _| | __ _ _ __     / ___| |   |_ _|
   / △ \ | '_ \ / _` | | | | |/ _` | '__|   | |   | |    | |
  / ___ \| | | | (_| | |_| | | (_| | |      | |___| |___ | |
 /_/   \_\_| |_|\__, |\__,_|_|\__,_|_|       \____|_____|___|
                |___/


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
@josephperrott josephperrott added feature This issue represents a new feature or feature request rather than a bug or bug fix P4 A relatively minor issue that is not relevant to core functions labels Sep 6, 2018
@Splaktar
Copy link
Member

Splaktar commented Sep 23, 2018

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.

@Splaktar Splaktar changed the title Support/Doc Lazy rendering in Stepper feat(stepper): support lazy rendering of steps Sep 23, 2018
@Splaktar
Copy link
Member

Here's a nice (related) example of using ng-container *ngTemplateOutlet inside of *ngIf and *ngFor.

@olaf89
Copy link

olaf89 commented Dec 5, 2018

It would be really cool if its supported out of the box

@matthiaswelz
Copy link

matthiaswelz commented Jan 24, 2019

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.

@akvaliya
Copy link

Is there any update on when feature will be available?

@akvaliya
Copy link

@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.

@Flashunt
Copy link

Is there any update?

@russcarver
Copy link

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.

@hossameldeen
Copy link

@akvaliya A workaround that I haven't tried:

  • Create step1FormGroup, step2FormGroup, and step3FormGroup in the parent component you have here.
  • Pass them down to 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.

@Hallah-94
Copy link

Hallah-94 commented Dec 16, 2019

You can use use to load content of steps and in routes' file, use lazy loading for loading modules in each step.

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},
    ]
 }
];

@tatsujb
Copy link

tatsujb commented Jan 28, 2020

@matthiaswelz "CdkStep and TemplateRef have no overlap".

@hartmair
Copy link

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);
}

@rafaelss95
Copy link
Contributor

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?

@jelbourn
Copy link
Member

Duplicate of #12339

@jelbourn jelbourn marked this as a duplicate of #12339 Aug 12, 2021
@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Sep 13, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area: cdk/stepper feature This issue represents a new feature or feature request rather than a bug or bug fix P4 A relatively minor issue that is not relevant to core functions perf This issue is related to performance
Projects
None yet
Development

No branches or pull requests