web-dev-qa-db-fra.com

Angular Dialogue Material: Comment mettre à jour les données injectées quand elles changent dans le composant parent?

L'injection de données dans une boîte de dialogue de matériau fonctionne bien, mais comment peut-on mettre à jour automatiquement ces données si elles changent dans le composant parent/ouvreur (provenant d'un abonnement (WAMP) par exemple)?

[Mise à jour]
J'ai créé une version minimale pour tester le comportement: https://stackblitz.com/edit/angular-njh44v?embed=1&file=src/app/app.component.ts
Ouvrez la console en bas à droite et cliquez sur la case grise avec les chiffres. Vous voyez que la boîte de dialogue obtient le numéro actuel et ne met plus à jour après cela.
[/ Mise à jour]

[Mise à jour 2]
Basé sur la deuxième idée de Jusmpty, cela semble fonctionner à première vue (même si la boîte de dialogue affiche/met à jour la première fois après l'arrivée des données): https://stackblitz.com/edit/ angular-thd34f? embed = 1 & file = src/app/app.component.ts
[/ Mise à jour]

Le cas concret ressemble à ceci:

  • Il existe un composant area qui s'abonne à un service WAMP et obtient toutes les données data pour les parties incluses. Cela dessine toutes les parties avec un * ngFor.
    Fichier area.component.ts
  • Chaque partie possède un propre composant affichant quelques données. Si les données dans les abonnements changent, la vue est correctement et automatiquement mise à jour, pour chaque/tous pièces.
    Fichiers part.component.ts et part.component.html
  • Sur chaque partie un clic ouvre une boîte de dialogue où plus de données s'affichent.
    Fichiers part-details-dialog.component.ts et part-details-dialog.component .html

Et sur cette dernière action/étape se produit le problème où les données actuelles s'affichent (injection) mais ne sont plus mises à jour si quelque chose dans les données abonnées change.

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

Au lieu de cela, vous pouvez simplement modifier data de l'instance du composant, comme ceci:

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

Exemple ici: https://stackblitz.com/edit/angular-dialog-update

21
penghui

Il y a 2 façons de penser en ce moment, je n'aime pas vraiment non plus, mais bon ...

Les deux manières impliquent l'envoi d'un observable à la boîte de dialogue à travers les données.

  1. Vous pouvez transmettre l'observable de la pièce au composant de la pièce, puis transmettre l'observable des données à la boîte de dialogue. La boîte de dialogue pourrait alors s'abonner à l'observable et obtenir les mises à jour de cette façon.

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

Composant de pièce

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

Bien sûr, la question est alors de savoir à quel point il est utile de transmettre même partData, si vous pouviez tout de même accéder directement aux données.

  1. L'autre façon vous oblige à modifier simplement le composant de pièce, mais vous devrez créer un sujet que vous alimenterez les modifications apportées à la pièce que le composant reçoit. Je n'aime pas vraiment faire des sujets, surtout si les données proviennent déjà d'un observable, mais peu importe:

Composant de pièce

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

enfin, pour accéder aux données, vous pouvez changer le html en

Dialogue 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>

Ou vous pouvez vous abonner à observable dans le composant de dialogue et fournir les données à partir de là

2
Jusmpty