web-dev-qa-db-ja.com

Angular Material Dialog:挿入されたデータが親コンポーネントで変更されたときに更新する方法は?

マテリアルダイアログへのデータの注入はうまく機能していますが、これらのデータが親/オープナーコンポーネントで変更された場合(たとえば(WAMP)サブスクリプションから)自動的に更新する方法はありますか?

[更新]
動作がどのようになるかを試すために、最小バージョンを作成しました: https://stackblitz.com/edit/angular-njh44v?embed=1&file=src/app/app.component.ts
右下のコンソールを開き、数字の付いた灰色のボックスをクリックします。ダイアログが現在の番号を取得し、それ以降は更新しないことがわかります。
[/ Update]

[更新2]
Jusmptyの2番目のアイデアに基づいて、これは一目で機能します(データが到着した後にダイアログが最初に表示/更新する場合でも): https://stackblitz.com/edit/ angle-thd34f?embed = 1&file = src/app/app.component.ts
[/ Update]

具体的なケースは次のようになります。

  • WAMPサービスにサブスクライブし、partsを含むすべてのdataを取得するareaコンポーネントがあります。これは、すべてのpartsを* ngForで描画します。
    ファイルarea.component.ts
  • partには、いくつかのdataを示す独自のコンポーネントがあります。サブスクリプションのdataが変更された場合、ビューは各/すべてpartsに対して正しく自動的に更新されます。
    ファイルpart.component.tsおよびpart.component.html
  • partをクリックすると、さらにデータが表示されるdialogが開きます。
    ファイルpart-details-dialog.component.tsおよびpart-details-dialog.component .html

そして、この最後のアクション/ステップで、現在のデータが表示(注入)されますが、サブスクライブされたデータの何かが変更されても更新されないという問題が発生します。

area.component.ts

@Component({
  selector: 'app-area',
  template: '<app-part *ngFor="let part of plan" [partData]="part"></app-part>'
})
export class AreaComponent {

  plan = [];

  planasync$ = this.wampService
    .topic('sendplan')
    .subscribe(
      res => {
        let tm = new TableMap();
        this.plan = tm.tableToMap(res.argskw).filter((m) => m.area === 1);
      },
      err => console.log("res err", err),
      () => console.log("res complete"));

  constructor(private wampService: WampService) { }
}

part.component.ts

import { PartDetailsDialogComponent } from './part-details-dialog/part-details-dialog.component';

@Component({
  selector: 'app-part-details',
  templateUrl: './part.component.html'
})
export class PartComponent {
  @Input() partData: any;

  constructor(public dialog: MatDialog) {}

  openDetailsDialog(): void {
    let dialogRef = this.dialog.open(PartDetailsDialogComponent, {
      width: '650px',
      height: '400px',
      data: {
        partData: this.partData
      }
    });
  }
}

part.component.html

<mat-card (click)="openDetailsDialog()">
  <mat-card-title>Nr: {{ partData.partNr }}</mat-card-title>
  <mat-card-content>
      <div>Current speed: {{ partData.speed }}</div>
  </mat-card-content>
</mat-card>

part-details-dialog.component.ts

@Component({
  selector: 'app-part-details-dialog',
  templateUrl: './part-details-dialog.component.html'
})
export class PartDetailsDialogComponent {

  constructor(public dialogRef: MatDialogRef<PartDetailsDialogComponent>, @Inject(MAT_DIALOG_DATA) public data: any) { }

  onNoClick(): void {
    this.dialogRef.close();
  }
}

part-details-dialog.component.html

<h1 mat-dialog-title>Part Details for Nr. {{ data.partData.partNr }}</h1>
<div mat-dialog-content>
  <div>Current speed details: {{ data.partData.speed }}</div>
</div>
<div mat-dialog-actions>
  <button mat-button (click)="onNoClick()">Cancel</button>
  <button mat-button [mat-dialog-close]="data.animal" cdkFocusInitial>Ok</button>
</div>
16
Billy G

その代わりに、次のようにコンポーネントインスタンスのdataを変更するだけです。

this.dialogRef.componentInstance.data = {numbers: value};

ここの例: https://stackblitz.com/edit/angular-dialog-update

21
penghui

現時点では2つの方法がありますが、どちらもあまり好きではありませんが、ちょっと...

どちらの方法でも、データを通じてダイアログにオブザーバブルを送信する必要があります。

  1. 部品のオブザーバブルを部品コンポーネントに渡し、データのオブザーバブルをダイアログに渡すことができます。その後、ダイアログはオブザーバブルをサブスクライブし、そのようにして更新を取得できます。

AreaComponent

@Component({
  selector: 'app-area',
  template: '<app-part *ngFor="let part of planAsync$ | async; i as index" [partData]="part" [part$]="part$(index)"></app-part>'
})
export class AreaComponent {

  plan = [];

  constructor(private wampService: WampService) {
  }

  part$(index) {
    return this.planAsync$
      .map(plan => plan[index]);
  }

  get planAsync$() {
    return this.wampService
      .topic('sendplan')
      .map(res => {
        let tm = new TableMap();
        return tm.tableToMap(res.argskw).filter((m) => m.area === 1);
      });
  }
}

部品コンポーネント

@Component({
  selector: 'app-part-details',
  templateUrl: './part.component.html'
})
export class PartComponent {
  @Input() partData: any;
  @Input() part$

  constructor(public dialog: MatDialog) {}

  openDetailsDialog(): void {
    let dialogRef = this.dialog.open(PartDetailsDialogComponent, {
      width: '650px',
      height: '400px',
      data: {
        partData: this.partData,
        part$: this.part$
      }
    });
  }
}

もちろん、問題は、とにかく直接データにアクセスできる場合、partDataを渡すことですら便利なことです。

  1. もう1つの方法では、パーツコンポーネントを変更する必要がありますが、コンポーネントが受け取るパーツに変更をフィードするサブジェクトを作成する必要があります。特にデータがすでに観測可能なものから来ている場合は特に、被験者を作るのは好きではありませんが、

部品コンポーネント

@Component({
  selector: 'app-part-details',
  templateUrl: './part.component.html'
})
export class PartComponent, OnChanges {
  @Input() partData: any;

  private Subject part$ = new Subject();

  constructor(public dialog: MatDialog) {}

  ngOnChanges(changes){
    this.part$.next(changes['partData']);    
  }

  openDetailsDialog(): void {
    let dialogRef = this.dialog.open(PartDetailsDialogComponent, {
      width: '650px',
      height: '400px',
      data: {
        partData: this.partData,
        part$: this.part$
      }
    });
  }
}

最後に、データにアクセスするには、htmlを次のように変更できます。

ダイアログHTML

<div *ngIf="(data.part$ | async) as part">
  <h1 mat-dialog-title>Part Details for Nr. {{ part.partNr }}</h1>
  <div mat-dialog-content>
    <div>Current speed details: {{ part.speed }}</div>
  </div>
  <div mat-dialog-actions>
    <button mat-button (click)="onNoClick()">Cancel</button>
    <button mat-button [mat-dialog-close]="data.animal" cdkFocusInitial>Ok</button>
  </div>
</div>

または、ダイアログコンポーネントでobservableをサブスクライブし、そこからデータを提供できます

2
Jusmpty