Angular: EXCEPTION : l'expression a changé après avoir été vérifiée.

Créé le 18 déc. 2015  ·  147Commentaires  ·  Source: angular/angular

Commentaire le plus utile

Ce n'est pas un bug, c'est une fonctionnalité du mode dev qui fonctionne comme prévu. L'appel de enableProdMode( ) - voir le plunk mis à jour lors de l'amorçage de l'application empêche la levée de l'exception.

Cela dit, il est lancé pour une bonne raison : en bref, après chaque cycle de détection de changement, le mode dev effectue immédiatement un deuxième cycle pour vérifier qu'aucune liaison n'a changé depuis la fin du premier, car cela indiquerait que des changements sont en cours par la détection de changement elle-même.

Dans votre plunk, vous avez la liaison [attr.spinner]=isLoading , et isLoading change à la suite de votre appel à this.load() , ce qui se produit lorsque ngAfterViewInit est déclenché, ce qui se produit dans le cadre du tour de détection de changement initial. Cela en soi n'est pas problématique cependant - le problème est que this.load() modifie une liaison mais ne déclenche pas un nouveau cycle de détection de changement, ce qui signifie que cette liaison ne sera pas mise à jour avant un prochain cycle de détection de changement.

Tous les 147 commentaires

J'ai été confronté à un problème similaire et semble être dû à la façon dont un enfant est initialisé et à la façon dont un parent peut mettre à jour l'enfant dans le cadre de son processus d'initialisation. J'ai un exemple similaire avec une solution de contournement laide utilisant la zone : http://plnkr.co/edit/GI45u805pnFUtFeORnxn?p=preview , avec la solution de contournement ressemblant à la suivante :

constructeur(private _zone : NgZone) { }

ngAfterViewInit() : void {
this._zone.overrideOnTurnDone(() => {
this.child.title = 'titre du parent';
});
}

Ce n'est pas un bug, c'est une fonctionnalité du mode dev qui fonctionne comme prévu. L'appel de enableProdMode( ) - voir le plunk mis à jour lors de l'amorçage de l'application empêche la levée de l'exception.

Cela dit, il est lancé pour une bonne raison : en bref, après chaque cycle de détection de changement, le mode dev effectue immédiatement un deuxième cycle pour vérifier qu'aucune liaison n'a changé depuis la fin du premier, car cela indiquerait que des changements sont en cours par la détection de changement elle-même.

Dans votre plunk, vous avez la liaison [attr.spinner]=isLoading , et isLoading change à la suite de votre appel à this.load() , ce qui se produit lorsque ngAfterViewInit est déclenché, ce qui se produit dans le cadre du tour de détection de changement initial. Cela en soi n'est pas problématique cependant - le problème est que this.load() modifie une liaison mais ne déclenche pas un nouveau cycle de détection de changement, ce qui signifie que cette liaison ne sera pas mise à jour avant un prochain cycle de détection de changement.

J'ai besoin de configurer mon composant dans afterViewInit car j'ai ChildView qui est initialisé ici. Quelle est la bonne façon d'apporter ce changement à isLoading ? Enveloppez l'appel à this.load par setTimeout ?

Vous avez juste besoin de faire quelque chose, n'importe quoi, qui déclenche un autre cycle de détection de changement pendant cette méthode - émettre un événement, peu importe. L'envelopper dans un délai d'attente (flashback de la file d'attente vers ng1 :-P) est une façon de fonctionner, mais cela me semble très compliqué dans ng2.

Cela n'a pas l'air convivial. Je pense à cette méthode comme à l'endroit où je peux terminer l'initialisation de mon composant lorsque ses enfants sont définis et maintenant il s'avère qu'elle a des règles personnalisées. Je pense que vous devriez forcer en interne le nouveau cycle de détection de changement afin qu'il soit caché à l'intérieur de ng2 de l'utilisateur.

pourriez-vous poster un échantillon plus représentatif de ce que vous essayez d'accomplir ici ? si c'est votre spinner d'image, vous feriez mieux d'utiliser une reliure standard.

@ AerisG222 idem pour vous. y a-t-il une raison pour laquelle vous ne pouvez pas utiliser une simple liaison pour y parvenir ? le comportement de zone prioritaire n'est généralement pas recommandé.

@robwormald Oui, dans ce cas, je pourrais faire la liaison de propriété. Le cas réel que j'ai implique également une autre propriété qui est un peu plus compliquée - un tableau d'objets personnalisés, mais cela aussi pourrait certainement être défini via la liaison de propriété.

Je suppose que s'il n'y avait pas de @ViewChild , je m'en ficherais beaucoup, car ce serait le seul moyen raisonnable de transmettre l'information. Cependant, j'étais très excité de voir @ViewChild afin que je puisse travailler à partir de cela pour les références de composants dans le code. Cela simplifie le balisage et permet plus de contrôle via le code, ce qui est en outre précieux avec le typescript car il y a plus de support d'outils (comme intellisense et la vérification du temps de compilation). Cela simplifie également le composant conteneur car il n'a pas à suivre les variables membres qui sont uniquement destinées à être utilisées comme état pour l'enfant.

Un autre point mineur est que lorsque vous parcourez une application à l'aide de la syntaxe de liaison de propriété, vous devez inspecter le modèle pour voir qui consomme finalement les données. Ce serait plus évident lorsqu'il est câblé via le code.

J'ai nettoyé le code pour ne montrer que ce qui compte pour l'exemple. L'idée est d'afficher le spinner uniquement pendant le chargement de l'image, puis de supprimer le spinner et d'afficher l'image. L'image est chargée uniquement lorsque l'élément est visible à l'écran.

inflate.ts

import {Component, Input, Renderer, ElementRef, AfterViewInit, ViewChild} from 'angular2/core';

@Component({
  selector: '[inflate]',
  templateUrl: './inflate.html',
  styleUrls: ['./inflate.css']
})
export class Inflate implements AfterViewInit {
  @Input('inflate') url: string;
  @ViewChild('holder') holder: ElementRef;
  isLoaded = false;
  isLoading = false;

  constructor(private renderer: Renderer) {}

  ngAfterViewInit() {
    setTimeout(_=> this.inflate()); // BUGFIX: https://github.com/angular/angular/issues/6005#issuecomment-165911194
  }

  inflate() {
    let bounds = <ClientRect>this.holder.nativeElement.getBoundingClientRect();
    if(bounds.bottom > 0 && bounds.top < window.innerHeight) {
      this.isLoading = true;
      let img = new Image();
      img.src = this.url;
      img.onload = _=> {
        this.isLoaded = true;
        this.isLoading = false;
        this.renderer.setElementStyle(this.holder,
          'background-image', 'url("' + this.url + '")');
      };
    }
  }
}

inflate.html

<image-holder #holder></image-holder>
<spinner *ngIf="isLoading"></spinner>

Je dois conserver l'image non dans l'hôte car, même si elle s'estompe avec l'animation au chargement, elle ne devrait pas affecter l'opacité de l'arrière-plan de l'hôte.

J'ai un problème potentiellement lié avec une directive imbriquée.
http://plnkr.co/edit/myX2Alx9jie2q0FDIME7?p=preview
Ici, je me lie à ngStyle dans progressBar.ts.
J'enregistre l'objet styles avant de revenir pour prouver qu'ils sont égaux.
J'obtiens également l'ExpressionChangedAfterItHasBeenCheckedException .

@svi3c, vous retournez en fait un objet _différent_. Chaque appel de fonction renverra une nouvelle instance d'objet qui est "égale" à la précédente en termes de clés/valeurs mais pas en termes d'instances/références.

Il existe plusieurs manières de gérer cette situation :

  • mettre à jour les valeurs dans une instance d'objet, au lieu de renvoyer une nouvelle copie
  • en utilisant la stratégie ChangeDetectionStrategy.OnPush afin que votre fonction ne soit invoquée que lorsque l'une des entrées change, ex : http://plnkr.co/edit/NWyVoTtkiAzqWJphLMbi?p=preview

Vous voudrez peut-être vérifier https://github.com/ng-bootstrap/core/blob/master/src/progressbar/progressbar.ts comme un autre exemple d'une directive de barre de progression pour ng2 (en utilisant le HTML / CSS de bootstrap)

@pkozlowski-opensource Merci beaucoup !
Je n'ai lu que https://angular.io/docs/ts/latest/guide/template-syntax.html#!#ngstyle et je ne suis pas encore tombé sur des stratégies de détection de changement. C'est génial! :)

@drew-moore :

Vous avez juste besoin de faire quelque chose, n'importe quoi, qui déclenche un autre cycle de détection de changement pendant cette méthode - émettre un événement, peu importe.

Pouvez-vous nous dire quel est le moyen _correct_ et _simple_ de relancer la détection des modifications dans ngAfterViewInit - en supposant que tout ce qui est nécessaire est une propriété à mettre à jour. ApplicationRef.tick() ne fonctionne pas ici car cela provoque l'exception 'ApplicationRef.tick est appelé récursivement' ; émettre un événement juste pour déclencher une détection de changement me semble mal (et n'a pas fonctionné pour moi), tout comme setTimeout .

Sur la base des commentaires ici, il semble être une exigence assez courante de mettre à jour une propriété liée dans ngAfterViewInit (en raison d'une dépendance sur un composant enfant), donc Angular 2 semble manquer un moyen simple de le faire.

Si la mise à jour d'une propriété liée dans ngAfterViewInit est la _mauvaise_ chose à faire, quelle est l'alternative ?

En voici un rapide :

Défi:
Définissez la hauteur de l'élément dom (via une certaine formule) en fonction de sa largeur rendue.

Attente:
Lorsque l'élément get est rendu, obtenez la largeur et en fonction de celle-ci, calculez sa hauteur.

Réalité:
setTimeout ?!

  ngAfterViewInit() {
    window.setTimeout(() =>
      this.elementHeight = (this.nativeElement.offsetWidth * this.randomImage.dim.h) / this.randomImage.dim.w
    )
  }

@Birowsky Plunker ?

@zoechi Voici un exemple : http://plnkr.co/edit/MML5uHEFQdL0k0TA5HtY

Vous pouvez basculer le setTimeout dans app/app.component.ts#21 dans le crochet de cycle ngAfterViewInit vie

J'ai également rencontré quelque chose de similaire en essayant d'implémenter la méthode writeValue( newValue ) d'un ngModel valueAccessor . Lorsque j'essaie d'appliquer la newValue au composant, cela me dit que la valeur a changé (dans le mauvais sens). Comme vous l'avez tous trouvé, l'envelopper dans un setTimeout() le "répare" ; mais pas dans le bon sens.

J'ai un autre exemple . C'est un peu plus simple dans la mesure où un @HostBinding() est configuré et est renseigné par un QueryList .

Identique à mon problème #5950.

Quelques recommandations ici sur la façon de le faire proprement.
http://stackoverflow.com/questions/34827334/triggering-angular2-change-detection-manually

Cependant, je suis toujours en train d'envelopper setTimeout parce que je n'ai pu en obtenir aucun pour faire ce dont j'ai besoin. Il semble que je pourrais ChangeDetectorRef.detectChanges() à la fin d'un rappel et déclencher le CD.... Est-ce que j'ai raté quelque chose ? Cela aidera peut-être l'un d'entre vous.

J'utilise les extensions de source multimédia HTML5 pour créer un contexte audio basé sur un élément vidéo, donc l'utilisation de @ViewChild est requise. Sur la base du contexte audio, j'essaie d'afficher des informations, comme le nombre de canaux. Cependant, je suis également tombé sur ce problème. Quelle est la bonne façon de contourner cela?

@ElsewhereGames Difficile à dire sans voir du code réel. Pouvez-vous créer un Plunker

@zoechi Je n'ai pas de bogue réel à signaler, indiquant simplement que ne pas accéder directement au DOM, bien que souhaitable, n'est pas toujours quelque chose qui peut être évité (en réponse au commentaire de @robwormald ). Pour l'API actuelle, consultez la documentation sur createMediaElementSource .

J'ai posté en janvier avec un problème de _ngModel_. Maintenant, je suis de retour avec le problème _ViewChildren_. Cependant, contrairement à certains de ces autres exemples, je n'essaie pas de faire quoi que ce soit dans ma démo. Cela signifie que mes contrôleurs n'ont aucune logique - ce ne sont que des métadonnées et des liaisons de vue. En tant que tel, je ne sais pas où j'ai même une marge de manœuvre pour _gâcher les choses_ :

http://www.bennadel.com/blog/3040-i-have-a-fundamental-misunderstanding-of-change-detection-in-angular-2-beta-8.htm

Je rencontre un problème similaire mais pourrait réécrire le code pour le contourner. J'ai bien rigolé avec le post de @bennadel alors merci. Partagez une partie du sentiment avec les problèmes précédents que j'ai eus où la détection intelligente des changements n'était pas assez intelligente, trop intelligente ou trop intelligente pour moi :-)

https://github.com/angular/angular/issues/6618

Jusqu'à présent, il y avait toujours un moyen de le contourner, mais je le suggérerai à nouveau ici, à tout le moins, le mécanisme de détection des changements pourrait utiliser plus de documentation. @bennadel Votre problème me rappelle un autre problème,

https://github.com/angular/angular/issues/5177

où résoudre ce problème, il a été suggéré d'ajouter un hook de cycle de vie supplémentaire OnBeforeBinding/OnBeforeRendering. Il semble que cette solution résoudrait également votre problème.

@tandu remplace simplement ngAfterViewInit par ngOnInit

@tandu peu importe quand votre spinner démarrera (chargement de l'enfant, chargement de l'image dans l'enfant - pour l'utilisateur, tout est en train de se charger), n'a d'importance que lorsque vous devez l'éteindre. Vous pouvez le désactiver dans "ngAfterViewInit", en utilisant setTimeout (je suis d'accord que c'est moche), mais ce sera faux - le composant enfant devrait déclencher un événement une fois le chargement de l'image terminé, vers le composant parent. Le chargement du composant enfant et le chargement de l'image sont 2 événements différents.

@e-oz https://github.com/angular/angular/issues/6005#issuecomment -165951692 J'accède aux limites des enfants pour détecter si je dois lancer le chargement de l'image.

@tandu et vous pourriez déplacer toute cette vérification vers le code du composant enfant (ce serait légitime dans ngAfterViewInit du composant enfant) et envoyer l'événement au parent, quand changer isLoading.

@e-oz Je définis le composant comme #holder en html : <image-holder #holder></image-holder> . Il n'y a donc pas de fichier js pour cela.

alors créez-le, sinon il n'est pas nécessaire d'utiliser ngAfterViewInit, si View
n'a pas de composants réels en tant qu'enfants - rien ne sera rendu.

Le dimanche 13 mars 2016 à 15h18, tandu [email protected] a écrit :

@e-oz https://github.com/e-oz Je définis le composant comme #holder en html :

titulaire>

. Il n'y a donc pas de fichier js pour cela.

-
Répondez directement à cet e-mail ou consultez-le sur GitHub
https://github.com/angular/angular/issues/6005#issuecomment -195963389.

Je peux aussi bien utiliser element.getElementsByTagsName ou des méthodes similaires et ne pas utiliser ChildComponent. Il existe certainement des solutions de contournement. Je suis d' setTimeout avec le correctif

@tandu Je suis absolument d'accord avec vous là-dessus, c'est une limitation très ennuyeuse.

Pour ce que cela vaut, j'ai changé ma solution de contournement d'origine pour la suivante, qui commence à sembler être une approche raisonnable. Cela vaut peut-être la peine d'essayer pour votre situation :

    constructor(private _changeDetectionRef : ChangeDetectorRef) { }

    ngAfterViewInit() : void {
        // your code

        this._changeDetectionRef.detectChanges();
    }

J'obtiens la même exception lors de l'utilisation de la validation de formulaire (entrée requise). Lié à ce problème ?

Plunker : http://plnkr.co/edit/Fp6XT5mC6ZCB14Z1gvTi?p=preview

@eivindr a

 [ngFormControl]="formModel.find('title')" 

et il corrige votre erreur.

Je refuse de laisser tomber ce problème :) Hier, j'ai pu démontrer que mon problème était spécifiquement lié aux liaisons d'hôte qui dépendent des propriétés d'entrée :

http://www.bennadel.com/blog/3056-host-bindings-are-breaking-the-ngmodel-bridge-in-angular-2-beta-11.htm

Je dois croire qu'il s'agit d'un bogue à ce stade car le composant a deux choses qui dépendent de l'entrée - le modèle de vue et les liaisons d'hôte - et _un seul d'entre eux_ est à l'origine de l'exception de modification. Si les deux causaient le problème, je penserais à autre chose; mais, comme un seul d'entre eux est à l'origine du problème, il doit s'agir d'un bogue, non ?

@eivindr J'ai également le même problème lorsque je commence avec un formulaire vierge, puis que je

@MarkPerryBV Non, je reçois toujours "a changé après vérification" lors de l'utilisation requise. Idem lors de l'utilisation du validateur de modèle. Mais minLength etc. fonctionne.

@ user414 Si je supprime [ngFormControl]... il n'y a pas de validation "requise" sur l'entrée.

@MarkPerryBV J'ai trouvé une solution de contournement qui fonctionne pour moi (pour l'instant). Je devais activerProdMode(). J'utilise ngIf sur les entrées, j'ai donc également dû faire un ChangeDetectorRef.detectChanges() après le rendu de l'entrée (sinon, l'entrée était invalide malgré le contenu).

L'utilisation de enableProdMode() ne résout vraiment rien - elle masque simplement le symptôme en l'empêchant de faire la double vérification et en vous informant que quelque chose ne va probablement pas.

Le correctif d'AerisG222 semble mieux que d'utiliser un setTimeout . Est-il vraiment vrai que le comportement souhaité est de devoir déclencher manuellement la détection de changement après avoir appelé addControl() avec un validateur ?

Même problème ici après avoir appelé addControl(). Je pense que addControl() devrait appeler ChangeDetectionRef.detectChanges().

@CaptainCodeman Je sais - c'était une solution de contournement temporaire.

Cela n'échouera plus en mode dev lorsque j'utiliserai ng2@beta17 , [email protected] , zone. [email protected]. Je dois encore appeler ChangeDetectionRef.detectChanges()

@eivindr pouvez-vous mettre à jour votre plnkr vers le correctif beta17 ?
Je perds la tête sur celui-ci, je refuse de me soumettre à enableProdMode() .

@AerisG222 Votre approche fonctionne pour moi !

L'utilisation de l'approche publiée par @AerisG222 est-elle une bonne approche à utiliser ? Oui, cela fonctionne, mais existe-t-il des consciences de déclencher manuellement changeDetectionRef.detectChanges() ?

J'ai le même problème lorsque j'essaie de valider un formulaire dynamique, puis de réinitialiser le formulaire. voici mon plunk

Étapes à reproduire. Soumettez le formulaire avec Is Employee Checked. Une fois le formulaire actualisé, cochez à nouveau Is Employee.

J'utilise changeDetectionRef.detectChanges() dans la fonction de soumission.

merci pour ce @AerisG222 :)

Je n'aime pas toute cette solution de contournement setTimeout. Je propose ce qui suit :

http://plnkr.co/edit/9umnTGsdFWhbaOBeftuZ?p=preview

Utiliser ChangeDetectorRef.detectChanges n'est pas toujours une bonne approche. Exemple pourquoi :

Le composant parent appelle l'API publique des composants enfants pendant son ngAfterViewInit . Cet appel modifie la liaison de l'enfant. Appeler detectChanges du parent résoudrait également le problème, mais cela signifie que le parent doit savoir quand appeler detectChanges après quel appel API de l'enfant. Vous remarquerez que l'appel de detectChanges dans la méthode childs qui modifie la liaison dans ce cas ne fonctionne pas lorsque vous souhaitez utiliser ChangeDetectionStrategy.OnPush .

J'ai rencontré cette erreur lors de l'ajout dynamique d'enfants via ViewContainerRef.createComponent() , puis de la mise à jour de l'instance enfant. J'utilise RC2 (et j'essaie de ne pas utiliser de trucs dépréciés).

Au départ, tout fonctionnait bien (premier rendu), mais j'ai ensuite ajouté un composant qui émettrait de nouvelles données. Cet événement a été écouté par le composant parent, qui a mis à jour son tableau de données déclenchant la détection des modifications et le remplissage des nouveaux composants enfants dynamiques. L'événement a été déclenché par une soumission de formulaire. Si j'appuyais sur le bouton d'envoi, tout allait bien. Si j'appuie sur Entrée pour générer la soumission, cela produirait l'erreur.

J'ai fini par ajouter ChangeDetectorRef.detectChanges() immédiatement après la création et la mise à jour du composant dynamique. Ce n'est pas un gros problème, mais il semble étrange que même si je viens d'ajouter un nouveau composant à la vue, le système ne déclenche pas automatiquement la détection des changements pour l'enfant.

@ aerisg222 Dois-je appeler this._changeDetectionRef.detectChanges() Après chaque changement de propriété des enfants par rapport au parent?

Bon je viens de rencontrer ce problème. J'ai deux composants frères utilisant le même objet de données. Les deux objets sont supposés pouvoir mettre à jour la source de données. L'autre source voit réellement le changement dans l'objet mais déclenche que l'expression a changé après avoir été vérifiée. Valeur précédente : 'true'. Valeur actuelle : « faux »

Cela m'est arrivé après la mise à niveau de RC1 à RC4

Pareil ici. J'ai recommencé à obtenir cela après la mise à niveau de RC1 à RC4.

Peut-être que le changement qui a causé cela aurait dû être communiqué comme un changement de rupture dans le journal des modifications... Kuz, mes affaires sont cassées maintenant.

Il faut attendre la finale.
J'ai arrêté d'utiliser des composants enfants car la mise à jour des propriétés externes est nulle.
Je pensais que les zones étaient là pour ça...

Pareil ici. Le chargement dynamique des composants via ViewComponentRef semble se comporter différemment dans RC2 et RC4 comme dans RC1

Voici mes dépendances de travail :
"dépendances": {
"@angular/common": "2.0.0-rc.1",
"@angular/compiler": "2.0.0-rc.1",
"@angular/core": "2.0.0-rc.1",
"@angular/http": "2.0.0-rc.1",
"@angular/platform-browser": "2.0.0-rc.1",
"@angular/platform-browser-dynamic": "2.0.0-rc.1",
"systemjs": "0.19.31",
"core-js": "^2.4.0",
"reflect-metadata": "^0.1.3",
"rxjs": "5.0.0-beta.6",
"zone.js": "^0.6.12"
},
"devDependencies": {
"typescript": "^1.8.10",
"typings":"^1.0.4",
"en même temps": "^2.2.0",
"lite-server": "^2.2.2"
}

Et voici les dépendances qui produisent l'erreur mentionnée :
"dépendances": {
"@angular/common": "2.0.0-rc.4",
"@angular/compiler": "2.0.0-rc.4",
"@angular/core": "2.0.0-rc.4",
"@angular/http": "2.0.0-rc.4",
"@angular/platform-browser": "2.0.0-rc.4",
"@angular/platform-browser-dynamic": "2.0.0-rc.4",
"systemjs": "0.19.31",
"core-js": "^2.4.0",
"reflect-metadata": "^0.1.3",
"rxjs": "5.0.0-beta.6",
"zone.js": "^0.6.12"
},
"devDependencies": {
"typescript": "^1.8.10",
"typings":"^1.0.4",
"en même temps": "^2.2.0",
"lite-server": "^2.2.2"
}

RC2 ne fonctionne pas non plus !

Juste une question, mais y a-t-il un moyen d'obtenir plus d'informations sur l'erreur ? Par exemple, il serait bon de savoir ce qu'est "l'expression" lorsque l'erreur dit "l'expression passe de 'vrai' à 'faux' !.

J'ai également exactement le même problème après la mise à niveau de RC1 à RC4. Et ce que j'ai compris est dû à la fonction ngOnInit dans mon composant. Si je commente cette fonction, le message d'erreur disparaît et tout fonctionne correctement. Des pensées?

Clôturer ce problème car il est ancien et des mesures ont été prises. Si le problème persiste, assurez-vous de créer un nouveau problème et remplissez le modèle de problème avec tous les détails requis . Merci

mais les gens disaient qu'ils rencontraient toujours ce problème dans le nouveau rc ...

Le mer. 13 juil. 2016 à 00:52, Victor Berchet [email protected]
a écrit:

Fermé #6005 https://github.com/angular/angular/issues/6005.

-
Vous recevez ceci parce que vous avez été mentionné.
Répondez directement à cet e-mail, consultez-le sur GitHub
https://github.com/angular/angular/issues/6005#event -720794445, ou couper le son
le fil
https://github.com/notifications/unsubscribe/AAgIEFEVZmE0M53XxNpzAYg9hJT_E_viks5qVAyGgaJpZM4G4J_s
.

Oui, je viens de passer à RC4 et je n'ai apporté aucune modification à aucun de mes composants à part la configuration d'un nouveau routeur et j'ai rencontré ce problème. Tout allait bien en RC1, on dirait que c'est toujours là.

Plus d'informations:

Il s'avère qu'en raison de l'appel de async intérieur de ngOnInit , cette erreur se produit. Si je commente le code d'appel asynchrone ou si je déplace ce morceau de code vers le constructeur, tout fonctionne bien.

@ammar91
Appelez ceci après les modifications :
changeDetectionRef.detectChanges()

@ zigzag95 Oui, c'est une solution de contournement, mais cela semble très difficile à faire. Je m'attendrais à ce qu'un framework comme angular gère cela avec plus de grâce plutôt que de nous obliger à déclencher manuellement son propre mécanisme de détection des changements.

Dans mon cas, cela se produit lorsque je m'abonne à l'émetteur d'événements angulaires dans app.component.ts pour afficher et masquer la

Voici la reproduction, http://plnkr.co/edit/jEtfUrQdc4sJBySj5kWN?p=preview
Cliquez sur Heroes Link et sélectionnez l'un des héros à modifier et vous verrez l'erreur.
Veuillez voir les changements dans app.component.ts et hero.service.ts

Salut @vicb , merci pour la mise à jour. Pouvez-vous nous dire quelles actions ont été entreprises ? Voulez-vous dire que cela sera corrigé dans la prochaine version ?

Une autre solution alternative simple qui a fonctionné pour moi consiste à renvoyer la valeur comme observable et à retarder l'émission des valeurs de 10 ms. Dans mon cas, l'exception se produisait avec la valeur spinner
<app-spinner *ngIf="spinner"></app-spinner>

  this.eventService.toggleSpinnerAnnounced$.subscribe(
      (show: boolean) => this.spinner = show
    );

cela a fonctionné pour moi :

private spinner = new Subject<boolean>();
toggleSpinnerAnnounced$ = this.spinner.asObservable().delay(10); // This  delay() is important

announceSpinnerToggle(show: boolean) {
        this.spinner.next(show);
    }

Cela a fonctionné pour vous car vous utilisez le délai et ce n'est pas lié à l'observable.
Vous aurez le même comportement lors de l'utilisation de stettimeout.

@Jarvens s'il vous plaît arrêtez de publier ce commentaire sur des endroits inappropriés.

Les problèmes GitHub concernent les rapports de bogues et les demandes de fonctionnalités.
Pour les questions d'assistance, veuillez utiliser d'autres canaux comme ceux répertoriés dans CONTRIBUTION - Vous avez une question ou un problème ?

Votre commentaire ne fournit pas suffisamment d'informations pour diagnostiquer votre problème.

Pour info : j'ai eu cette erreur à cause de l'extension chrome ng-inspector for AngularJS (alternative d'extension angulaire 1 à batarang)

C'est fou ! Après avoir lu tout le fil, ce qui a résolu le problème en mode développement était la désactivation de ng-inspector pour AngularJS comme le dit @KarlGustav-unimicro.

Cela dit, ChangeDetectorRef.detectChanges() également fonctionné pour moi, mais je me sentais vraiment maladroit.

@pkozlowski-opensource,
je ne sais toujours pas pourquoi indicatorStyle() sera invoqué lorsque l'une des entrées change,
peux-tu donner plus d'explications ?
Si j'implémente ngDoCheck dans _progress-bar component_ sans définir ChangeDetectionStrategy.OnPush dans le composant parent,
j'ai découvert que ngDoCheck exécution deux fois,
je sais pourquoi le premier sera exécuté,
mais je suis vraiment confus au sujet du deuxième ngDoCheck ...

http://plnkr.co/edit/myX2Alx9jie2q0FDIME7?p=preview

@LiTiang ,

je ne sais toujours pas pourquoi indicateurStyle () sera invoqué lorsque l'une des entrées change,
peux-tu donner plus d'explications ?

Étant donné que indicatorStyle() est lié à [ngStyle] dans la vue, il sera toujours invoqué lors de l'exécution du détecteur de changement. Cela se produira, que les entrées aient réellement changé ou non.

j'ai découvert que l'exécution de ngDoCheck deux fois,
je sais pourquoi le premier sera exécuté,
mais je suis vraiment confus au sujet du deuxième ngDoCheck ...

C'est très probablement parce que, comme mentionné précédemment dans ce fil, la détection de changement s'exécute toujours deux fois en mode dev. Il le fait pour vérifier que nous n'avons pas de code qui modifie les liaisons lors de la détection des modifications, ce qui pourrait provoquer des bogues difficiles en production.

Une variation sur setTimeout()

MISE À JOUR : le processeur est devenu fou à 100 % sans compagnonHauteur de la dernière comparaison : les mises à jour de l'AFAIK devenaient récursives !

UPDATE2 : Exemple de Plunker

Je voulais que la hauteur d'un bloc <ul> dynamique contrôle la hauteur d'un <img> que j'ai lié à "companionHeight".

Tellement mal... Mais ce qui suit fonctionne à l' aide d'un Observable.

Voici l'élément <ul> dont la hauteur est surveillée :

<ul class="title-list-ul" #listul (window:resize)="setCompanionHeight(listul)">

Voici l'élément <img> contrôlé :

<img class="title-list-companion-img" [src]="getCompanionImageURL()" [height]="companionHeight"/>

Voici les méthodes des composants qui interagissent :

import { Component, OnInit, AfterViewChecked, ElementRef } from '@angular/core';  // redacted
import { Observable } from 'rxjs';

@Component({
  selector: 'resume-title-list',
  templateUrl: './title-list.component.html',
  styleUrls: ['./title-list.component.scss']
})
export class TitleListComponent implements OnInit, AfterViewChecked {

  private error: Object;
  private companionHeight: number = 0;
  private companionHeightLast: number = 0;

   // Lots of irrelevant stuff like ngInit redacted for simplicity.

  constructor(private elementRef: ElementRef) { }  // redacted, mine is more complicated.

  setCompanionHeight(listElement: any) {
    let clientRect = listElement.getBoundingClientRect();
    if (clientRect) {
      this.companionHeight = clientRect["height"];
    }
  }

  // window::resize only happens when the window is resized: This is an initialization 
  // function that sets the initial size of the image... Without it the image height is 
  // initially zero and the image is not shown. 
  // The height was not correct in ngAfterViewInit, so I used ngAfterViewChecked instead.

  ngAfterViewChecked() {
    let ularray = this.elementRef.nativeElement.getElementsByClassName("title-list-ul");
    if (ularray && ularray.length > 0) {
        let h = ularray[0].getBoundingClientRect().height;
        if (h && h != this.companionHeightLast) {
          this.companionHeightLast = h;
          Observable
          .of(h)
          .delay(10)
          .subscribe(h => this.companionHeight = h,
                     error => this.error = error);
        }
    }
  }
}

Les commentaires sur les problèmes fermés peuvent ne pas être lus par les membres de l'équipe Angular.
Que diriez-vous de poster une question dans l'un de ces canaux CONTRIBUANT - Vous avez une question ou un problème ? avec un Plunker pour se reproduire ?

J'ai eu le même problème avec :

<div *ngIf="isLoading">
   <app-some-viewchild></app-some-viewchild>
</div>

Utilisez [caché] à la place *ngIf résolvez le problème.

J'ai rencontré ce problème aussi. Enfin, j'ai trouvé que l'objet d'initialisation par défaut est le même que l'objet ngModle.

le réglage du changeDetection: ChangeDetectionStrategy.OnPush dans le décorateur @Component corrigé pour moi

@elvismercado merci, résout mon problème en 2.1.1 !

J'avais un composant enfant s'appuyant sur une valeur transmise par le modèle parent, le modèle enfant lançant cette exception lorsque la valeur parent a changé. L'ajout de votre suggestion au composant enfant empêche la détection des modifications de s'exécuter tant que la valeur modifiée n'est pas transmise au composant enfant, évitant ainsi que la détection ne s'exécute prématurément, je suppose?

Si j'ai une entrée qui a le focus et que je la supprime du DOM (condition ngIf), l'erreur "L'expression a changé après avoir été vérifiée" se produit (uniquement si l'entrée a un groupe de formulaires associé).
Voici un plunker avec le problème:
https://plnkr.co/edit/dooRvC1gY1WEcaNdshoP?p=preview
Merci!

Alors, quelle est la solution ici ?

La modification de n'importe quelle valeur d'entrée dans ngOnInit déclenchera cela.

@coli
il suffit d'envelopper

setTimeout(()=>{
 ///your code here
}, 1);

J'ai une combinaison de composants enfants qui doivent être préchargés lorsqu'un utilisateur souhaite modifier une entité existante.
J'ai lutté avec ce problème pendant des heures jusqu'à ce que je réussisse à le résoudre en utilisant @drewlio post ci-dessus.

Injectez _changeDetectionRef :

    constructor(
        private _changeDetectionRef: ChangeDetectorRef) {
    }

Et puis juste après avoir effectué votre changement, appelez detectchanges.
Cela a fait l'affaire pour moi.

    setSizeFromCtrl() {
        console.log("setSizeFromCtrl");
        if (this.sizeFromList && this.tradeProfile && this.tradeProfile.SizeFrom && this.sizeFromCtrl) {
            this.sizeFromCtrl.select(String(this.tradeProfile.SizeFrom));
            this._changeDetectionRef.detectChanges();
            console.log("setSizeFromCtrl DONE");
        }
    }

    setCentreTypeCtrl() {
        console.log("setCentreTypeCtrl");
        if (this.centreTypeList && this.tradeProfile && this.tradeProfile.centreTypeList && this.centreTypeCtrl) {

            for (let i of this.tradeProfile.centreTypeList) {
                this.centreTypeCtrl.select(i.value);
            }
            this._changeDetectionRef.detectChanges();
            console.log("centreTypeCtrl DONE");
        }
    }


Notez que j'ai eu ce même problème mais une cause différente (similaire ?) et des solutions différentes.

  • Notre application Angular - https://github.com/GeoscienceAustralia/GNSS-Site-Manager , a des formes réactives modèles qui sont imbriquées et construites sur plusieurs composants dans ngOnInit() . Certains de ces formulaires sont réduits avec un ngIf dans le modèle
  • Les données sont également lues dans un ngOnInit() et ont été appliquées avec un theModelForm.patchValue(data)
  • Cette erreur apparaissait lorsque l'un des formulaires réduits a été développé. C'est lié au fait que le DOM n'a pas été construit car le formulaire a été créé dans le ngOnInit() qui ne s'est déclenché qu'une fois étendu. Sortez les patchValue() et le problème a disparu
  • Nous avons trouvé deux solutions
  • Avant le patchValue() exécutez un this._changeDetectionRef.detectChanges(); . Et changez le ngIf en [hidden] afin que le formulaire et le DOM soient entièrement construits. L'inconvénient de ceci est le temps et le coût de mémoire lors de la création du formulaire complet et du DOM associé (cela peut ne pas être un problème dans de nombreuses applications, mais c'était pour nous car nous avons un formulaire gigantesque).
  • La meilleure solution consiste à appliquer le patchValue() (ou setValue() si les données et les champs sont 1:1) dans les composants et ne se produit donc que lorsque le formulaire est créé en ngOnInit() .

Je n'ai pas encore validé ces modifications (22/3/17).

Je ne pense donc pas que cette décision sauvera tout le monde.
Mais chez moi l'apparition de l'erreur donnée s'accompagnait de la création d'un nouvel élément de la classe. Ensuite, ajoutez-le au tableau, qui est généré via * ngFor.
J'ai résolu ce problème en ajoutant un constructeur à ma classe, qui a rempli les champs nécessaires à l'affichage dans le template des valeurs par défaut (zéros, lignes vides)

export class Task {
    task_id: number;
....
    constructor() {
        this.task_name = '';
        this.task_desc = '';
        this.task_percent = 0;
  addNewTask():void{
    let newTask = new Task();
    this.tasksList.push(newTask);
  }

task_name & task_desc & task_percent que j'affiche dans le modèle. Si au moins un des champs répertoriés n'est pas initialisé dans le constructeur, j'obtiens une erreur similaire.

+1

Travail de la solution AerisG222

constructor(private _changeDetectionRef : ChangeDetectorRef) { }

ngAfterViewInit() : void {
    // your code

    this._changeDetectionRef.detectChanges();
}

this._changeDetectionRef.detectChanges() fonctionne, mais cela entraînera d'autres problèmes dans mon cas. Je viens d'envelopper le code dans la fonction setTimeout(), il le résout.

@GuofuHuang faire cela est un moyen d'éviter le problème de ne pas le résoudre, ce qui peut parfois conduire à des cycles de détection de changement infinis

@Toxicable Désolé, je n'ai probablement pas été clair, je n'utilise pas this._changeDetectionRef.detectChanges(); Au lieu de cela, j'enveloppe mon code source dans setTimeout().

@GuofuHuang si tout ce que vous mettez dans le setTimeout provoque un CD et que vous l'exécutez sur un hook de cycle de vie, il est possible que vous provoquiez des cycles de CD infinis. La solution n'est pas d'apporter des modifications qui causent le CD lors d'une exécution de CD

@Toxicable Même si je le mets à 0 ?

@GuofuHuang si le réglage à 0 provoque un CD alors oui

@Toxicable Je viens de trouver ce lien, http://stackoverflow.com/questions/779379/why-is-settimeoutfn-0-sometimes-useful , il dit que setTimeout de 0 est quelque chose comme pause/yield en C, je ne ' Je ne sais pas si cela provoquera un CD, mais maintenant cela fonctionne parfaitement dans le mien. Si ce n'est pas cette solution, en avez-vous une meilleure pour résoudre ce problème ?

@GuofuHuang Je ne sais pas ce que fait la pause/le rendement en C mais en JS, le rappel sera placé à la fin de la file d'attente des événements. Ma solution est comme ci-dessus : la solution n'est pas d'apporter des modifications qui causent le CD lors d'une exécution de CD

settimeout est la solution
Vous n'avez pas besoin de spécifier le paramètre ms et j'attendrai une coche

@zigzag95 @GuofuHuang voir cet exemple pourquoi setTimeout n'est pas la solution
https://plnkr.co/edit/dv8K9EvVQLG59Gxsl3oI?p=preview

@Toxicable Merci, mais mon cas est très différent de celui-ci et je ne vois aucune boucle infinie. Mais c'est certainement un bon exemple qui m'aide à mieux comprendre l'angle.

@GuofuHuang En effet cela n'arrivera pas dans votre cas car vous le faites dans ngAfterViewInit qui n'est appelé qu'une seule fois. Mon point était qu'il est possible de provoquer une boucle infinie avec cette méthode, il est donc dangereux à utiliser. C'est ce que montre le plunkr.

@moutono Vos astuces m'ont aidé ! Super merci

Comme le problème concerne le déclenchement d'une détection de changement, la résolution d'une promesse peut également fonctionner car les correctifs de singe ngZone promettent également. Celui-ci a fonctionné pour moi :

ngAfterViewInit() {
  Promise.resolve().then(() => your_code_here);
}

Oui, setTimeout fonctionne, mais je n'ai pas aimé ce correctif, dans mon cas, cela était dû au fait que je modifiais la valeur d'un EventEmitter
La solution était le changement
private generalSubscriber: EventEmitter<any> = new EventEmitter<any>();
à
private generalSubscriber: EventEmitter<any> = new EventEmitter<any>(true);

bizarre non ?
Eh bien, cela signifie que l'émetteur d'événements est asynchrone
https://angular.io/docs/ts/latest/api/core/index/EventEmitter-class.html

Y a-t-il une solution à cela? Pourquoi est-il fermé ?

Je dois vider le cache à chaque fois

dans mon cas, je remplaçais un stub d'image lors du chargement de l'image, je devais le mettre dans un setTimeout

@intergleam @ wasa4587 @ caroso1222 @tandu @ @ @robwormald @ svi3c @ pkozlowski-opensource @pcroc @zoechi @stefankoru @bennadel @bryanforbes @Necroskillz @drewlio @ElsewhereGames @zoechi @ user414 @ e-oz

Il fait face à une erreur lors du développement. Sur le serveur de production fonctionne bien.

@smartHasmukh c'est une erreur en mode développement uniquement, donc pas de surprise.
Vous devriez quand même le réparer.

Si cette erreur se produit, vous interrompez très probablement le flux de données dans une direction. Ce qui pourrait être quelque chose comme changer les données d'un composant dans le frère, changer les données du parent dans les crochets du cycle de vie des enfants peut-être quelque chose de plus.
Cette exception levée n'est pas une erreur, mais l'aide au développeur pour comprendre que le contrat de flux de données unidirectionnel est rompu d'une manière ou d'une autre. Le fait que cela fonctionne en production est accidentel, vous devez revoir la façon dont vous modifiez vos données dans la communication parent-enfants et le corriger en mode développement.

@smartHasmukh J'avais aussi l'habitude de trouver cela inconfortable. Par exemple, je me connecte à des événements de navigation et j'émets en outre un événement vers le composant principal pour afficher un chargeur. Et dans chaque composant, je cache ensuite ce chargeur, après avoir reçu les données du serveur (donc, dans le cadre d'un abonnement). C'est très bien. Mais, dans un composant, je n'ai obtenu que quelques données d'une source "statique", aucune observable là-bas, j'ai donc simplement essayé d'utiliser ngOnInit pour dire au composant principal de masquer le chargeur. Ensuite, j'ai eu l'erreur, bien sûr.

Même si mon cas devrait être une chose simple, ce n'est pas la bonne façon de le faire. J'ai dû soit prendre la décision de ne pas afficher le chargeur en premier lieu, pour ce composant (ce qui pourrait devenir plus délicat si j'avais plus de composants comme ça)... Ou, également gérer le masquage du chargeur de mon service de données , plutôt qu'au niveau des composants. Ou, une solution laide, serait d'envelopper ce cas spécifique dans un setTimeout(). Parce que l'approche initiale n'était pas la bonne.

TL/DR :
Je suis sûr qu'il doit également y avoir un moyen approprié pour votre scénario, autre qu'un correctif _setTimeout()_ laid (que j'ai _malheureusement_ choisi à ce moment-là, mais encore une fois, c'est un scénario très simple et je sais ce qui est se passe là-bas - pourtant, je devrai peut-être le changer à un moment donné, dans une future version).
Vous pouvez essayer StackOverflow pour votre cas spécifique, donnez plus de détails. Il doit y avoir un moyen.

Bien sûr, merci @MrCroft @tytskyi et je le ferai M. @zoechi et je sais que ce n'est pas une surprise. mais je fais juste savoir. rien d'autre

@tytskyi

Le fait que cela fonctionne en production est par accident, vous devriez revoir

Non, dans la plupart des cas, ma réponse à cette erreur est "oui, je sais, et c'est voulu". De nombreux composants de mes applications chargent quelque chose après l'initialisation et après avoir reçu de nouvelles valeurs dans les variables liées. Ce problème n'est que "la laideur connue" d'Angular et personne ne s'en soucie.

@e-oz si vous affectez d'une manière ou d'une autre l'état du parent ou du frère dans l'un des crochets du cycle de vie, cela rompt le flux de données à sens unique. Vous devez effectuer ces modifications lorsque l'état de l'application est stabilisé, mais pas au milieu de la détection des modifications. C'est pourquoi il existe ces astuces avec microtask, setTimeout 0, etc.

@tytskyi calmez- vous avec votre "devrait",

@e-oz comment est-ce lié à une requête HTTP. Une réponse de requête HTTP provoque une détection de changement et vous n'obtiendrez pas l'erreur "L'expression a changé ...".
Pouvez-vous s'il vous plaît fournir plus d'informations?

@zoechi il n'y a aucune raison de perdre du temps dessus, personne ne changera rien, la tâche est fermée.

@e-oz le problème a été clos il y a un an et l'équipe Angular avait des priorités assez différentes à l'époque (sortir une version) et ce problème n'était pas un bloqueur à l'époque.
Si vous pensez avoir un cas valide, vous devez créer un nouveau problème.

@zoechi Je pense que non, tu décideras de ce que je dois faire. Je peux créer un nouveau problème, mais je n'y suis pas obligé. En tout respect, je n'aime pas ce ton "tu devrais".

@e-oz Je vois, vos commentaires ne servent qu'à évacuer votre frustration et vous n'êtes pas intéressé à trouver une solution. S'il vous plaît, arrêtez de faire perdre du temps aux autres avec vos commentaires inutiles.

si vous appelez http, abonnez-vous à votre composant, ajoutez simplement ..
Promise.resolve().then(() => your_code_here);
ou
myObservable.delay(10).subscribe(...)

Une des deux solutions résoudra votre problème

@vicb Arrêtez de fermer les problèmes qui ne sont pas résolus. Pourquoi fais-tu ça ?

Suggérez au moins une solution correcte, mais la fermeture est inacceptable

@diegogarciaa ... et qu'est-ce qui n'est pas corrigé ou qui ne va pas de votre point de vue ? La conversation ^^^ est si longue qu'il est difficile de trouver le noyau réel.

@mlc-mlapis avec tout le respect que je vous dois, je ne suis pas votre professeur, si vous ne pouvez pas lire ou comprendre le problème, arrêtez de vous embêter. J'ai passé de longues minutes à lire tout le contenu, je recommande le même.

L'attitude de l'équipe angulaire (pas toute l'équipe, mais qui sont ici en train de clore ce problème) semble si entreprenante: ne pas bloquer (mon salaire), ne pas le réparer de sitôt.

@diegogarciaa ... très bien. Continuez comme ci-dessus et vous serez les bienvenus partout.

@mlc-mlapis Je ne veux pas être le bienvenu là où les gens ne peuvent pas lire et essaient de faire des conneries là où ils ne devraient pas.

@diegogarciaa ... Je pensais juste que vous aviez personnellement un problème et il semble que ce n'est pas vrai maintenant. Et la réalité est aussi que la plupart des problèmes avec Expression has changed after it was checked ... ne sont pas des bugs mais juste une mauvaise compréhension du fonctionnement du CD.

@mlc-mlapis ok... comprenons

http://plnkr.co/edit/nm8OkrpZCIp4cvA6TbpO?p=preview

Dans ce plunk, je peux comprendre que la variable a été liée et vérifiée lorsque ngAfterViewInit a été appelé, donc lorsqu'elle reçoit une nouvelle valeur et ne déclenche pas de nouvelle vérification, les exceptions sont levées

Mais par exemple, si j'ai une référence de conteneur de vue... et j'utilise par exemple :

this.service.GetData().subscribe(
réponse => this.component.instance.dataModel = réponse
);

Considérez que dataModel est utilisé comme {{ dataModel.Field }} dans mon html

this.component est un composant dynamique étant chargé lors de certains événements, comment pourrait-on éviter l'erreur ? Quelle serait la bonne façon de transmettre les données à mon composant avant les crochets du cycle de vie de la détection des changements

@diegogarciaa ... oui, c'est un exemple typique où les opérations async et synchro sont mélangées et se rencontrent dans un CD et sa confirmation en mode dev. Je ne répéterai pas l'explication du post https://hackernoon.com/everything-you-need-to-know-about-the-expressionchangedafterithasbeencheckederror-error-e3fd9ce7dbb4 , alors essayez peut-être de le lire en premier.

Et regardez aussi sur https://hackernoon.com/here-is-how-to-get-viewcontainerref-before-viewchild-query-is-evaluated-f649e51315fb.

Donc, l'article dit essentiellement que je dois appeler manuellement un cycle de détection de changement...

Soyons honnêtes, je suis très contrarié par l'état actuel... même les petites choses angulaires sont si ennuyeuses et fatigantes... Je ne peux même pas respirer. Va te faire foutre

@diegogarciaa ... non, c'est juste une solution auxiliaire. Vous pouvez également utiliser promise.then ou utiliser setTimeout ... mais la solution recommandée dans ce cas est probablement un service partagé donc vous ne mettrez pas à jour directement la propriété du composant dynamique traverser le cycle du CD et son confirmation.

Ou créez simplement une instance du composant dynamique au moment où vous aurez les données. Ainsi, votre abonnement invoquera la création de l'instance de composant dynamique.

Mais ce qui est important -> la logique principale pour laquelle l'erreur se produit -> parce que les changements unidirectionnels doivent toujours circuler de haut en bas à travers l'arbre entier et après un cycle de CD, il y a une confirmation du statut que le principe unidirectionnel a été respecté.

Ce " va te faire foutre " n'était pas personnel... juste pour évacuer mes frustrations.

Nous utilisons un schéma de directive pour gérer nos composants. Nous avons construit un assistant qui charge un composant dans une référence de vue de composant, et comme nous le comprenons, les données doivent être disponibles lorsqu'un composant est en cours de chargement

protected loadPartialView(viewName: string, target: ViewContainerRef) : ComponentRef<any> {
        const factories = Array.from(this.resolver['_factories'].keys());
        const factoryClass = <Type<any>>factories.find((x: any) => x.name === viewName);
        const factory = this.resolver.resolveComponentFactory(factoryClass);
        return target.createComponent(factory);
}

Dans l'exemple ci-dessus : je voudrais faire quelque chose comme ceci :

return target.createComponent(factory, dataSource);

Où ma dataSource serait disponible au moment du constructeur

Comme redux, qui utilise un objet pour gérer la source de données passant par les composants, nous envisageons d'implémenter un composant injectable qui gère les données pour nous, afin que nous puissions l'obtenir avant que les crochets du cycle de vie.

diegogarciaa

Je ne pense pas que les données doivent être disponibles avant ... un composant dynamique est toujours un composant avec @Input() et @Output() mais la question est de savoir quand modifier les données.

... les données doivent être disponibles lors du chargement d'un composant ...

Je suppose que vous devez également utiliser des composants de modules chargés paresseux, j'utilise donc une carte directement dans un module pour avoir la possibilité d'accéder aux composants en utilisant uniquement des noms de chaîne.

export default class LazyLoadedModule {
    cmps: Map<{}, {}> = new Map();
    constructor() {
        this.cmps.set('first-component', FirstComponent);
        ...
    }
}

Que signifie return target.createComponent(factory, dataSource); ? Parce que l'API réelle est createComponent(componentFactory, index, injector, projectableNodes, ngModule) .

Et peut-être que le nouveau module @angular/cdk serait intéressant pour vous. Juste une introduction ici : https://medium.com/@caroso1222/a -first-look-into-the-angular-cdk-67e68807ed9b.

Pour ce que ça vaut et si ça peut aider quelqu'un, au lieu d'utiliser setTimeout et comme utiliser ChangeDetectorRef.detectChanges() n'a pas fonctionné pour moi, j'ai fini par utiliser NgZone.onStable et exécuter mon code en vous inscrivant une fois sur le EventEmitter ...

constructor(
  private zone: NgZone,
) { }

ngAfterViewInit() {
  this.zone.onStable.first().subscribe(() => {
    // your code here
  });
}

Je ne suis pas pleinement conscient des conséquences de cela, mais cela peut-il être pire que d'utiliser setTimeout ? Si quelqu'un a un commentaire à ce sujet, il serait vraiment le bienvenu !

La solution

constructor(private _changeDetectionRef : ChangeDetectorRef) { }

ngAfterViewChecked() : void {
    // your code
    this._changeDetectionRef.detectChanges();
}

voici le composant html :

{{ lastData.note[i].date | date : 'jj.MM.aaaa'}} | {{ lastData.note[i].date | date : 'HH : mm' }}:#BFL15_200817 | {{ lastData.status.name }} par {{lastData.note[i].admin}}

J'ai l'erreur si les données du tableau sont plusieurs, une solution ?

C'est certainement un problème complexe, mais dans certains cas (en particulier lorsque vous modifiez des propriétés sur un ContentChild), il peut être résolu en déplaçant la logique vers ngAfterContentInit() au lieu de ngAfterViewInit() ou ngOnInit()

C'est incroyable à quel point des choses simples peuvent causer tant de confusion dans les angles

Ne vous inquiétez pas, tout le monde l'a remarqué aussi 😕
image

@jakeNiemiec boulot boiteux, troll, plus personne ne cherche Angularjs
https://trends.google.com/trends/explore?date=all&q=Angular%20tutorial ,React%20tutorial
image

Oui, je m'attendrais à ce que plus de gens aient besoin de google "tutoriel angulaire" en désespoir de cause, d'où ce commentaire initial sur la "confusion".

Mais ne vous inquiétez pas, nous pouvons vérifier npm pour les statistiques d'utilisation exactes : http://www.npmtrends.com/react-vs-@angular/cli -vs-vue-vs-@angular/core
image
image

Il suffit de comparer le nombre de problèmes. J'aimerais vraiment voir angular s'améliorer, mais je soupçonne que Vue.js le passera dans un avenir lointain. Ayant programmé dans les 3, je recommande fortement Vue aux développeurs angulaires. C'est comme angulaire sans le passe-partout.

ok réponse acceptée, je suis parfaitement d'accord avec ces statistiques.
J'imagine qu'il y a plus de personnes utilisant PHP en Inde que Java dans le monde entier. L'un serait idiot d'utiliser PHP pour une application d'entreprise à grande échelle, vous devriez donc utiliser votre propre cerveau parfois, la vie sera meilleure

@jakeNiemiec ... 50% des problèmes créés sont placés là par erreur ... ce sont en fait des problèmes de support/connaissance et ne devraient pas être là du tout. Le plus amusant est que la plupart des sujets requis sont très bien documentés dans les documents officiels + les réponses sont basées sur la connaissance de JavaScript. Mais vous savez, les gens ne lisent pas assez la doc.

@HostBinding peut être une source déroutante de cette erreur.

Considérons une simple liaison sur un composant, telle que @HostBinding('style.background') .

Cette propriété est vérifiée par la détection de changement du composant *parent * comme s'il la possédait. De notre point de vue, nous voulons le considérer comme le composant enfant « possédant » cette propriété.

Il semble donc qu'il devrait y avoir un moyen pour un composant d'exclure les propriétés à vérifier dans ce cas ? Ou est-ce plus compliqué que ça ?

Je devais faire exécuter à mon composant parent detectChanges (ou ajouter un div supplémentaire dans l'enfant) pour éviter l'erreur dans le cas où le composant enfant changeait simplement sa propre couleur d'arrière-plan (et ne le faisait jamais ailleurs) .

Description plus complète : https://stackoverflow.com/questions/43375532/expressionchangedafterithasbeencheckederror-explained/51662105#51662105

Vous avez juste besoin de faire quelque chose, n'importe quoi, qui déclenche un autre cycle de détection de changement pendant cette méthode - émettre un événement, peu importe. L'envelopper dans un délai d'attente (flashback de la file d'attente vers ng1 :-P) est une façon de fonctionner, mais cela me semble très compliqué dans ng2.

En angulaire 4, ça marche 4 moi <3

Ce problème a été automatiquement verrouillé en raison de l'inactivité.
Veuillez déposer un nouveau problème si vous rencontrez un problème similaire ou connexe.

En savoir plus sur notre politique de verrouillage automatique des conversations .

_Cette action a été effectuée automatiquement par un bot._

Cette page vous a été utile?
0 / 5 - 0 notes