web-dev-qa-db-ja.com

angular-Angularマテリアル2ステッパーコントロール

Angular Material 2Stepperを使用して線形ステッパーをセットアップしました。

さまざまなコンポーネント(コンポーネント-a、コンポーネント-b、コンポーネント-c)のフォームがあります。私のメインコンテナコンポーネント(container-component)には、フォームが有効なときに各コンポーネントを「ステップスルー」する線形ステッパーが必要です。

ステッパーのstepControlと通信して、正しく機能させるための何らかの機能はありますか?

ステッパーとStackBlitzバージョンのアプリケーションのドキュメントを添付しました。また、私が作業しているリポジトリへのリンクもあります。

マテリアルステッパーコンポーネント: https://material.angular.io/components/stepper/overview

StackBlitz: https://stackblitz.com/edit/angular-material2-issue-mjmu9u?file=app/app.component.ts

Github: https://github.com/sam11385

5
Sam Luedke

まったく同じ問題が発生し、数日間の試行錯誤の末、実用的な解決策が見つかりました。基本的に、ステップはそれぞれがフォームを定義するいくつかのコンポーネントに分割され、対応するstepControlが親にある必要があるため、子コンポーネントのFormGroupをステッパーを定義する親コンポーネントに送信する必要があります。 FormGroupを作成した後、イベントを発行し、親にそのイベントをリッスンさせ、FormGroupをそのイベントに渡します。これをすべての子に適用して、それぞれに個別のイベントを作成し、各エミッターの親に個別のリスナーを作成できます。

子コンポーネントの場合:

  1. イベントを宣言する

    @Output() notify: EventEmitter<FormGroup> = new EventEmitter<FormGroup>();                                                                                                         
    
  2. formGroupを親コンポーネントに発行します

    this.notify.emit(this.myFormGroup);                                                                                                                                                
    

マットステッパーのstepControl(s)を含む親コンポーネント:

  1. Component(.ts)に、フォームグループを受信して​​設定する関数を追加します。

    myFormGroup: FormGroup;
    onNotify(formGroup: FormGroup): void {
      this.myFormGroup = formGroup;
    }
    
  2. HTMLで、通知リスナーを追加します。

    <mat-step [completed]="false" [stepControl]="myFormGroup">
      <ng-template matStepLabel>Step 1</ng-template>
      <app-child (notify)='onNotify($event)'></app-child>
    </mat-step>                                                                                                                                            
    

ネストされたコンポーネントの説明: https://www.themarketingtechnologist.co/building-nested-components-in-angular-2/

11
prusik

上記の答えは的を射ていますが、特定のユースケースでこの実装でいくつかのより深い問題に遭遇したため、共有サービスで解決しました。

form-management.service.ts

import { Injectable } from '@angular/core';
import { FormGroup } from '@angular/forms';
import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';

@Injectable()
export class FormManagementService {

    private formSource: Subject<FormGroup> = new Subject();
    locationForm: Observable<FormGroup> = this.formSource.asObservable();

    formReady(form: FormGroup): void {
        this.formSource.next(form);
    }
}

parent.component.ts

import { Component } from '@angular/core';
import { FormGroup } from '@angular/forms';
import { FormManagementService } from './form-management.service';

@Component({
    ....
    providers: [ FormManagementService ]
})
export class ParentComponent {

    form: FormGroup;

    constructor(private formManagementService: FormManagementService) {
        this.formManagementService.locationForm.subscribe(form => this.form = form);
    }

}

ここでは、共有サービスの変更をサブスクライブしていることに注意してください。これにより、必要なときにフォームがコンポーネントに渡されます。親コンポーネントがフォームを受信すると、FormGroupにデータが入力されます。

また、コンポーネントデコレータの一部としてここで共有サービスを提供していることにも注意してください。コードが構造化されている場合は、機能モジュールで提供することもできます。

child.component.ts

import { Component } from '@angular/core';
import { FormGroup, FormBuilder } from '@angular/forms';
import { FormManagementService } from './form-management.service';

@Component({ ... })
export class ChildComponent {

    form: FormGroup;

    constructor(private formManagementService: FormManagementService,
                private formBuilder: FormBuilder) {
        this.form = this.formBuilder.group({});
        this.formManagementService.formReady(this.form);
    }

}

ここで子コンポーネントでフォームを初期化し、共有サービスを介してプッシュします。これにより、フォームが親コンポーネントに入力されます。

コンストラクター以外の場所でこれを実行した場合、ExpressionChangedAfterItHasBeenCheckedErrorでこれを実行したときに、ngAfterContentInitevenに遭遇したことに注意してください。おそらく、他の誰かがこれを解決するために私のソリューションをさらに進歩させるでしょう。

とにかく、この問題が数週間私を悩ませていたので、これがそこにいる誰かの助けになることを願っています。

2
lordchancellor