Veuillez m'expliquer pourquoi je continue à recevoir cette erreur: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked.
Évidemment, je ne l'obtiens qu'en mode dev, cela ne se produit pas sur ma version de production, mais c'est très ennuyeux et je ne comprends tout simplement pas les avantages d'avoir une erreur dans mon environnement de développement qui n'apparaîtra pas sur prod - -probablement à cause de mon manque de compréhension.
Habituellement, le correctif est assez facile, j'encapsule simplement l'erreur provoquant le code dans un setTimeout comme ceci:
setTimeout(()=> {
this.isLoading = true;
}, 0);
Ou forcez la détection des modifications avec un constructeur comme celui-ci constructor(private cd: ChangeDetectorRef) {}
:
this.isLoading = true;
this.cd.detectChanges();
Mais pourquoi est-ce que je rencontre constamment cette erreur? Je veux le comprendre afin que je puisse éviter ces corrections hacky à l'avenir.
la source
Réponses:
J'ai eu un problème similaire. En regardant la documentation des hooks du cycle de vie , j'ai changé
ngAfterViewInit
pourngAfterContentInit
et cela a fonctionné.la source
setTimeout
comme vous le montre ci-dessus a fait l'affaire. Merci!ngAfterContentChecked
fonctionne ici toutngAfterContentInit
en lançant toujours une erreur.Cette erreur indique un problème réel dans votre application, il est donc judicieux de lever une exception.
Dans la
devMode
détection de changement, un tour supplémentaire est ajouté après chaque exécution régulière de détection de changement pour vérifier si le modèle a changé.Si le modèle a changé entre le tour de détection de changement régulier et le changement supplémentaire, cela indique que
qui sont tous les deux mauvais, car il n'est pas clair comment procéder car le modèle pourrait ne jamais se stabiliser.
Si les exécutions angulaires changent de détection jusqu'à ce que le modèle se stabilise, il peut s'exécuter indéfiniment. Si Angular n'exécute pas la détection des modifications, la vue peut ne pas refléter l'état actuel du modèle.
Voir également Quelle est la différence entre le mode de production et le mode de développement dans Angular2?
la source
ngOnInit
oungOnChanges
pour modifier le modèle (certains rappels de cycle de vie permettent de modifier le modèle, d'autres non, je ne me souviens pas exactement de ce que l'on fait ou ne fait pas). Ne vous liez pas aux méthodes ou aux fonctions de la vue, mais liez-les aux champs et mettez à jour les champs dans les gestionnaires d'événements. Si vous devez vous lier à des méthodes, assurez-vous qu'elles renvoient toujours la même instance de valeur tant qu'il n'y a pas eu réellement de changement. La détection des changements appellera beaucoup ces méthodes.changeRef.detectChanges()
soit une solution / supprime l'erreur en est la preuve. C'est comme modifier l'état à l'intérieur$scope.$watch()
d'Angular 1.cdRef.detectChanges()
n'est généralement nécessaire que dans certains cas étranges et vous devez regarder attentivement lorsque vous en avez besoin et comprendre pourquoi.Beaucoup de compréhension est venue une fois que j'ai compris les crochets de cycle de vie angulaire et leur relation avec la détection des changements.
J'essayais d'obtenir Angular pour mettre à jour un indicateur global lié à l'
*ngIf
élément, et j'essayais de changer cet indicateur à l'intérieur dungOnInit()
crochet de cycle de vie d'un autre composant.Selon la documentation, cette méthode est appelée après que Angular a déjà détecté des modifications:
La mise à jour du drapeau à l'intérieur de
ngOnChanges()
n'initiera donc pas la détection des modifications. Ensuite, une fois que la détection des modifications s'est à nouveau naturellement déclenchée, la valeur de l'indicateur a changé et l'erreur est levée.Dans mon cas, j'ai changé cela:
Pour ça:
et cela a résolu le problème :)
la source
router.navigate
) lors du chargement vers un fragment s'il était présent dans l'URL. Ce code a été initialement placéAfterViewInit
là où je recevais l'erreur, puis j'ai déménagé comme vous le dites au constructeur mais il ne respectait pas le fragment. Déplacement versngOnInit
résolu :) merci!setInterval()
fonctionne également s'il doit se déclencher après un autre code d'événement à vie.Mettre à jour
Je recommande fortement de commencer par l'auto-réponse du PO : réfléchissez correctement à ce qui peut être fait dans le
constructor
vs ce qui devrait être fait dansngOnChanges()
.Original
C'est plus une note que une réponse, mais cela pourrait aider quelqu'un. Je suis tombé sur ce problème en essayant de faire dépendre la présence d'un bouton de l'état du formulaire:
Pour autant que je sache, cette syntaxe entraîne l'ajout et la suppression du bouton du DOM en fonction de la condition. Qui à son tour conduit à la
ExpressionChangedAfterItHasBeenCheckedError
.La solution dans mon cas (bien que je ne prétende pas saisir toutes les implications de la différence), était d'utiliser à la
display: none
place:la source
[hidden]
place de la[style.display]
partie très verbeuse . :)[hidden]
n'aura pas toujours le même comportement quedisplay: none
Il y avait des réponses intéressantes mais je ne semblais pas en trouver une qui correspondait à mes besoins, la plus proche étant de @ chittrang-mishra qui ne fait référence qu'à une fonction spécifique et non à plusieurs bascules comme dans mon application.
Je ne voulais pas utiliser
[hidden]
pour profiter de*ngIf
ne même pas faire partie du DOM, j'ai donc trouvé la solution suivante qui n'est peut-être pas la meilleure pour tous car elle supprime l'erreur au lieu de la corriger, mais dans mon cas où je connais le le résultat final est correct, cela semble correct pour mon application.Ce que j'ai fait, c'est mettre en œuvre
AfterViewChecked
, ajouterconstructor(private changeDetector : ChangeDetectorRef ) {}
, puisJ'espère que cela aide les autres comme beaucoup d'autres m'ont aidé.
la source
Angular exécute la détection de changement et lorsqu'il détecte que certaines valeurs qui ont été transmises au composant enfant ont été modifiées, Angular renvoie l'erreur:
ExpressionChangedAfterItHasBeenCheckedError
cliquez pour en savoir plusPour corriger cela, nous pouvons utiliser le
AfterContentChecked
crochet de cycle de vie etla source
Dans mon cas, j'ai eu ce problème dans mon fichier de spécifications, lors de l'exécution de mes tests.
Je devais changer
ngIf
pour[hidden]
à
la source
[hidden]
: speakdotnet.com/dont-use-hidden-attribute-angularjs-2*ngIf
change le DOM, en ajoutant et en supprimant l'élément de la page, tout en[hidden]
changeant la visibilité de l'élément sans le supprimer du DOM.Suivez les étapes ci-dessous:
1. Utilisez «ChangeDetectorRef» en l'important de @ angular / core comme suit:
2. Implémentez-le dans constructor () comme suit:
3. Ajoutez la méthode suivante à votre fonction que vous appelez lors d'un événement comme un clic sur le bouton. Cela ressemble donc à ceci:
la source
J'utilisais ng2-carouselamos (Angular 8 & Bootstrap 4)
Ci-dessous résolu mon problème:
Ce que j'ai fait:
la source
J'étais confronté au même problème que la valeur changeait dans l'un des tableaux de mon composant. Mais au lieu de détecter les changements lors du changement de valeur, j'ai changé la stratégie de détection de changement de composant en
onPush
(qui détectera les changements lors du changement d'objet et non lors du changement de valeur).la source
Se référant à l'article https://blog.angularindepth.com/everything-you-need-to-know-about-the-expressionchangedafterithasbeencheckederror-error-e3fd9ce7dbb4
Ainsi, la mécanique derrière la détection des modifications fonctionne en fait de manière à ce que les analyses de détection des modifications et de vérification soient effectuées de manière synchrone. Cela signifie que si nous mettons à jour les propriétés de manière asynchrone, les valeurs ne seront pas mises à jour lorsque la boucle de vérification est en cours d'exécution et nous n'obtiendrons pas d'
ExpressionChanged...
erreur. La raison pour laquelle nous obtenons cette erreur est que, pendant le processus de vérification, Angular voit différentes valeurs, puis ce qu'il a enregistré pendant la phase de détection de changement. Donc, pour éviter cela ...1) Utilisez changeDetectorRef
2) utilisez setTimeOut. Cela exécutera votre code dans une autre machine virtuelle en tant que macro-tâche. Angular ne verra pas ces changements pendant le processus de vérification et vous n'obtiendrez pas cette erreur.
3) Si vous voulez vraiment exécuter votre code sur la même machine virtuelle, utilisez
Cela créera une micro-tâche. La file d'attente des microtâches est traitée une fois que le code synchrone en cours a terminé son exécution, la mise à jour de la propriété aura donc lieu après l'étape de vérification.
la source
@HostBinding
peut être une source déroutante de cette erreur.Par exemple, supposons que vous ayez la liaison hôte suivante dans un composant
Pour simplifier, supposons que cette propriété est mise à jour via la propriété d'entrée suivante:
Dans le composant parent, vous le définissez par programme dans
ngAfterViewInit
Voici ce qui se passe:
carousel
(via ViewChild)carousel
jusqu'àngAfterViewInit()
(ce sera nul)style_groupBG = 'red'
background: red
le composant ImageCarousel hôtecarousel.style.background
et n'est pas assez intelligent pour savoir que ce n'est pas un problème, donc il lève l'exception.Une solution consiste à introduire un autre diviseur de wrapper ImageCarousel et à définir la couleur d'arrière-plan, mais vous n'obtenez pas certains des avantages de l'utilisation
HostBinding
(comme permettre au parent de contrôler les limites complètes de l'objet).La meilleure solution, dans le composant parent, consiste à ajouter detectChanges () après avoir défini la configuration.
Cela peut sembler assez évident comme ceci, et très similaire à d'autres réponses, mais il y a une différence subtile.
Considérez le cas où vous n'ajoutez
@HostBinding
que plus tard au cours du développement. Soudain, vous obtenez cette erreur et cela ne semble pas avoir de sens.la source
Voici mes réflexions sur ce qui se passe. Je n'ai pas lu la documentation mais je suis sûr que cela fait partie des raisons pour lesquelles l'erreur est affichée.
Lorsque vous utilisez * ngIf, il modifie physiquement le DOM en ajoutant ou supprimant l'élément à chaque fois que la condition change. Donc, si la condition change avant d'être rendue à la vue (ce qui est hautement possible dans le monde d'Angular), l'erreur est levée. Voir l'explication ici entre les modes de développement et de production.
Lors de son utilisation,
[hidden]
il ne modifie pas physiquement le,DOM
mais le cache simplementelement
à la vue, probablementCSS
à l'arrière. L'élément est toujours là dans le DOM mais n'est pas visible selon la valeur de la condition. C'est pourquoi l'erreur ne se produira pas lors de l'utilisation[hidden]
.la source
isProcessing()
fait la chose ame, vous devez utiliser!isProcessing()
pour le[hidden]
hidden
ne "utilise pas CSS à l'arrière", c'est une propriété HTML standard. developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/…Pour mon problème, je lisais github - "ExpressionChangedAfterItHasBeenCheckedError lors de la modification de la valeur" non modèle "d'un composant dans afterViewInit" et j'ai décidé d'ajouter le ngModel
Cela a résolu mon problème, j'espère que cela aide quelqu'un.
la source
ngModel
. Et pourriez-vous expliquer pourquoi cela devrait être utile?Conseils de débogage
Cette erreur peut être assez déroutante, et il est facile de faire une hypothèse erronée sur exactement quand elle se produit. Je trouve utile d'ajouter de nombreuses instructions de débogage comme celle-ci dans les composants concernés aux endroits appropriés. Cela aide à comprendre le flux.
Dans le parent, mettez des instructions comme celle-ci (la chaîne exacte 'EXPRESSIONCHANGED' est importante), mais à part cela, ce ne sont que des exemples:
Dans les rappels enfant / services / timer:
Si vous exécutez
detectChanges
manuellement ajouter la journalisation pour cela aussi:Ensuite, dans le débogueur Chrome, filtrez simplement par "EXPRESSIONCHANGES". Cela vous montrera exactement le flux et l'ordre de tout ce qui est réglé, et aussi exactement à quel point Angular jette l'erreur.
Vous pouvez également cliquer sur les liens gris pour insérer des points d'arrêt.
Une autre chose à surveiller si vous avez des propriétés de nom similaire dans votre application (comme
style.background
) assurez-vous de déboguer celle que vous pensez - en la définissant sur une valeur de couleur obscure.la source
Dans mon cas, j'avais une propriété asynchrone
LoadingService
avec un BehavioralSubjectisLoading
L'utilisation du modèle [caché] fonctionne, mais * ngIf échoue
la source
Une solution qui a fonctionné pour moi en utilisant rxjs
la source
ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked.
lorsque des changements de valeur sont déclenchés lorsque le DOM changedelay
disparaître l'erreur. Cela fonctionne de manière similaire àsetTimeout
.J'ai eu ce genre d'erreur dans Ionic3 (qui utilise Angular 4 dans le cadre de sa pile technologique).
Pour moi, cela faisait ceci:
<ion-icon [name]="getFavIconName()"></ion-icon>
J'essayais donc de changer conditionnellement le type d'une icône ionique de a
pin
à aremove-circle
, par un mode sur lequel un écran fonctionnait.Je suppose que je vais devoir ajouter un à la
*ngIf
place.la source
Mon problème était manifeste lorsque j'ai ajouté
*ngIf
mais ce n'était pas la cause. L'erreur a été provoquée en modifiant le modèle dans les{{}}
balises, puis en essayant d'afficher le modèle modifié dans l'*ngIf
instruction plus tard. Voici un exemple:Pour résoudre le problème, j'ai changé l'endroit où j'ai appelé
changeMyModelValue()
vers un endroit qui avait plus de sens.Dans ma situation, je voulais être
changeMyModelValue()
appelé chaque fois qu'un composant enfant modifiait les données. Cela nécessitait que je crée et émette un événement dans le composant enfant pour que le parent puisse le gérer (en appelantchangeMyModelValue()
. Voir https://angular.io/guide/component-interaction#parent-listens-for-child-eventla source
J'espère que cela aide quelqu'un à venir ici: nous faisons des appels de service
ngOnInit
de la manière suivante et utilisons une variabledisplayMain
pour contrôler le montage des éléments sur le DOM.component.ts
et component.html
la source
J'ai eu cette erreur car j'utilisais une variable dans component.html qui n'était pas déclarée dans component.ts. Une fois que j'ai supprimé la pièce en HTML, cette erreur a disparu.
la source
J'ai eu cette erreur car j'envoyais des actions redux en modal et le modal n'était pas ouvert à ce moment-là. Je distribuais des actions au moment où le composant modal recevait l'entrée. J'ai donc mis setTimeout là pour m'assurer que le modal est ouvert et que les actions sont déduites.
la source
À toute personne aux prises avec cela. Voici un moyen de déboguer correctement cette erreur: https://blog.angular-university.io/angular-debugging/
Dans mon cas, en effet, je me suis débarrassé de cette erreur en utilisant ce hack [caché] au lieu de * ngIf ...
Mais le lien que j'ai fourni m'a permis de trouver LA COUPABLE * ngIf :)
Prendre plaisir.
la source
hidden
au lieu dengIf
n'est pas un hack, ni ne résout le problème du tout. Vous masquez simplement le problème.La solution ... les services et les émetteurs d'événements rxjs ... et la liaison de propriété utilisent tous deux rxjs..vous êtes mieux de l'implémenter vous-même, plus de contrôle, plus facile à déboguer. N'oubliez pas que les émetteurs d'événements utilisent rxjs. Simplement, créez un service et dans un observable, faites abonner chaque composant à l'observateur et passez une nouvelle valeur ou une valeur de cosume selon les besoins
la source