«Expression has changed after it was checked» — यह क्यों होता है और इससे कैसे निपटें?
\n\nअगर आपने कभी Angular में एप्लिकेशन बनाया है, तो आपने शायद कंसोल में यह डरावना मैसेज देखा होगा:
\n\nERROR Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'false'. Current value: 'true'.\n\nयह एरर डेवलपर्स के लिए एक बड़ी समस्या है। Stack Overflow पर हज़ारों सवाल, घंटों डिबगिंग, और कई नर्वस ब्रेकडाउन। लेकिन अच्छी खबर यह है कि इसे न सिर्फ ठीक किया जा सकता है, बल्कि समझा भी जा सकता है कि यह क्यों होता है। इस आर्टिकल में हम Expression has changed after it was checked एरर के मुख्य कारणों को समझेंगे, TypeScript में उदाहरण लिखेंगे और ठोस समाधान देंगे। चलिए शुरू करते हैं!
\n\nयह एरर आखिर क्या है?
\n\nAngular दो मोड में काम करता है: डेवलपमेंट (dev mode) और प्रोडक्शन। Dev मोड में फ्रेमवर्क एक अतिरिक्त जांच करता है — हर चेंज डिटेक्शन साइकल के बाद, वह दूसरा पास चलाता है ताकि यह सुनिश्चित कर सके कि डेटा "चलते-फिरते" नहीं बदला। अगर पहले और दूसरे पास के बीच वैल्यू बदल जाती है — तो Angular ExpressionChangedAfterItHasBeenCheckedError के साथ क्रैश हो जाता है।
\n\nसीधे शब्दों में: आपने Angular को बताया "यह है डेटा", उसने इसे चेक किया, और फिर आपने इसे बदल दिया। Angular नाराज हो जाता है और चिल्लाता है।
\n\nएरर के 3 मुख्य कारण
\n\n1. ngAfterViewInit के बाद लाइफसाइकल हुक्स में डेटा बदलना
\n\nसबसे आम कारण — ngAfterViewInit, ngAfterContentInit या ngAfterViewChecked हुक में कंपोनेंट की प्रॉपर्टी बदलने की कोशिश करना। इस समय Angular व्यू की जांच पूरी कर चुका होता है, और कोई भी बदलाव एरर का कारण बनता है।
TypeScript में उदाहरण:
\n\nimport { Component, AfterViewInit } from '@angular/core';\n\n@Component({\n selector: 'app-example',\n template: '<p>{{ message }}</p>'\n})\nexport class ExampleComponent implements AfterViewInit {\n message = 'शुरुआती वैल्यू';\n\n ngAfterViewInit(): void {\n // एरर! व्यू चेक करने के बाद प्रॉपर्टी बदल रहे हैं\n this.message = 'बदली हुई वैल्यू';\n }\n}\n\nयहाँ Angular पहले message को चेक करता है (वैल्यू "शुरुआती वैल्यू"), फिर ngAfterViewInit को कॉल करता है, जहाँ आप इसे "बदली हुई वैल्यू" में बदल देते हैं। दोबारा चेक करने पर Angular को अंतर दिखता है और वह एरर फेंकता है।
2. लाइफसाइकल के अंदर एसिंक्रोनस ऑपरेशन
\n\nअगर आप हुक के अंदर एसिंक्रोनस कोड (setTimeout, Promise, Observable) चलाते हैं, तो डेटा अपडेट Angular के कंट्रोल ज़ोन के बाहर होता है।
\n\nउदाहरण:
\n\nimport { Component, OnInit } from '@angular/core';\n\n@Component({\n selector: 'app-async',\n template: '<p>{{ data }}</p>'\n})\nexport class AsyncComponent implements OnInit {\n data = 'शुरू';\n\n ngOnInit(): void {\n setTimeout(() => {\n this.data = 'नई वैल्यू';\n }, 0);\n }\n}\n\nहालाँकि setTimeout 0 मिलीसेकंड की देरी से लगभग तुरंत चलता है, Angular पहले ही अपनी जांच पूरी कर चुका होता है। Dev मोड में यह एरर का कारण बनेगा।
\n\n3. नेस्टेड कंपोनेंट और Input के ज़रिए डेटा ट्रांसफर
\n\nजब पैरेंट कंपोनेंट चाइल्ड कंपोनेंट को @Input के ज़रिए डेटा भेजता है, और चाइल्ड कंपोनेंट अपने लाइफसाइकल में इस डेटा को बदलने की कोशिश करता है, तो कंफ्लिक्ट होता है।
उदाहरण:
\n\n// पैरेंट कंपोनेंट\n@Component({\n selector: 'app-parent',\n template: '<app-child [value]="parentValue"></app-child>'\n})\nexport class ParentComponent {\n parentValue = 'मूल वैल्यू';\n}\n\n// चाइल्ड कंपोनेंट\n@Component({\n selector: 'app-child',\n template: '<p>{{ value }}</p>'\n})\nexport class ChildComponent implements AfterViewInit {\n @Input() value: string = '';\n\n ngAfterViewInit(): void {\n this.value = 'चाइल्ड में बदला गया'; // यह एरर देगा\n }\n}