angular - एक्सप्रेशनसंचालित इसके बाद ।हैसबैनचेक किए गए विवरण को समझाया




angular2-changedetection angular2-databinding (15)

डिबगिंग टिप्स

यह त्रुटि काफी भ्रामक हो सकती है, और यह गलत होने के बारे में गलत धारणा बनाना आसान है। मुझे उपयुक्त स्थानों में प्रभावित घटकों में इस तरह के बहुत सारे डिबगिंग स्टेटमेंट जोड़ना मददगार लगता है। इससे प्रवाह को समझने में मदद मिलती है।

माता-पिता में इस तरह के बयान (सटीक स्ट्रिंग 'महत्वपूर्ण है' महत्वपूर्ण है), लेकिन इसके अलावा ये सिर्फ इस तरह हैं:

    console.log('EXPRESSIONCHANGED - HomePageComponent: constructor');
    console.log('EXPRESSIONCHANGED - HomePageComponent: setting config', newConfig);
    console.log('EXPRESSIONCHANGED - HomePageComponent: setting config ok');
    console.log('EXPRESSIONCHANGED - HomePageComponent: running detectchanges');

बच्चे / सेवाओं / टाइमर कॉलबैक में:

    console.log('EXPRESSIONCHANGED - ChildComponent: setting config');
    console.log('EXPRESSIONCHANGED - ChildComponent: setting config ok');

यदि आप detectChanges चलाते हैं detectChanges मैन्युअल रूप से detectChanges लिए भी लॉगिंग जोड़ें:

    console.log('EXPRESSIONCHANGED - ChildComponent: running detectchanges');
    this.cdr.detectChanges();

फिर Chrome डीबगर में 'EXPRESSIONCHANGES' द्वारा फ़िल्टर करें। यह आपको हर चीज के प्रवाह और क्रम को दिखाएगा, जो सेट हो जाता है, और यह भी कि कोणीय किस बिंदु पर त्रुटि फेंकता है।

आप विराम बिंदुओं को डालने के लिए ग्रे लिंक पर भी क्लिक कर सकते हैं।

यह देखने के लिए एक और बात कि क्या आपके पूरे आवेदन में इसी तरह के नाम हैं (जैसे कि style.background ) सुनिश्चित करें कि आप जो सोचते हैं, उसे डिबग कर रहे हैं - इसे अस्पष्ट रंग मान पर सेट करके।

कृपया मुझे समझाएं कि मुझे यह त्रुटि क्यों हो रही है: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked.

जाहिर है, मैं इसे केवल देव मोड में प्राप्त करता हूं, यह मेरे प्रोडक्शन बिल्ड पर नहीं होता है, लेकिन यह बहुत कष्टप्रद है और मैं बस अपने देव वातावरण में त्रुटि होने के लाभों को नहीं समझता हूं जो ठेस पर दिखाई नहीं देंगे - -क्योंकि मेरी समझ में कमी के कारण।

आमतौर पर, यह तय करना काफी आसान है, मैं इस तरह से सेटटाइमआउट में कोड के कारण त्रुटि को लपेटता हूं:

setTimeout(()=> {
    this.isLoading = true;
}, 0);

या इस तरह से एक कंस्ट्रक्टर के साथ परिवर्तनों का पता लगाने के लिए: constructor(private cd: ChangeDetectorRef) {} :

this.isLoading = true;
this.cd.detectChanges();

लेकिन मैं लगातार इस त्रुटि में क्यों भागूं? मैं इसे समझना चाहता हूं ताकि मैं भविष्य में इन हैकी सुधारों से बच सकूं।


@HostBinding इस त्रुटि का एक भ्रामक स्रोत हो सकता है।

उदाहरण के लिए, हम कहते हैं कि आपके पास घटक में निम्नलिखित मेजबान बंधन है

// image-carousel.component.ts
@HostBinding('style.background') 
style_groupBG: string;

सादगी के लिए, इस संपत्ति को निम्नलिखित इनपुट संपत्ति के माध्यम से अद्यतन किया गया है:

@Input('carouselConfig')
public set carouselConfig(carouselConfig: string) 
{
    this.style_groupBG = carouselConfig.bgColor;   
}

मूल घटक में आप इसे प्रोग्राम में ngAfterViewInit में सेट कर रहे हैं

@ViewChild(ImageCarousel) carousel: ImageCarousel;

ngAfterViewInit()
{
    this.carousel.carouselConfig = { bgColor: 'red' };
}

यहाँ क्या होता है:

  • आपका मूल घटक बनाया गया है
  • ImageCarousel घटक बनाया गया है, और carousel को सौंपा गया है (ViewChild के माध्यम से)
  • हम जब तक ngAfterViewInit() तक पहुंच नहीं सकते हैं (यह शून्य होगा)
  • हम कॉन्फ़िगरेशन असाइन करते हैं, जो style_groupBG = 'red' सेट करता है
  • यह बदले में background: red सेट करता background: red मेजबान ImageCarousel घटक पर background: red
  • यह घटक आपके मूल घटक का 'स्वामित्व' है, इसलिए जब यह परिवर्तनों की जाँच करता है तो यह carousel.style.background पर परिवर्तन पाता है और यह जानने के लिए पर्याप्त चतुर नहीं है कि यह कोई समस्या नहीं है इसलिए यह अपवाद फेंकता है।

इसका एक उपाय यह है कि आप एक अन्य रैपर डिवाइडर ImageCarousel को शुरू करें और उस पर पृष्ठभूमि का रंग सेट करें, लेकिन तब आपको HostBinding (जैसे कि माता-पिता को ऑब्जेक्ट की पूर्ण सीमा को नियंत्रित करने की अनुमति) का उपयोग करने के कुछ लाभ नहीं मिलते हैं।

पैरेंट घटक में बेहतर समाधान, विन्यास सेट करने के बाद डिटेचेंजेज () जोड़ना है।

ngAfterViewInit()
{
    this.carousel.carouselConfig = { ... };
    this.cdr.detectChanges();
}

यह इस तरह से स्पष्ट रूप से सेट किया हुआ लग सकता है, और अन्य उत्तरों के समान है, लेकिन एक सूक्ष्म अंतर है।

उस मामले पर विचार करें जहां आप @HostBinding विकास के दौरान बाद तक नहीं जोड़ते हैं। अचानक आपको यह त्रुटि मिलती है और इसका कोई मतलब नहीं है।


एक बार जब मैं कोणीय जीवनचक्र के हुक और परिवर्तन का पता लगाने के साथ उनके संबंध को समझ गया तो बहुत कुछ समझ में आया।

मैं एक तत्व के *ngIf के लिए बाध्य वैश्विक ध्वज को अद्यतन करने के लिए कोणीय प्राप्त करने की कोशिश कर रहा था, और मैं उस घटक को एक अन्य घटक के ngOnInit() जीवन चक्र हुक के अंदर बदलने की कोशिश कर रहा था।

प्रलेखन के अनुसार, इस विधि को एंगुलर द्वारा पहले ही परिवर्तनों का पता लगाने के बाद कहा जाता है:

पहले ngOnChanges () के बाद एक बार कॉल किया जाता है।

इसलिए ngOnChanges() अंदर ध्वज को अपडेट करने से परिवर्तन का पता नहीं चलेगा। फिर, एक बार परिवर्तन का पता लगाने के बाद स्वाभाविक रूप से फिर से शुरू हो गया है, ध्वज का मूल्य बदल गया है और त्रुटि को फेंक दिया गया है।

मेरे मामले में, मैंने इसे बदल दिया:

constructor(private globalEventsService: GlobalEventsService) {

}

ngOnInit() {
    this.globalEventsService.showCheckoutHeader = true;
}

इसके लिए:

constructor(private globalEventsService: GlobalEventsService) {
    this.globalEventsService.showCheckoutHeader = true;
}

ngOnInit() {

}

और इसने समस्या तय कर दी :)


कोणीय परिवर्तन का पता लगाता है और जब इसका पता चलता है कि कुछ घटक जो कि बच्चे के घटक को पास कर दिए गए हैं, तो बदल दिया गया है, कोणीय ने त्रुटि को फेंक दिया है।

इसे ठीक करने के लिए हम AfterContentChecked जीवन चक्र हुक और का उपयोग कर सकते हैं

import { ChangeDetectorRef, AfterContentChecked} from '@angular/core';

  constructor(
  private cdref: ChangeDetectorRef) { }

  ngAfterContentChecked() {

    this.cdref.detectChanges();

  }

दिलचस्प जवाब थे, लेकिन मुझे अपनी ज़रूरतों को पूरा करने के लिए कोई ऐसा नहीं मिला, जो सबसे नज़दीक हो @ chittrang-mishra जो केवल एक विशिष्ट फ़ंक्शन को संदर्भित करता है और मेरे ऐप में कई टॉगल नहीं।

मैं इसका उपयोग नहीं करना चाहता था [hidden] का लाभ लेने के लिए *ngIf भी डोम का हिस्सा नहीं है इसलिए मुझे निम्नलिखित समाधान मिला जो सभी के लिए सबसे अच्छा नहीं हो सकता है क्योंकि यह इसे सही करने के बजाय त्रुटि को दबाता है, लेकिन मेरे में मामला जहां मुझे पता है कि अंतिम परिणाम सही है, यह मेरे ऐप के लिए ठीक लगता है।

मैंने जो किया था उसे लागू किया गया था AfterViewChecked , constructor(private changeDetector : ChangeDetectorRef ) {} जोड़ें constructor(private changeDetector : ChangeDetectorRef ) {} और फिर

functionName() {   
    yourCode;  
    //add this line to get rid of the error  
    this.cdRef.detectChanges();     
}

मुझे उम्मीद है कि यह अन्य मदद करता है क्योंकि कई अन्य लोगों ने मेरी मदद की है।


नीचे दिए गए चरणों का पालन करें:

1. 'ChangeDetectorRef' का उपयोग @ एंगुलर / कोर से निम्नानुसार करके करें:

ngAfterViewChecked(){
  this.changeDetector.detectChanges();
}

2. इसे कंस्ट्रक्टर () में लागू करें:

import{ ChangeDetectorRef } from '@angular/core';

3. अपने फ़ंक्शन के लिए निम्न विधि जोड़ें, जिसे आप बटन के क्लिक जैसी घटना पर बुला रहे हैं। तो यह इस तरह दिखता है:

constructor(   private cdRef : ChangeDetectorRef  ) {}

मुझे उसी समस्या का सामना करना पड़ रहा था क्योंकि मेरे घटक में सरणी में से एक में मूल्य बदल रहा था। लेकिन मूल्य परिवर्तन पर परिवर्तनों का पता लगाने के बजाय, मैंने onPush घटक परिवर्तन का पता लगाने की रणनीति को बदल दिया (जो वस्तु परिवर्तन पर परिवर्तनों का पता लगाएगा और मूल्य परिवर्तन पर नहीं)।

import { Component, OnInit, ChangeDetectionStrategy } from '@angular/core';

@Component({
    changeDetection: ChangeDetectionStrategy.OnPush
    selector: -
    ......
})

मुझे यह त्रुटि मिली क्योंकि मैं घटक में एक चर का उपयोग कर रहा था। एक बार जब मैंने HTML में भाग हटा दिया, तो यह त्रुटि हो गई थी।



मेरे मामले में, मुझे अपने परीक्षण को चलाने के दौरान, मेरी कल्पना फ़ाइल में यह समस्या थी।

मुझे ngIf को बदलना पड़ा [hidden]

<app-loading *ngIf="isLoading"></app-loading>

सेवा मेरे

<app-loading [hidden]="!isLoading"></app-loading>

मेरे मुद्दे के लिए, मैं github पढ़ रहा था - "ExpressionChangedAfterItHasBeenCheckedError जब एक घटक 'नॉन मॉडल' का मूल्य afterViewInit में बदल रहा है" और ngModel को जोड़ने का फैसला किया

<input type="hidden" ngModel #clientName />

इसने मेरा मुद्दा ठीक कर दिया, मुझे आशा है कि यह किसी की मदद करेगा।


मैं सदस्यता क्षेत्र में एक कोड ब्लॉक कर रहा था, मैंने इसे ऊपरी दायरे में बदल दिया और यह ठीक काम करता है।


यहाँ क्या हो रहा है पर मेरे विचार हैं। मैंने दस्तावेज़ीकरण नहीं पढ़ा है लेकिन मुझे यकीन है कि यह त्रुटि क्यों दिखाई गई इसका एक हिस्सा है।

*ngIf="isProcessing()" 

* NgIf का उपयोग करते समय, यह हर बार स्थिति बदलने पर तत्व को जोड़कर या हटाकर डोम को बदल देता है। इसलिए यदि स्थिति को दृश्य में प्रस्तुत करने से पहले बदल जाता है (जो कि कोणीय की दुनिया में अत्यधिक संभव है), तो त्रुटि को फेंक दिया जाता है। विकास और उत्पादन मोड के बीच स्पष्टीकरण here देखें।

[hidden]="isProcessing()"

[छिपी] का उपयोग करते समय यह डोम को भौतिक रूप से नहीं बदलता है लेकिन तत्व को केवल दृश्य से छिपाता है, सबसे अधिक संभावना है कि पीठ में सीएसएस का उपयोग करना। DOM में तत्व अभी भी है लेकिन स्थिति के मूल्य के आधार पर दिखाई नहीं देता है। यही कारण है कि [छिपा] का उपयोग करते समय त्रुटि नहीं होगी।


लेख का जिक्र https://blog.angularindepth.com/everything-you-need-to-know-about-the-expressionchangedafterithasbeencheckederror-error-e3fd9ce7dbb4

तो परिवर्तन का पता लगाने के पीछे यांत्रिकी वास्तव में एक तरह से काम करता है कि परिवर्तन का पता लगाने और सत्यापन पचाने दोनों को सिंक्रोनाइज़ किया जाता है। इसका मतलब है, अगर हम संपत्तियों को अतुल्यकालिक रूप से अपडेट करते हैं तो सत्यापन लूप के चलने पर मानों को अपडेट नहीं किया जाएगा और हमें ExpressionChanged... नहीं किया जाएगा ExpressionChanged... त्रुटि। इस त्रुटि के कारण के सत्यापन की प्रक्रिया के दौरान, कोणीय विभिन्न मानों को देखता है और फिर परिवर्तन का पता लगाने के चरण के दौरान यह क्या दर्ज करता है। जिससे बचने के लिए…।

1) ChangeDetectorRef का उपयोग करें

2) setTimeOut का उपयोग करें। यह आपके कोड को दूसरे वीएम में मैक्रो-टास्क के रूप में निष्पादित करेगा। सत्यापन प्रक्रिया के दौरान कोणीय इन परिवर्तनों को नहीं देखेंगे और आपको वह त्रुटि नहीं मिलेगी।

 setTimeout(() => {
        this.isLoading = true;
    });

3) यदि आप वास्तव में उसी वीएम उपयोग पर अपने कोड को निष्पादित करना चाहते हैं जैसे

Promise.resolve(null).then(() => this.isLoading = true);

यह एक माइक्रो-टास्क बनाएगा। वर्तमान समकालिक कोड को निष्पादित करने के बाद माइक्रो-टास्क कतार को संसाधित किया जाता है, इसलिए संपत्ति का अद्यतन सत्यापन चरण के बाद होगा।


अद्यतन करें

मैं अत्यधिक ओपी की पहली प्रतिक्रिया के साथ शुरू करने की सलाह देता हूं: ठीक से सोचें कि constructor में क्या किया जा सकता है बनाम क्या ngOnChanges() में किया जाना चाहिए।

मूल

यह एक उत्तर की तुलना में अधिक ध्यान देने योग्य है, लेकिन यह किसी की मदद कर सकता है। मैं इस समस्या पर अड़ गया जब एक बटन की उपस्थिति को फॉर्म की स्थिति पर निर्भर करने की कोशिश कर रहा है:

<button *ngIf="form.pristine">Yo</button>

जहाँ तक मुझे पता है, यह सिंटैक्स बटन को शर्त के आधार पर DOM से जोड़ा और हटाया जाता है। जो बदले में ExpressionChangedAfterItHasBeenCheckedError ओर जाता है।

मेरे मामले में सुधार (हालांकि मैं अंतर के पूर्ण निहितार्थों को समझने का दावा नहीं करता), display: none का उपयोग करना था display: none इसके बजाय:

<button [style.display]="form.pristine ? 'inline' : 'none'">Yo</button>




angular2-databinding