web-dev-qa-db-fra.com

Angular 2: Comment détecter les modifications dans un tableau? (propriété @input)

J'ai un composant parent qui récupère un tableau d'objets à l'aide d'une requête ajax.

Ce composant a deux composants enfants: l'un d'eux montre les objets dans une arborescence et l'autre en rend le contenu sous forme de tableau. Le parent transmet le tableau à ses enfants par le biais d'une propriété @input et affiche le contenu correctement. Tout comme prévu.

Le problème se produit lorsque vous modifiez un champ dans les objets: les composants enfants ne sont pas informés de ces modifications. Les modifications ne sont déclenchées que si vous réaffectez manuellement le tableau à sa variable.

Je suis habitué à travailler avec Knockout JS et il me faut un effet similaire à observableArrays.

J'ai lu quelque chose sur DoCheck mais je ne sais pas comment cela fonctionne.

52
pablolmedorado

OnChangesLifecycle Hook ne se déclenche que lorsque l'instance de la propriété d'entrée change.

Si vous voulez vérifier si un élément du tableau d'entrée a été ajouté, déplacé ou supprimé, vous pouvez utiliser IterableDiffers à l'intérieur du DoCheck Lifecycle Hook comme suit:

constructor(private _iterableDiffers: IterableDiffers) {
    this.iterableDiffer = this._iterableDiffers.find([]).create(null);
}

ngDoCheck() {
    let changes = this.iterableDiffer.diff(this.inputArray);
    if (changes) {
        console.log('Changes detected!');
    }
}

Si vous devez détecter les modifications d'objets dans un tableau, vous devrez parcourir tous les éléments et appliquer KeyValueDiffers à chaque élément. (Vous pouvez le faire en parallèle avec le contrôle précédent).

Visitez ce post pour plus d'informations: Détecter les modifications d'objets dans un tableau dans Angular2

84
Seid Mehmedovic

Lisez l'article suivant, ne manquez pas les objets mutables et immuables.

Le problème clé est que vous modifiez les éléments du tableau, tandis que la référence au tableau reste la même. Et la détection de changement Angular2 vérifie uniquement la référence du tableau pour détecter les changements. Une fois que vous aurez compris le concept d’objets immuables, vous comprendrez pourquoi vous avez un problème et comment le résoudre.

J'utilise redux store dans l'un de mes projets pour éviter ce genre de problème.

https://blog.blingtram.io/angular/2016/02/22/angular-2-change-detection-explained.html

9
Jan Cejka

Vous pouvez toujours créer une nouvelle référence au tableau en le fusionnant avec un tableau vide:

this.yourArray = [{...}, {...}, {...}];
this.yourArray[0].yourModifiedField = "whatever";

this.yourArray = [].concat(this.yourArray);

Le code ci-dessus modifiera la référence du tableau et déclenchera le mécanisme OnChanges dans les composants enfants.

9
Wojciech Majerski

Vous pouvez utiliser IterableDiffers

Il est utilisé par * ngFor

constructor(private _differs: IterableDiffers) {}

ngOnChanges(changes: SimpleChanges): void {
  if (!this._differ && value) {
     this._differ = this._differs.find(value).create(this.ngForTrackBy);
  }
}

ngDoCheck(): void {
  if (this._differ) {
    const changes = this._differ.diff(this.ngForOf);
    if (changes) this._applyChanges(changes);
  }
}
6
Günter Zöchbauer

Cela semble déjà répondu. Cependant, pour les futurs demandeurs de problèmes, je voulais ajouter quelque chose qui manquait lorsque je recherchais et déboguais un problème de détection de changement que je rencontrais. Maintenant, mon problème était un peu isolé et, certes, une erreur stupide de ma part, mais néanmoins pertinente. Lorsque vous mettez à jour les valeurs dans la référence Array ou Object, assurez-vous que vous vous trouvez dans la bonne portée. Je me suis mis dans un piège en utilisant setInterval(myService.function, 1000), où myService.function() mettrait à jour les valeurs d'un tableau public, que j'ai utilisé en dehors du service. Cela n'a jamais réellement mis à jour le tableau, car la liaison était désactivée et l'utilisation correcte aurait dû être setInterval(myService.function.bind(this), 1000). J'ai perdu mon temps à essayer des bidouilles de détection de changement, alors que c'était une gaffe idiote/simple. Éliminer la portée en tant que coupable avant d'essayer des solutions de détection de changement; cela pourrait vous faire gagner du temps.

1
bess

Vous pouvez utiliser un tuyau impur si vous utilisez directement le tableau dans votre modèle de composants. (Cet exemple concerne les tableaux simples ne nécessitant pas de vérification approfondie)

@Pipe({
  name: 'arrayChangeDetector',
  pure: false
})
export class ArrayChangeDetectorPipe implements PipeTransform {
  private differ: IterableDiffer<any>;

  constructor(iDiff: IterableDiffers) {
    this.differ = iDiff.find([]).create();
  }

  transform(value: any[]): any[] {
    if (this.differ.diff(value)) {
      return [...value];
    }
    return value;
  }
}
<cmp [items]="arrayInput | arrayChangeDetector"></cmp>

Pour les voyageurs de temps parmi nous qui rencontrent encore des problèmes de réseau, voici une reproduction du problème avec plusieurs solutions possibles.

https://stackblitz.com/edit/array-value-changes-not-detected-ang-8

Les solutions comprennent:

  • NgDoCheck
  • Utiliser un tuyau
  • Utilisation de JS immuable NPMgithub
1
Rheimus

C'est un travail pour moi:

@Component({
  selector: 'my-component',
  templateUrl: './my-component.component.html',
  styleUrls: ['./my-component.component.scss']
})
export class MyComponent implements DoCheck {

  @Input() changeArray: MyClassArray[]= [];
  private differ: IterableDiffers;

  constructor(private differs: IterableDiffers) {
    this.differ = differs;
  }

  ngDoCheck() {
    const changes = this.differ.find(this.insertedTasks);
    if (changes) {
      this.myMethodAfterChange();
  }
}
0
Stack Over