जाँच के बाद अभिव्यक्ति ___ बदल गई है


286

इस सरल प्लंक में घटक क्यों है

@Component({
  selector: 'my-app',
  template: `<div>I'm {{message}} </div>`,
})
export class App {
  message:string = 'loading :(';

  ngAfterViewInit() {
    this.updateMessage();
  }

  updateMessage(){
    this.message = 'all done loading :)'
  }
}

फेंक:

अपवाद: App @ 0: 5 में 'मैं {{मैसेज}} एक्सप्रेशन कर रहा हूँ, इसे चेक करने के बाद बदल गया है। पिछला मान: 'मैं लोड कर रहा हूँ :(': वर्तमान मूल्य: 'मैं सब लोड कर रहा हूँ :)' [मैं {{संदेश}} ऐप @ 0: 5] में हूँ

जब सब मैं कर रहा हूँ एक सरल बाध्यकारी अद्यतन कर रहा है जब मेरा दृष्टिकोण शुरू किया जाता है?



अपने को संशोधित करने पर विचार ChangeDetectionStrategyका उपयोग करते समय detectChanges() stackoverflow.com/questions/39787038/...
लुइस Limas

बस इस बारे में सोचें कि एक इनपुट नियंत्रण है और आप इसे एक विधि में डेटा को पॉप्युलेट कर रहे हैं और उसी पद्धति में आप इसके लिए कुछ मूल्य प्रदान कर रहे हैं। संकलक नए / पिछले मूल्य के साथ सुनिश्चित करने के लिए भ्रमित हो जाएगा। इसलिए बाध्यकारी और आबादी अलग-अलग तरीकों से होनी चाहिए।
मैक

जवाबों:


293

जैसा कि ड्रूमोर द्वारा कहा गया है, इस मामले में उचित समाधान वर्तमान घटक के लिए परिवर्तन का पता लगाने को मैन्युअल रूप से ट्रिगर करना है। यह की detectChanges()विधि का उपयोग करके किया जाता हैChangeDetectorRef ऑब्जेक्ट (से आयातित angular2/core), या इसकी markForCheck()विधि , जो किसी भी मूल घटक को अपडेट करता है। प्रासंगिक उदाहरण :

import { Component, ChangeDetectorRef, AfterViewInit } from 'angular2/core'

@Component({
  selector: 'my-app',
  template: `<div>I'm {{message}} </div>`,
})
export class App implements AfterViewInit {
  message: string = 'loading :(';

  constructor(private cdr: ChangeDetectorRef) {}

  ngAfterViewInit() {
    this.message = 'all done loading :)'
    this.cdr.detectChanges();
  }

}

यहाँ भी Plunkers प्रदर्शन कर रहे हैं ngOnInit , setTimeout केवल मामले में और enableProdMode दृष्टिकोणों का ।


7
मेरे मामले में मैं एक मोडल खोल रहा था। मोडल खोलने के बाद यह संदेश दिखा रहा था "एक्सप्रेशन ___ बदल गया है इसे जांचने के बाद", इसलिए मेरा समाधान जोड़ा गया था। LCDr.detectChanges (); मेरे मोडल को खोलने के बाद। धन्यवाद!
जॉर्ज कैसरियोगो

सीडीआर संपत्ति के लिए आपकी घोषणा कहां है? मैं घोषणा के cdr : anyतहत एक लाइन जैसी या कुछ और देखने की उम्मीद करूंगा message। बस चिंतित मैं कुछ याद किया है?
कोडकबी

1
@CodeCabbie यह कंस्ट्रक्टर के तर्कों में है।
मैला

1
यह संकल्प मेरी समस्या को ठीक करे! आपका बहुत बहुत धन्यवाद! बहुत स्पष्ट और सरल तरीका।
lwozniak

3
इसने मेरी मदद की - कुछ संशोधनों के साथ। मुझे झूठे तत्वों को स्टाइल करना पड़ा जो एनजीफ़ोर लूप के माध्यम से उत्पन्न हुए थे। मुझे इनटेरटेक् ट के आधार पर सूची आइटम के भीतर स्पैन का रंग बदलने की जरूरत थी, 'सॉर्ट' पर क्लिक करने से जो एक बूल को सॉर्ट पाइप के पैरामीटर के रूप में इस्तेमाल किया जा रहा था (सॉर्ट किए गए परिणाम डेटा की एक प्रति थी, इसलिए शैलियों को नहीं मिल रहा था अकेले ngStyle के साथ अद्यतन)। - 'AfterViewInit' का उपयोग करने के बजाय, मैंने 'AfterViewChecked' का उपयोग किया - मैंने AfterViewChecked को आयात और कार्यान्वित करना भी सुनिश्चित किया । नोट: पाइप को 'शुद्ध: झूठे' पर सेट करने से काम नहीं चल रहा था, मुझे यह अतिरिक्त कदम जोड़ना पड़ा:
च्लोए कोरिगन

170

पहले, ध्यान दें कि यह अपवाद केवल तब फेंका जाएगा जब आप अपना ऐप देव मोड में चला रहे हों (जो कि डिफ़ॉल्ट रूप से बीटा -0 के रूप में है): यदि आप enableProdMode()ऐप को बूटस्ट्रैप करते समय कॉल करते हैं, तो यह फेंका नहीं जाएगा ( देखें अद्यतन प्लंक )।

दूसरा, ऐसा न करें क्योंकि यह अपवाद अच्छे कारण के लिए फेंका जा रहा है: संक्षेप में, जब देव मोड में, परिवर्तन का पता लगाने के हर दौर के तुरंत बाद एक दूसरा दौर होता है, जो सत्यापित करता है कि पहले के अंत से कोई बाइंडिंग नहीं बदली है, जैसा कि यह इंगित करेगा कि परिवर्तन का पता लगाने के कारण ही परिवर्तन हो रहे हैं।

आपके प्लंक में, {{message}}आपके कॉल द्वारा बाइंडिंग को बदल दिया जाता है setMessage(), जो ngAfterViewInitहुक में होता है , जो प्रारंभिक परिवर्तन का पता लगाने वाले मोड़ के एक हिस्से के रूप में होता है। हालांकि यह अपने आप में समस्याग्रस्त नहीं है - समस्या यह है कि यह setMessage()परिवर्तन को बदल देता है लेकिन परिवर्तन का पता लगाने के एक नए दौर को ट्रिगर नहीं करता है, जिसका अर्थ है कि इस परिवर्तन का पता तब तक नहीं चलेगा जब तक कि भविष्य के परिवर्तन का पता लगाने का दौर कहीं और शुरू न हो जाए।

टेकअवे: कोई भी चीज़ जो किसी परिवर्तन को बदल देती है उसे परिवर्तन का पता लगाने के एक दौर को शुरू करने की आवश्यकता होती है।

उदाहरण के लिए सभी अनुरोधों के जवाब में अपडेट करें कि कैसे करें : @ टिको का समाधान काम करता है, जैसा कि उत्तर में तीन तरीके @ मर्कराजोक ने बताया है। लेकिन स्पष्ट रूप से, वे सभी मेरे लिए बदसूरत और गलत महसूस करते हैं, जैसे हैक की तरह हमें एनजी 1 पर झुकाव करने की आदत है।

यह सुनिश्चित करने के लिए कि कभी-कभार ऐसी परिस्थितियाँ होती हैं जहाँ ये हैक उपयुक्त होते हैं, लेकिन यदि आप इनका उपयोग बहुत अधिक सामयिक आधार पर कर रहे हैं, तो यह एक संकेत है कि आप इसकी प्रतिक्रियाशील प्रकृति को पूरी तरह से अपनाने के बजाय रूपरेखा से लड़ रहे हैं।

IMHO, एक और अधिक मुहावरेदार, "एंगुलर 2 तरीका" जो इस के निकट है: ( प्लंक ) की तर्ज पर कुछ

@Component({
  selector: 'my-app',
  template: `<div>I'm {{message | async}} </div>`
})
export class App {
  message:Subject<string> = new BehaviorSubject('loading :(');

  ngAfterViewInit() {
    this.message.next('all done loading :)')
  }
}

13
सेटमैसेज () परिवर्तन का पता लगाने के एक नए दौर को क्यों सेट नहीं करता है? मुझे लगा कि जब आप UI में किसी चीज़ का मूल्य बदलते हैं तो Angular 2 ने स्वचालित रूप से परिवर्तन का पता लगाया।
डैनी लिबिन

4
@drewmoore "जो कुछ भी करता है एक बाध्यकारी परिवर्तन को परिवर्तन का एक दौर ट्रिगर करने की आवश्यकता होती है जब वह करता है"। कैसे? क्या यह एक अच्छा अभ्यास है? क्या सब कुछ एक ही रन में पूरा नहीं होना चाहिए?
डैनियल बिरोव्स्की पोपस्की

2
@Tycho, वास्तव में। जब से मैंने वह टिप्पणी लिखी है, मैंने एक और प्रश्न का उत्तर दिया है जहां मैं परिवर्तन का पता लगाने के लिए 3 तरीके बताता हूं , जिसमें शामिल हैं detectChanges()
मार्क रजक

4
बस ध्यान दें कि, वर्तमान प्रश्न निकाय में, आह्वान विधि का नाम है updateMessage, न किsetMessage
सुपरजोस

3
@Daynil, जब तक मैं इस प्रश्न के तहत टिप्पणी में दिए गए ब्लॉग को नहीं पढ़ता, तब तक मेरे मन में एक ही भावना थी: blog.angularindepth.com/… यह बताता है कि मैन्युअल रूप से ऐसा करने की आवश्यकता क्यों है। इस मामले में, कोणीय परिवर्तन का पता एक जीवनचक्र है। यदि कोई चीज़ इन जीवन चक्रों के बीच एक मूल्य बदल रही है, तो एक परिवर्तनकारी परिवर्तन का पता लगाने की जरूरत है (या एक निपटारा - जो अगले घटना लूप में निष्पादित होता है, फिर से परिवर्तन का पता लगाने को ट्रिगर करता है)।
महेश

50

ngAfterViewChecked() मेरे लिए काम किया:

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

constructor(private cdr: ChangeDetectorRef) { }
ngAfterViewChecked(){
   //your code to update the model
   this.cdr.detectChanges();
}

जैसा कि उल्लेख किया गया है, कोणीय परिवर्तन का पता लगाने का चक्र जो दो चरण का है, बच्चे के दृष्टिकोण को संशोधित करने के बाद परिवर्तनों का पता लगाना है, और मुझे सबसे अच्छा तरीका लगता है कि कोणीय द्वारा प्रदान किए गए जीवनचक्र हुक का उपयोग कर रहा है, और मैन्युअल रूप से कोणीय पूछ रहा है परिवर्तन का पता लगाएं और इसे बांधें। मेरी व्यक्तिगत राय है कि यह एक उचित उत्तर है।
जोये 587

1
मेरे लिए यह काम, एक साथ अंदर पदानुक्रम गतिशील घटक लोड करने के लिए।
मेहदी डस्टनी

47

मैं कोणीय कोर से ChangeDetectionStrategy जोड़कर इसे ठीक किया।

import {  Component, ChangeDetectionStrategy } from '@angular/core';
@Component({
  changeDetection: ChangeDetectionStrategy.OnPush,
  selector: 'page1',
  templateUrl: 'page1.html',
})

इसने मेरे लिए काम किया। मुझे आश्चर्य है कि इसके बीच अंतर क्या है और ChangeDetectorRef
Ari

1
हम्म ... फिर परिवर्तन डिटेक्टर का मोड शुरू में CheckOnce( प्रलेखन ) के लिए निर्धारित किया जाएगा
एलेक्स क्लॉस

हां, त्रुटि / चेतावनी समाप्त हो गई है। लेकिन यह 5-7 सेकंड के अंतर से पहले की तुलना में बहुत अधिक लोडिंग समय ले रहा है जो बहुत बड़ा है।
कपिल रघुवंशी

1
@KapilRaghuwanshi this.cdr.detectChanges();आप जो भी लोड करने की कोशिश कर रहे हैं उसके बाद चलाएँ । क्योंकि यह हो सकता है क्योंकि परिवर्तन का पता लगाने के लिए ट्रिगर नहीं है
हर्षल कारपेंटर

36

क्या आप उपयोग नहीं कर सकते ngOnInitक्योंकि आप केवल सदस्य चर को बदल रहे हैं message?

यदि आप किसी चाइल्ड कंपोनेंट के संदर्भ को एक्सेस करना चाहते हैं @ViewChild(ChildComponent), तो आपको वास्तव में इसके साथ प्रतीक्षा करने की आवश्यकता है ngAfterViewInit

एक गंदा फिक्स updateMessage()उदाहरण के लिए अगले इवेंट लूप में कॉल करना है जैसे कि सेटटाइमआउट।

ngAfterViewInit() {
  setTimeout(() => {
    this.updateMessage();
  }, 1);
}

4
मेरे कोड को ngOnInit मे बदलकर मेरे लिए काम किया।
11

29

इसके लिए मैंने उपरोक्त उत्तरों की कोशिश की है कि कई कोणीय के नवीनतम संस्करण में काम नहीं करते हैं (6 या बाद में)

मैं उस सामग्री नियंत्रण का उपयोग कर रहा हूं जिसे पहले बाइंडिंग के बाद परिवर्तन की आवश्यकता होती है।

    export class AbcClass implements OnInit, AfterContentChecked{
        constructor(private ref: ChangeDetectorRef) {}
        ngOnInit(){
            // your tasks
        }
        ngAfterContentChecked() {
            this.ref.detectChanges();
        }
    }

मेरे जवाब को जोड़ते हुए, यह कुछ विशेष समस्या को हल करने में मदद करता है।


यह वास्तव में मेरे मामले के लिए काम करता है, लेकिन आपके पास एक टाइपो है, AfterContentChecked को लागू करने के बाद आपको ngAfterContentChecked कॉल किया जाना चाहिए, न कि ngAfterViewInit।
टॉमस लुकाक

मैं वर्तमान में संस्करण 8.2.0 :)
टॉमस

1
@ TomášLukáč उत्तर के लिए धन्यवाद, मैंने अपना उत्तर (y)
MarmiK

1
मैं इस जवाब की सलाह देता हूं अगर ऊपर से कोई भी काम नहीं करता है।
अमीर चौबानी

प्रत्येक बार DOM को पुनर्गठित या रीचेक किए जाने के बादContentChecked कहा जाता है। कोणीय संस्करण 9 से
परिचय

24

लेख आपको ExpressionChangedAfterItHasBeenCheckedErrorत्रुटि के बारे में जानने के लिए आवश्यक सब कुछ महान विवरणों में व्यवहार की व्याख्या करता है।

आपके सेटअप में समस्या यह है कि ngAfterViewInitजीवनचक्र हुक को परिवर्तन का पता लगाने के बाद DOM अपडेट किया जाता है। और आप इस हुक में टेम्पलेट में उपयोग की जाने वाली संपत्ति को प्रभावी ढंग से बदल रहे हैं जिसका अर्थ है कि DOM को फिर से प्रस्तुत करने की आवश्यकता है:

  ngAfterViewInit() {
    this.message = 'all done loading :)'; // needs to be rendered the DOM
  }

और इसके लिए एक और परिवर्तन का पता लगाने के चक्र की आवश्यकता होगी और डिजाइन द्वारा कोणीय केवल एक पाचन चक्र चलाता है।

आपके पास मूल रूप से दो विकल्प हैं कि इसे कैसे ठीक किया जाए:

  • या तो एसिंक्रोनस रूप से संपत्ति को अद्यतन का उपयोग कर setTimeout, Promise.thenया अतुल्यकालिक नमूदार टेम्पलेट में संदर्भित

  • DOM अपडेट से पहले हुक में प्रॉपर्टी अपडेट करें - ngOnInit, ngDoCheck, ngAfterContentInit, ngAfterContentChecked।


अपने लेख पढ़ें: blog.angularindepth.com/… , जल्द ही एक और ब्लॉग पढ़ेंगे । फिर भी इस समस्या के समाधान का पता नहीं लगा सके। क्या आप मुझे बता सकते हैं कि क्या होता है अगर मैं ngDoCheck या ngAfterContentChecked Lifeecycle हुक जोड़ता हूं और यह जोड़ देता हूं। LCD..markForCheck (); (cdr for ChangeDetectorRef) इसके अंदर। क्या यह जीवन चक्र हुक और बाद में पूर्ण जाँच के बाद परिवर्तनों की जाँच करने का एक सही तरीका नहीं है।
हरसिमर

9

आपको बस अपने संदेश को सही जीवन चक्र हुक में अपडेट करना होगा, इस मामले में ngAfterContentCheckedइसके बजाय है ngAfterViewInit, क्योंकि ngAfterViewInit में चर संदेश के लिए एक चेक शुरू किया गया है, लेकिन अभी तक समाप्त नहीं हुआ है।

देखें: https://angular.io/docs/ts/latest/guide/lifecycle-hooks.html###fterview

तो कोड सिर्फ होगा:

import { Component } from 'angular2/core'

@Component({
  selector: 'my-app',
  template: `<div>I'm {{message}} </div>`,
})
export class App {
  message: string = 'loading :(';

  ngAfterContentChecked() {
     this.message = 'all done loading :)'
  }      
}

प्लंकर पर काम कर रहे डेमो देखें ।


मैं एक @ViewChildren()लंबाई-आधारित काउंटर के संयोजन का उपयोग कर रहा था, जो एक ऑब्जर्वेबल द्वारा आबाद था। यह एकमात्र समाधान था जो मेरे लिए काम करता था!
एमएसनफोर्ड

2
का संयोजन ngAfterContentCheckedऔर ChangeDetectorRefसे ऊपर मेरे लिए काम किया। पर ngAfterContentCheckedबुलाया -this.cdr.detectChanges();
कुणाल देथे

7

यह त्रुटि इसलिए आ रही है क्योंकि मौजूदा मान आरंभ होने के तुरंत बाद अपडेट हो रहा है। इसलिए यदि आप DOM में मौजूदा मूल्य प्रदान करने के बाद नए मूल्य को अपडेट करेंगे, तो यह ठीक काम करेगा। इस लेख में उल्लेख किया गया है। Angular Debugging "अभिव्यक्ति की जाँच होने के बाद यह बदल गया है"

उदाहरण के लिए आप उपयोग कर सकते हैं

ngOnInit() {
    setTimeout(() => {
      //code for your new value.
    });

}

या

ngAfterViewInit() {
  this.paginator.page
      .pipe(
          startWith(null),
          delay(0),
          tap(() => this.dataSource.loadLessons(...))
      ).subscribe();
}

जैसा कि आप देख सकते हैं कि मैंने सेटटाइम पद्धति में समय का उल्लेख नहीं किया है। जैसा कि यह ब्राउज़र प्रदान किया गया एपीआई है, जावास्क्रिप्ट एपीआई नहीं है, इसलिए यह ब्राउज़र स्टैक में अलग-अलग चलेगा और तब तक इंतजार करेगा जब तक कि स्टैक आइटम समाप्त न हो जाए।

ब्राउज़र एपीआई एनवोकस अवधारणा को एक यूट्यूब वीडियो (हैक क्या इवेंट लूप है) में फिलिप रॉबर्ट्स द्वारा समझाया गया है।


4

आप अपने कॉल को ngOnInt () में अपडेट करने के लिए अपनी कॉल भी लगा सकते हैं - विधि, कम से कम यह मेरे लिए काम करता है

ngOnInit() {
    this.updateMessage();
}

RC1 में यह अपवाद को ट्रिगर नहीं करता है


3

आप rxjs Observable.timerफ़ंक्शन का उपयोग करके एक टाइमर भी बना सकते हैं , और फिर अपनी सदस्यता में संदेश को अपडेट कर सकते हैं:                    

Observable.timer(1).subscribe(()=> this.updateMessage());

2

यह एक त्रुटि फेंक देता है क्योंकि जब ngAfterViewInit () कहा जाता है तो आपका कोड अपडेट हो जाता है। जब ngAfterViewInit जगह लेता है, तो आपका प्रारंभिक मूल्य बदल गया है, अगर आप ngAfterContentInit () में कॉल करते हैं तो यह एक त्रुटि नहीं करेगा।

ngAfterContentInit() {
    this.updateMessage();
}

0

मैं @Biranchi की पोस्ट पर टिप्पणी नहीं कर सका क्योंकि मेरे पास पर्याप्त प्रतिष्ठा नहीं है, लेकिन इसने मेरे लिए समस्या को ठीक कर दिया।

एक बात ध्यान दें! यदि परिवर्तन जोड़ रहा है तो परिवर्तन: ChangeDetectionStrategy.OnPush घटक पर काम नहीं किया, और इसका एक बच्चा घटक (गूंगा घटक) इसे माता-पिता में भी जोड़ने का प्रयास करें।

इसने बग को ठीक कर दिया, लेकिन मुझे आश्चर्य है कि इसके दुष्प्रभाव क्या हैं।


0

मुझे डेटाटेबल के साथ काम करते समय इसी तरह की त्रुटि मिली। जब आप एक दूसरे के अंदर * ngFor * का उपयोग करते हैं तो क्या होता है * ngFor datatable इस त्रुटि को फेंक देता है क्योंकि यह कोणीय परिवर्तन चक्र को बाधित करता है। इसके बजाय datatable अंदर datatable का उपयोग करने के बजाय एक नियमित तालिका का उपयोग करें या सरणी नाम के साथ mf.data बदलें। यह ठीक काम करता है।


0

मुझे लगता है कि एक सबसे सरल समाधान इस प्रकार होगा:

  1. फ़ंक्शन या सेटर के माध्यम से कुछ वैरिएबल पर मान निर्दिष्ट करने का एक कार्यान्वयन करें।
  2. (static working: boolean)उस कक्षा में एक क्लास वैरिएबल बनाएं जहां यह फ़ंक्शन मौजूद है और हर बार जब आप फ़ंक्शन को कॉल करते हैं, तो बस इसे अपनी पसंद के अनुसार सही बनाएं। फ़ंक्शन के भीतर, यदि काम करने का मूल्य सही है, तो बस कुछ भी किए बिना तुरंत वापस लौटें। और, वह कार्य करें जिसे आप चाहते हैं। एक बार कार्य पूरा होने के बाद, कोड की लाइन के अंत में या सब्सक्रिप्शन विधि के भीतर जब आप मान असाइन किए जाते हैं, तो इस चर को बदल देना सुनिश्चित करें!
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.