Components: Unable to use MatStepper with transclusion

Created on 25 Oct 2017  路  10Comments  路  Source: angular/components

Bug, feature request, or proposal:

Bug

What is the expected behavior?

Since there is not currently a responsive stepper, I am attempting to write a component that will switch between a horizontal and vertical stepper depending on the size of the viewport. My idea was that the responsive-stepper would not define any steps inside it. It would use *ngIf to determine which stepper to render, and then use <ng-content> transclusion to allow other components to include the actual steps.

What is the current behavior?

When I attempt to use <mat-step> outside of a stepper, I get an error No provider for MatStepper. I have set up a stackblitz reproduction of the bug, the error is visible in the console.

What are the steps to reproduce?

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

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

I'm working in an enterprise and I'd like to be able to define a responsive stepper component once, and let others reuse it in a generic fashion. I'm open to other strategies to achieve this.

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

Material beta 12

Is there anything else we should know?

P3 feature needs discussion

Most helpful comment

any updates on this one?

All 10 comments

I have never test it, but you could try, instead of <mat-step>, use a div as a container with the attribute ngProjectAs="mat-step". It will work if mat-stepper expects mat-step to be a select attribute value of an ng-content. I doubt, but take a look at the source code and give it a shot if you think It makes sense.

Thanks for the suggestion, I did give it a shot but that didn't work either (as I saw you just commented).

I started down the path of just having 2 steppers inside my component, using ngIf to hide them, and then to avoid duplicating logic, pulling out the content of the mat-steps into separate components - something like this:

<mat-card>
  <mat-card-content>
    <mat-horizontal-stepper linear="true" *ngIf="facade.largeScreen">
      <mat-step label="Verify Account" [stepControl]="verify.group">
        <dash-password-verify #verify></dash-password-verify>
      </mat-step>
      <mat-step label="New Password">
        <dash-password-choose></dash-password-choose>
      </mat-step>
      <mat-step label="Secondary Email">
        <dash-password-email></dash-password-email>
      </mat-step>
    </mat-horizontal-stepper>

    <mat-vertical-stepper linear="true" *ngIf="!facade.largeScreen">
      <mat-step label="Verify Account" [stepControl]="verify.group">
        <dash-password-verify></dash-password-verify>
      </mat-step>
      <mat-step label="New Password">
        <dash-password-choose></dash-password-choose>
      </mat-step>
      <mat-step label="Secondary Email">
        <dash-password-email></dash-password-email>
      </mat-step>
    </mat-vertical-stepper>
  </mat-card-content>
</mat-card>

However, I think that will have its own set of issues. When one component is destroyed, I think I'm going to end up losing the user's form state, unless I put in code to manage it separately. The same problems would apply if I were to use fxShow/fxHide from @angular/flex-layout, since there would be two instances of the controls.

It sounds like the only way forward at this time is to have the form/forms contained within a single component, and duplicate the markup for both steppers, which is less than ideal.

@rpd10 I think this _may_ be possible if MatStepper would use descendants: true on it's ContentChildren query, (there's a lot more discussion on https://github.com/angular/material2/issues/6625 about a related, but slightly different problem)

https://github.com/angular/material2/blob/0ea43700efb105aa02ad3b1d3f800bf4a9925883/src/lib/stepper/stepper.ts#L69-L75

You may also be able to create your own implementation of MatStepper that adds the correct classes and animations,

https://github.com/angular/material2/blob/0ea43700efb105aa02ad3b1d3f800bf4a9925883/src/lib/stepper/stepper.ts#L103-L127

any updates on this one?

I got around this for now by having an <ng-container>, the <mat-step> then another <ng-container>with an *ngTemplateOutlet pointed at the template which in turn has <ng-content> in it. I feel like there has to be a better way but I haven't found it yet.

@cmsImagine could you please tell us more about your solution (in code)?

any updates on this one? x2

I didn't find a way to transclude the full template, but if you're looking to just transclude everything but the title and the previous/next buttons, then this worked:

<ng-template #matStep1 let-fileInput="fileInput">
  <p>
  My form
  </p>
  <mat-form-field>
    <input matInput placeholder="Upload" [formControl]="fileInput" required autocomplete="off">
    <mat-error *ngIf="fileInput.hasError('required')">
      File is <strong>required</strong>
    </mat-error>
  </mat-form-field>
</ng-template>

<mat-horizontal-stepper #horizontalStepper fxFlex>
     <mat-step [stepControl]="fileInput">
        <ng-template matStepLabel>{{STEP_NAMES.step1}}</ng-template>
        <ng-container *ngTemplateOutlet="matStep1;context:{fileInput: fileInput}">
        </ng-container>
        <button mat-button class="button-back" type="button">Back</button>
        <button mat-flat-button color="accent" matStepperNext type="button" (click)="markAsTouched(fileInput)">Next</button>
     </mat-step>
</mat-horizontal-stepper>

This is a limitation of Angular's content projection. https://github.com/angular/angular/issues/37319 has been filed to hopefully improve some of the short-comings

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

Was this page helpful?
0 / 5 - 0 ratings

Related issues

alanpurple picture alanpurple  路  3Comments

RoxKilly picture RoxKilly  路  3Comments

LoganDupont picture LoganDupont  路  3Comments

dzrust picture dzrust  路  3Comments

MurhafSousli picture MurhafSousli  路  3Comments