समस्या और संभावित समाधानों को पूरी तरह से समझने के लिए, हमें कोणीय परिवर्तन का पता लगाने की आवश्यकता है - पाइप और घटकों के लिए।
पाइप परिवर्तन का पता लगाने
स्टेटलेस / प्योर पाइप्स
डिफ़ॉल्ट रूप से, पाइप स्टेटलेस / प्योर हैं। स्टेटलेस / प्योर पाइप केवल इनपुट डेटा को आउटपुट डेटा में बदलते हैं। उन्हें कुछ भी याद नहीं है, इसलिए उनके पास कोई गुण नहीं है - बस एक transform()
विधि। इसलिए कोणीय, स्टेटलेस / शुद्ध पाइप के उपचार का अनुकूलन कर सकता है: यदि उनके इनपुट नहीं बदलते हैं, तो पाइप को परिवर्तन का पता लगाने के चक्र के दौरान निष्पादित करने की आवश्यकता नहीं है। जैसे पाइप के लिए {{power | exponentialStrength: factor}}
, power
और factor
इनपुट हैं।
इस प्रश्न के लिए "#student of students | sortByName:queryElem.value"
, students
और queryElem.value
इनपुट हैं, और पाइप sortByName
स्टेटलेस / प्योर है। students
एक सरणी (संदर्भ) है।
- जब एक छात्र जोड़ा जाता है, तो सरणी संदर्भ नहीं बदलता है -
students
नहीं बदलता है - इसलिए स्टेटलेस / शुद्ध पाइप निष्पादित नहीं किया जाता है।
- जब कुछ को फ़िल्टर इनपुट में टाइप किया जाता है,
queryElem.value
तो परिवर्तन होता है, इसलिए स्टेटलेस / शुद्ध पाइप निष्पादित होता है।
सरणी समस्या को ठीक करने का एक तरीका यह है कि किसी छात्र के जुड़ने पर हर बार सरणी संदर्भ को परिवर्तित किया जाए - यानी, जब छात्र जोड़ा जाता है, तो हर बार एक नया सरणी बनाएं। हम इसके साथ कर सकते हैं concat()
:
this.students = this.students.concat([{name: studentName}]);
हालांकि यह काम करता है, हमारी addNewStudent()
विधि को एक निश्चित तरीके से लागू नहीं किया जाना चाहिए क्योंकि हम एक पाइप का उपयोग कर रहे हैं। हम push()
अपने ऐरे से जोड़ना चाहते हैं ।
स्टेटफुल पाइप्स
स्टेटफुल पाइप में स्थिति होती है - उनके पास सामान्य रूप से गुण होते हैं, न कि केवल एक transform()
विधि। यदि उनके इनपुट नहीं बदले हैं तो भी उनका मूल्यांकन करने की आवश्यकता हो सकती है। जब हम यह निर्दिष्ट करते हैं कि एक पाइप स्टेटफुल / नॉन-प्योर है - pure: false
- तब जब भी कोणीय की परिवर्तन पहचान प्रणाली परिवर्तनों के लिए एक घटक की जाँच करती है और वह घटक एक स्टेटफुल पाइप का उपयोग करता है, तो यह पाइप के आउटपुट की जाँच करेगा, कि उसका इनपुट बदला है या नहीं।
यह लगता है कि हम क्या चाहते हैं, भले ही यह कम कुशल है, क्योंकि हम चाहते हैं कि पाइप निष्पादित करें भले ही students
संदर्भ बदल नहीं गया हो। यदि हम बस पाइप को स्टेटफुल बनाते हैं, तो हमें एक त्रुटि मिलती है:
EXCEPTION: Expression 'students | sortByName:queryElem.value in HelloWorld@7:6'
has changed after it was checked. Previous value: '[object Object],[object Object]'.
Current value: '[object Object],[object Object]' in [students | sortByName:queryElem.value
@ ड्रूमोर के उत्तर के अनुसार , "यह त्रुटि केवल देव मोड में होती है (जो कि बीटा -० के रूप में डिफ़ॉल्ट रूप से सक्षम होती है)। यदि आप enableProdMode()
ऐप को बूटस्ट्रैप करते समय कॉल करते हैं, तो त्रुटि नहीं मिलेगी।" के लिये दस्तावेजApplicationRef.tick()
राज्य:
विकास मोड में, टिक () यह सुनिश्चित करने के लिए एक दूसरा परिवर्तन का पता लगाने का चक्र भी करता है ताकि कोई और परिवर्तन का पता न चले। यदि इस दूसरे चक्र के दौरान अतिरिक्त परिवर्तन किए जाते हैं, तो ऐप में बाइंडिंग के साइड-इफेक्ट्स होते हैं, जिन्हें एकल परिवर्तन पहचान पास में हल नहीं किया जा सकता है। इस मामले में, कोणीय एक त्रुटि फेंकता है, क्योंकि एक कोणीय आवेदन में केवल एक परिवर्तन खोज पास हो सकता है, जिसके दौरान सभी परिवर्तन का पता लगाने को पूरा करना होगा।
हमारे परिदृश्य में मेरा मानना है कि त्रुटि फर्जी / भ्रामक है। हमारे पास एक स्टेटफुल पाइप है, और आउटपुट को हर बार इसे बदलने के लिए कहा जा सकता है - इसके दुष्प्रभाव हो सकते हैं और यह ठीक है। पाइप के बाद NgFor का मूल्यांकन किया जाता है, इसलिए इसे ठीक काम करना चाहिए।
हालाँकि, हम वास्तव में इस त्रुटि को फेंकने के साथ विकसित नहीं कर सकते हैं, इसलिए एक वर्कअराउंड पाइप कार्यान्वयन के लिए एक सरणी संपत्ति (यानी, राज्य) को जोड़ना है और हमेशा उस सरणी को वापस करना है। इस समाधान के लिए @ पिक्सेलबिट्स का उत्तर देखें।
हालांकि, हम अधिक कुशल हो सकते हैं, और जैसा कि हम देखेंगे, हमें पाइप कार्यान्वयन में सरणी संपत्ति की आवश्यकता नहीं होगी, और हमें दोहरे परिवर्तन का पता लगाने के लिए वर्कअराउंड की आवश्यकता नहीं होगी।
घटक परिवर्तन का पता लगाने
डिफ़ॉल्ट रूप से, प्रत्येक ब्राउज़र घटना पर, कोणीय परिवर्तन का पता लगाने के लिए हर घटक के माध्यम से यह देखने के लिए जाता है कि क्या यह बदल गया है - इनपुट और टेम्पलेट (और शायद अन्य सामान?) की जाँच की जाती है।
यदि हम जानते हैं कि एक घटक केवल उसके इनपुट गुणों (और टेम्पलेट ईवेंट्स) पर निर्भर करता है, और यह कि इनपुट गुण अपरिवर्तनीय हैं, तो हम बहुत अधिक कुशल onPush
परिवर्तन का पता लगाने की रणनीति का उपयोग कर सकते हैं । इस रणनीति के साथ, हर ब्राउज़र घटना पर जाँच करने के बजाय, एक घटक की जाँच तभी की जाती है जब इनपुट बदलते हैं और जब टेम्पलेट इवेंट ट्रिगर होते हैं। और, जाहिर है, हम Expression ... has changed after it was checked
इस सेटिंग के साथ उस त्रुटि को प्राप्त नहीं करते हैं । ऐसा तब होता है क्योंकि एक onPush
घटक को फिर से जांच नहीं किया जाता है जब तक कि इसे "चिह्नित" ( ChangeDetectorRef.markForCheck()
) फिर से नहीं किया जाता है। इसलिए टेम्पलेट बाइंडिंग और स्टेटफुल पाइप आउटपुट केवल एक बार निष्पादित / मूल्यांकन किए जाते हैं। स्टेटलेस / प्योर पाइप अभी भी निष्पादित नहीं किए जाते हैं जब तक कि उनके इनपुट नहीं बदलते। इसलिए हमें अभी भी यहां एक स्टेटफुल पाइप की जरूरत है।
यह समाधान है @EricMartinez ने सुझाव दिया: onPush
परिवर्तन का पता लगाने के साथ स्टेटफुल पाइप । इस समाधान के लिए @ caffinatedmonkey का उत्तर देखें।
ध्यान दें कि इस समाधान के साथ transform()
विधि को हर बार एक ही सरणी वापस करने की आवश्यकता नहीं है। मुझे लगता है कि थोड़ा अजीब है: कोई राज्य नहीं के साथ एक राज्य पाइप। इसके बारे में कुछ और सोचना ... स्टेटफुल पाइप को शायद हमेशा एक ही सरणी वापस करना चाहिए। अन्यथा इसका उपयोग केवल onPush
देव विधा में घटकों के साथ किया जा सकता है।
इतना सब होने के बाद, मुझे लगता है कि मुझे @ एरिक और @ पिक्सेलबिट्स के उत्तरों का संयोजन पसंद है: स्टेटफुल पाइप जो उसी सरणी संदर्भ को वापस करता है, onPush
परिवर्तन का पता लगाने के साथ अगर घटक इसे अनुमति देता है। चूंकि स्टेटफुल पाइप एक ही सरणी संदर्भ देता है, इसलिए पाइप को अभी भी उन घटकों के साथ उपयोग किया जा सकता है जो कि कॉन्फ़िगर नहीं हैं onPush
।
Plunker
यह संभवतः एक कोणीय 2 मुहावरा बन जाएगा: यदि एक सरणी एक पाइप खिला रही है, और सरणी बदल सकती है (सरणी में आइटम जो कि नहीं है, तो सरणी संदर्भ नहीं), हमें एक स्टेटफुल पाइप का उपयोग करने की आवश्यकता है।
pure:false
अपने पाइप में, औरchangeDetection: ChangeDetectionStrategy.OnPush
अपने घटक में जोड़ें ।