9
votes

ngOnChanges ne se déclenche pas si un @Input reçoit un objet

Modèle de composant parent:

@Input() set param(value) {
  console.log(value);
}

Code du composant parent:

@Input() public param: any;

ngOnChanges() {
  console.log('Received input: ', param);
}

Code du composant enfant:

whatever = { value1: 0, value2: 1, value3: 'foo' };

Cela ne marche pas. Toute modification de whatever ce whatever ne sera pas ngOnChanges par le composant enfant et ngOnChanges ne se déclenchera pas.

Cela ne fonctionne pas non plus si j'essaie d'utiliser un setter:

<my-component [param]="whatever"></my-component>

Et cela n'aide pas si j'essaye d'exécuter une mise à jour manuelle de zone dans le composant parent.

Apparemment, @Input() ne peut détecter que lorsque la structure d'un objet a changé, mais pas ses valeurs.

Alors, comment puis-je passer un objet en tant que propriété @Input() et faire en sorte que le composant enfant détecte les changements de valeur?


2 commentaires

OnChanges prend un argument SimpleChanges . Vous seriez en mesure d'obtenir la nouvelle valeur à partir de là dans votre premier exemple plutôt qu'à partir de this .


Je vous remercie. Mais qu'en est-il du setter?


4 Réponses :


1
votes

Comme ça:

@Input() public param: any;
ngOnChanges(changes: SimpleChanges): void {
    // Do your check here
    console.log(changes.param.previousValue);
}

L'utilisation des modifications vous permet d'accéder à previousValue et currentValue.


1 commentaires

Merci, mais pour une raison qui me dépasse, notre avance interdit l'utilisation de ngChanges. C'est pourquoi j'essaye d'utiliser un setter.



12
votes

Le hook de cycle de vie OnChanges est déclenché lorsque la valeur de la propriété @Input change. Dans le cas d'un objet, cette valeur est la référence de l' objet . Si la référence d'objet ne change pas, OnChanges n'est pas déclenché.

Une technique possible pour forcer la détection de changement consiste à définir une nouvelle référence d'objet après avoir modifié les valeurs de propriété:

private _param: Object;

@Input() get param(): Object {
  return this._param;
} 
set param(value: Object) {
  console.log("setter", value);
  this._param = value;
}

Le ngOnChanges événements ngOnChanges peut ensuite être utilisé pour surveiller les modifications:

ngOnChanges(changes: SimpleChanges) {
  for (let propName in changes) {
    let chng = changes[propName];
    let cur = JSON.stringify(chng.currentValue);
    console.log(propName, cur);
  }
}

Comme alternative, si @Input décore une propriété getter / setter, les modifications peuvent être surveillées dans le setter:

this.whatever.value1 = 2;
this.whatever.value2 = 3;
this.whatever = Object.assign({}, this.whatever);

Voir ce stackblitz pour une démo.


1 commentaires

C'est peut-être une bonne idée d'ajouter une note expliquant pourquoi la création de nouveaux objets au lieu de modifier ses propriétés n'est pas un mauvais modèle.



2
votes

La détection de changement angulaire est déclenchée lorsque la valeur de la propriété @Input change.

Ainsi, pour déclencher la détection de changement dans le cas d'un objet, vous pouvez transmettre une copie de l'objet en utilisant l'opérateur de propagation comme entrée.

par exemple. someVar = {key: value} c'est la variable @Input() , alors passez comme

JSON.parse(JSON.stringify(obj))

{...VARIABLE} <- voici la magie

si l'opérateur de propagation ne fonctionne pas, utilisez des méthodes de copie profonde d'objet comme

<app-sample [someVar]="{...someVar}" ></app-sample>


5 commentaires

Pouvez-vous donner un exemple de la façon dont vous feriez cela.


Le compilateur angulaire renvoie l'erreur «jeton inattendu».


Cela ne fonctionne pas. Veuillez fournir une solution de travail ou une explication. J'ai la même question que @Jack ci-dessus.


Merci. C'est maintenant clair. Cependant, cela ne semble pas être une bonne solution car une copie d'objet serait créée à chaque actualisation - n'est-ce pas?


Dans mon cas cloneDeep, opérateur de propagation rien ne fonctionne état = {skip: 0, filtre: {op: 'asc', filters: [{field: 'demo', value: '123'}]}, prenez: 1} ceci est mon objet et quand je fais state.filter = <nouvel objet>; état = _.cloneDeep (état); onchange n'est pas déclenché



0
votes

J'ai trouvé une solution basée sur la réponse de Franklin:

créez simplement une méthode qui renvoie la copie de l'objet, comme

<my-component [param]="whateverCopy"></my-component>

et passer comme

whateverCopy() {
    return {...this.whatever}
}


1 commentaires

Oui, c'est la solution que je me suis trouvée.