1

I'm trying to implement the Angular Material Stepper functionality in our application. Based on the documentation here I came up with the following solution:

civilliability-proposal-detail.component.html

<form [formGroup]="formGroup">
  <mat-horizontal-stepper labelPosition="bottom" formArrayName="formArray" #stepper>
    <mat-step formGroupName="0" [stepControl]="formArray?.get([0])" [editable]="true">
      <div>
        <ng-template matStepLabel>{{ 'proposals.details.civilLiability.stepper.details' | translate }}</ng-template>
        <app-civilliability-step1 [model]="model.details"></app-civilliability-step1>

        <button mat-button matStepperNext type="button">Next</button>
      </div>
    </mat-step>

    <mat-step formGroupName="1" [stepControl]="formArray?.get([1])" [editable]="true">
      <div>
        <ng-template matStepLabel>{{ 'proposals.details.civilLiability.stepper.antecedents' | translate }}</ng-template>
        <!-- <app-civilliability-step2 [model]="model.questionnaire"></app-civilliability-step2> -->

        <button mat-button matStepperPrevious type="button">Back</button>
        <button mat-button matStepperNext type="button">Next</button>
      </div>
    </mat-step>
  </mat-horizontal-stepper>
</form>

civilliability-proposal-detail.component.ts

get formArray(): AbstractControl | null {
  return this.formGroup.get('formArray');
}

constructor(private formBuilder: FormBuilder) {}

ngOnInit() {
  this.buildForms();
}

buildForms() {
  this.formGroup = this.formBuilder.group({
    formArray: this.formBuilder.array([this.buildDetailsForm(), this.buildQuestionnaireForm()])
  });
}

buildDetailsForm(): FormGroup {
  if (typeof this.model === 'undefined') {
    this.model = getEmptyCivilLiabilityRequest();
  }

  const formGroup = this.formBuilder.group({
    horses: new FormControl(this.model.details.horses, Validators.min(0)),
    properties: new FormControl(this.model.details.properties, Validators.min(0)),
    garages: new FormControl(this.model.details.garages, Validators.min(0)),
    ...
  });

  return formGroup;
}

buildQuestionnaireForm(): FormGroup {
  const formGroup = this.formBuilder.group({});

  return formGroup;
}

civilliability-step1.component.html

<form [formGroup]="formGroup">
  <section class="row">
    <section class="col-md-4 full-width-form">
      <mat-form-field>
        <mat-label>{{ 'proposals.details.civilLiability.horses' | translate }}</mat-label>
        <input type="number" matInput formControlName="horses" />
        <mat-error>{{ formGroup.controls['horses'].getError('server-error') }}</mat-error>
      </mat-form-field>
    </section>
    <section class="col-md-4 full-width-form">
      <mat-form-field>
        <mat-label>{{ 'proposals.details.civilLiability.terrainsWithTrees' | translate }}</mat-label>
        <input type="number" matInput formControlName="terrainsWithTrees" />
        <mat-error>{{ formGroup.controls['terrainsWithTrees'].getError('server-error') }}</mat-error>
      </mat-form-field>
    </section>
    <section class="col-md-4 full-width-form">
      <mat-form-field>
        <mat-label>{{ 'proposals.details.civilLiability.garages' | translate }}</mat-label>
        <input type="number" matInput formControlName="garages" />
        <mat-error>{{ formGroup.controls['garages'].getError('server-error') }}</mat-error>
      </mat-form-field>
    </section>
  </section>
</form>

Based on this I get a bunch of errors which I think are related to the fact that I'm using child components in my form, but I'm not quite sure how to tackle this. So, any suggestions?

I've also read somebody not using the formArrayName in the stepper, but I can't seem to find this example anymore, so can't test if that works or not.

enter image description here

Jordec
  • 1,516
  • 2
  • 22
  • 43
  • Have you tried initialising the forms in individual child components rather than using the `formArray`? You can then get the output using event emitters/public properties from the child components. – umutesen Mar 15 '19 at 09:46
  • So you mean instead of initialising the forms in my parent component, I initialise them in the child component, right? I'm not quite sure how to use the stepper without the formArray though, since it's not documented (or I just can't find it) – Jordec Mar 15 '19 at 09:49
  • Yes, having individual form groups in each step's child component. If I can find the code, I'll provide a stackblitz example. – umutesen Mar 15 '19 at 10:07

1 Answers1

3

Instead of using a form array, I would recommend declaring a form group in each individual step component. Here is how I have done it:

  1. Create a component to keep the stepper.
  2. Create a component for each step and initialise it in the step component.
  3. Use public component variables or define a model to keep user input.
  4. You can then access the public members of each step component in the parent stepper component via data binding.

Live demo

umutesen
  • 2,523
  • 1
  • 27
  • 41
  • 2
    Your solution was a great idea and it got my slider working! I did change one thing though. Instead of having the get step1() step2 and sterp3() I've removed those and added this in the html: https://i.stack.imgur.com/Wxr8H.png – Jordec Mar 15 '19 at 12:33