Angular2 परिवर्तन का पता लगाने: ngOnChanges नेस्टेड ऑब्जेक्ट के लिए फायरिंग नहीं


129

मुझे पता है कि मैं इस बारे में पूछने वाला पहला व्यक्ति नहीं हूं, लेकिन मुझे पिछले सवालों का जवाब नहीं मिला। मेरे पास यह एक घटक में है

<div class="col-sm-5">
    <laps
        [lapsData]="rawLapsData"
        [selectedTps]="selectedTps"
        (lapsHandler)="lapsHandler($event)">
    </laps>
</div>

<map
    [lapsData]="rawLapsData"
    class="col-sm-7">
</map>

नियंत्रक में rawLapsdataसमय-समय पर उत्परिवर्तित होता है।

में laps, डेटा एक सारणीबद्ध प्रारूप में HTML के रूप में आउटपुट है। यह जब भी rawLapsdataबदलता है बदल जाता है।

मेरे mapघटक को ngOnChangesGoogle मानचित्र पर मार्कर को फिर से शुरू करने के लिए ट्रिगर के रूप में उपयोग करने की आवश्यकता है । समस्या यह है कि जब rawLapsDataमाता-पिता में बदलाव होता है तो ngOnChanges फायर नहीं करता है । मैं क्या कर सकता हूँ?

import {Component, Input, OnInit, OnChanges, SimpleChange} from 'angular2/core';

@Component({
    selector: 'map',
    templateUrl: './components/edMap/edMap.html',
    styleUrls: ['./components/edMap/edMap.css']
})
export class MapCmp implements OnInit, OnChanges {
    @Input() lapsData: any;
    map: google.maps.Map;

    ngOnInit() {
        ...
    }

    ngOnChanges(changes: { [propName: string]: SimpleChange }) {
        console.log('ngOnChanges = ', changes['lapsData']);
        if (this.map) this.drawMarkers();
    }

अपडेट: ngOnChanges काम नहीं कर रहा है, लेकिन ऐसा लगता है कि जैसे लैप्सडाटा अपडेट किया जा रहा है। NgInit में ज़ूम परिवर्तन के लिए एक ईवेंट श्रोता है जो कॉल भी करता है this.drawmarkers। जब मैं ज़ूम बदलता हूं तो मुझे वास्तव में मार्करों में बदलाव दिखाई देता है। तो केवल मुद्दा यह है कि इनपुट डेटा में बदलाव के समय मुझे सूचना नहीं मिलती है।

माता-पिता में, मेरे पास यह रेखा है। (याद रखें कि परिवर्तन लैप्स में परिलक्षित होता है, लेकिन मानचित्र में नहीं)।

this.rawLapsData = deletePoints(this.rawLapsData, this.selectedTps);

और ध्यान दें कि this.rawLapsDataखुद एक बड़े जोंस ऑब्जेक्ट के बीच में एक पॉइंटर है

this.rawLapsData = this.main.data.TrainingCenterDatabase.Activities[0].Activity[0].Lap;

आपका कोड यह नहीं दिखाता है कि डेटा कैसे अपडेट किया गया है या डेटा किस प्रकार का है। एक नया उदाहरण सौंपा गया है या केवल संशोधित मूल्य की संपत्ति है?
गुंटर ज़ोचबॉयर

@ गुंटरज़ोचबॉयर I ने मूल घटक
साइमन एच

मुझे लगता है कि इस पंक्ति को zone.run(...)तब करना चाहिए।
गुंटर जोचबॉयर

1
आपका सरणी (संदर्भ) नहीं बदल रहा है, इसलिए ngOnChanges()इसे नहीं बुलाया जाएगा। ngDoCheck()यदि सरणी सामग्री बदल गई है, तो यह निर्धारित करने के लिए आप अपने स्वयं के तर्क का उपयोग और कार्यान्वित कर सकते हैं । lapsDataअपडेट किया गया है क्योंकि इसमें उसी सरणी के लिए एक संदर्भ है / है rawLapsData
मार्क राजकोक

1
1) लैप्स घटक में आपका कोड / टेम्प्लेट लैप्सडाट सरणी में प्रत्येक प्रविष्टि पर लूप करता है, और सामग्री प्रदर्शित करता है, इसलिए प्रदर्शित होने वाले डेटा के प्रत्येक टुकड़े पर कोणीय बाइंडिंग हैं। 2) भले ही एंगुलर किसी घटक के इनपुट गुणों में किसी भी परिवर्तन (संदर्भ जाँच) का पता नहीं लगाता है, फिर भी यह (डिफ़ॉल्ट रूप से) सभी टेम्पलेट बाइंडिंग की जाँच करता है। यह है कि कैसे बदलाव पर उठाता है। 3) मानचित्र घटक की संभावना है कि इसकी लैप्सडेट इनपुट संपत्ति में इसके टेम्पलेट में कोई बाइंडिंग नहीं है, है ना? वह अंतर स्पष्ट करेगा।
मार्क राजकॉक

जवाबों:


153

rawLapsData भले ही आप सरणी की सामग्री को संशोधित करते हों, (उदाहरण के लिए, आइटम जोड़ें, आइटम निकालें, आइटम बदलें)।

परिवर्तन का पता लगाने के दौरान, जब कोणीय परिवर्तन के लिए घटकों के इनपुट गुणों की जाँच करता है, तो यह ===गंदे जाँच के लिए (अनिवार्य रूप से) उपयोग करता है । सरणियों के लिए, इसका मतलब है कि सरणी संदर्भ (केवल) गंदे चेक किए गए हैं। चूंकि rawLapsDataसरणी संदर्भ नहीं बदल रहा है, इसलिए ngOnChanges()इसे नहीं बुलाया जाएगा।

मैं दो संभावित समाधानों के बारे में सोच सकता हूं:

  1. ngDoCheck()सरणी सामग्री बदल गई है, यह निर्धारित करने के लिए अपने स्वयं के परिवर्तन का पता लगाने के तर्क को लागू करें और प्रदर्शन करें। (जीवनचक्र के हुक डॉक्स का एक उदाहरण है ।)

  2. rawLapsDataजब भी आप सरणी सामग्री में कोई परिवर्तन करते हैं, तो एक नया सरणी असाइन करें । फिर ngOnChanges()बुलाया जाएगा क्योंकि सरणी (संदर्भ) एक परिवर्तन के रूप में दिखाई देगी।

आपके जवाब में, आप एक और समाधान के साथ आए।

यहाँ ओपी पर कुछ टिप्पणियों को दोहराते हुए:

मैं अभी भी यह नहीं देख lapsसकता कि बदलाव पर कैसे उठा जा सकता है (निश्चित रूप से यह ngOnChanges()स्वयं के बराबर कुछ का उपयोग करना चाहिए ?) जबकि mapनहीं कर सकता।

  • में lapsमें प्रत्येक प्रविष्टि से अधिक घटक अपने कोड / टेम्पलेट छोरों lapsDataसरणी, और प्रदर्शित करता है सामग्री, इसलिए वहाँ डेटा के प्रत्येक टुकड़े कि प्रदर्शित किया जाता है पर कोणीय बाइंडिंग कर रहे हैं।
  • यहां तक ​​कि जब एंगुलर घटक के इनपुट गुणों ( ===जाँच का उपयोग करके) में किसी भी बदलाव का पता नहीं लगाता है , तब भी यह (डिफ़ॉल्ट रूप से) सभी टेम्पलेट बाइंडिंग की गंदे जाँच करता है। जब उनमें से कोई भी परिवर्तन होता है, तो कोणीय DOM को अपडेट करेगा। वही तुम देख रहे हो।
  • mapsघटक संभावना इसके लिए अपनी टेम्पलेट में कोई बाइंडिंग नहीं है lapsDataइनपुट संपत्ति, है ना? वह अंतर स्पष्ट करेगा।

ध्यान दें कि lapsDataदोनों घटकों और rawLapsDataमूल घटक में सभी एक ही / एक सरणी के लिए इंगित करते हैं। इसलिए भले ही कोणीय किसी भी (संदर्भ) lapsDataइनपुट गुणों में परिवर्तन को नोटिस नहीं करता है , घटक "मिलता है" / किसी भी सरणी सामग्री में परिवर्तन देखते हैं क्योंकि वे सभी साझा / संदर्भ करते हैं कि एक सरणी। हमें इन परिवर्तनों को प्रचारित करने के लिए कोणीय की आवश्यकता नहीं है, जैसे हम एक आदिम प्रकार (स्ट्रिंग, संख्या, बूलियन) के साथ करेंगे। लेकिन एक आदिम प्रकार के साथ, मूल्य में कोई भी परिवर्तन हमेशा ट्रिगर होता है ngOnChanges()- जो कि आपके उत्तर / समाधान में आपके द्वारा शोषण किया जाने वाला कुछ है।

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


हां, मैं एक गहरी नेस्टेड ऑब्जेक्ट को उत्परिवर्तित कर रहा था, और मुझे लगता है कि इससे एंगुलर के लिए संरचना में बदलाव को देखना मुश्किल हो गया। मेरी वरीयता को म्यूट करने के लिए नहीं, लेकिन इसका अनुवाद XML है और मैं आसपास के किसी भी डेटा को खोने का जोखिम नहीं उठा सकता हूं क्योंकि मैं xml को फिर से बनाना चाहता हूं
सिमोन एच

7
@SimonH, "संरचना में परिवर्तन के लिए कोणीय के लिए कठिन" - बस स्पष्ट होने के लिए, कोणीय भी इनपुट गुणों के अंदर नहीं दिखता है जो परिवर्तनों के लिए सरणियाँ या ऑब्जेक्ट हैं। यह केवल यह देखना चाहता है कि क्या मूल्य बदल गया है - वस्तुओं और सरणियों के लिए, मूल्य संदर्भ है। आदिम प्रकारों के लिए, मान है ... मान। (मुझे यकीन नहीं है कि मेरे पास सीधे सभी शब्दजाल हैं, लेकिन आप इस विचार को प्राप्त कर सकते हैं।)
मार्क राजकोक

11
बहुत बढ़िया जवाब। Angular2 टीम को परिवर्तन का पता लगाने के लिए एक विस्तृत, आधिकारिक दस्तावेज प्रकाशित करने की सख्त आवश्यकता है।

अगर मैं doCheck में कार्यक्षमता करता हूं, तो मेरे मामले में चेक कई बार कॉल कर रहा है। क्या आप मुझे कोई और तरीका बता सकते हैं?
Mr_Perfect

@MarkRajcok आप इस मुद्दे को हल करने के लिए मेरी मदद कर सकते हैं stackoverflow.com/questions/50166996/…
निकसन

27

सबसे साफ दृष्टिकोण नहीं है, लेकिन आप हर बार जब आप मूल्य बदल सकते हैं, तो आप ऑब्जेक्ट को क्लोन कर सकते हैं?

   rawLapsData = Object.assign({}, rawLapsData);

मुझे लगता है कि मैं अपने स्वयं के कार्यान्वयन पर इस दृष्टिकोण को पसंद करूंगा ngDoCheck()लेकिन शायद @ GünterZöchbauer जैसा कोई व्यक्ति इसमें झंकार कर सकता है।


यदि आप सुनिश्चित नहीं हैं कि यदि कोई लक्षित ब्राउज़र <Object.assign ()> का समर्थन करेगा, तो आप एक जंस स्ट्रिंग में भी कड़ाई कर सकते हैं और वापस
जौन

1
@ गौतम या एक पॉलीफ़िल?
डेविड

12

एरे के मामले में आप इसे इस तरह से कर सकते हैं:

में .tsफ़ाइल (जनक घटक) जहाँ आप अपने अद्यतन कर रहे हैं rawLapsDataइस तरह यह कार्य करें:

rawLapsData = somevalue; // change detection will not happen

उपाय:

rawLapsData = {...somevalue}; //change detection will happen

और ngOnChangesबच्चे के घटक में कहा जाएगा


10

मार्क राजकोक के दूसरे समाधान के विस्तार के रूप में

जब भी आप सरणी सामग्री में कोई परिवर्तन करते हैं, तो एक नया सरणी निर्दिष्ट करें। फिर ngOnChanges () कहा जाएगा क्योंकि सरणी (संदर्भ) एक परिवर्तन के रूप में दिखाई देगा

आप इस तरह से सरणी की सामग्री को क्लोन कर सकते हैं:

rawLapsData = rawLapsData.slice(0);

इसका जिक्र मैं इसलिए कर रहा हूं

rawLapsData = Object.assign ({}, rawLapsData);

मेरे लिए काम नहीं किया। आशा है कि ये आपकी मदद करेगा।


8

यदि डेटा किसी बाहरी लाइब्रेरी से आता है, तो आपको डेटा अपटेट स्टेटमेंट को चलाने की आवश्यकता हो सकती है zone.run(...)। इसके लिए इंजेक्ट zone: NgZoneकरें। यदि आप zone.run()पहले से ही बाहरी लाइब्रेरी की तात्कालिकता को चला सकते हैं , तो आपको zone.run()बाद में आवश्यकता नहीं हो सकती है ।


जैसा कि ओपी को टिप्पणियों में कहा गया है, परिवर्तन बाहरी नहीं थे, लेकिन एक जोंस वस्तु के भीतर गहरे थे
साइमन एच

1
जैसा कि आपका उत्तर कहता है, अभी भी हमें एंगुलर 2 में सिंक में चीज़ रखने के लिए कुछ चलाने की आवश्यकता है, जैसे कोणीय 1 यह था $scope.$apply?
पंकज पारकर

1
यदि कोणीय के बाहर से कुछ शुरू किया जाता है, तो जोन द्वारा पैच किए गए API का उपयोग नहीं किया जाता है और कोणीय को संभावित परिवर्तनों के बारे में सूचित नहीं किया जाता है। हाँ, zone.runके समान है $scope.apply
गुंटर Zöchbauer

6

ChangeDetectorRef.detectChanges()जब आप किसी नेस्टेड ऑब्जेक्ट को एडिट करते हैं तो परिवर्तन का पता लगाने के लिए एंगुलर को बताने के लिए उपयोग करें (कि यह उसकी गंदी जाँच से छूट जाता है)।


पर कैसे ? मैं इसका उपयोग करने की कोशिश कर रहा हूं, माता-पिता द्वारा 2 तरह से बंधे [(संग्रह)] में एक नई वस्तु को धकेलने के बाद मैं एक बच्चे में बदलाव को गति देना चाहता हूं।
देउंज

समस्या यह नहीं है कि परिवर्तन का पता नहीं चलता है, लेकिन उस परिवर्तन का पता लगाने से परिवर्तनों का पता नहीं चलता है! तो यह एक समाधान नहीं लगता है! इस उत्तर को पांच उत्थान क्यों मिले?
शाहरयार सज्जोई

5

आपकी समस्या के समाधान के लिए मेरे पास 2 उपाय हैं

  1. परिवर्तित डेटा ngDoCheckका पता लगाने के लिए उपयोग करें objectया नहीं
  2. मूल घटक से objectएक नया स्मृति पता असाइन करें object = Object.create(object)

2
क्या Object.create (ऑब्जेक्ट) बनाम Object.assign ({}, ऑब्जेक्ट) के बीच कोई उल्लेखनीय अंतर होगा?
डैनियल गैलेर्ज़ा

3

मेरा 'हैक' समाधान है

   <div class="col-sm-5">
        <laps
            [lapsData]="rawLapsData"
            [selectedTps]="selectedTps"
            (lapsHandler)="lapsHandler($event)">
        </laps>
    </div>
    <map
        [lapsData]="rawLapsData"
        [selectedTps]="selectedTps"   // <--------
        class="col-sm-7">
    </map>

रॉलेटैपडैट के रूप में उसी समय चयनितटैप्स में परिवर्तन होता है और यह मैप को एक सरल वस्तु आदिम प्रकार के माध्यम से परिवर्तन का पता लगाने का एक और मौका देता है । यह सुरुचिपूर्ण नहीं है, लेकिन यह काम करता है।


मुझे खासतौर पर मिडल / लार्ज स्केल ऐप्स पर टेम्पलेट सिंटैक्स में विभिन्न घटकों पर सभी परिवर्तनों को ट्रैक करना मुश्किल लगता है। आमतौर पर मैं डेटा (क्विक सॉल्यूशन) पास करने के लिए शेयर्ड इवेंट एमिटर और सब्सक्रिप्शन का उपयोग करता हूं, या इसके लिए Redux पैटर्न (Rx.Subject के माध्यम से) लागू करता हूं (जब प्लान करने का समय हो) ...
Sasxa

3

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

import * as _ from 'lodash';

this.foo = _.clone(this.foo);

2

यहाँ एक हैक है कि बस मुझे इस एक के साथ मुसीबत से बाहर मिला है।

तो ओपी के लिए एक समान परिदृश्य - मुझे एक नेस्टेड कोणीय घटक मिला है जो डेटा को इसके पास करने की आवश्यकता है, लेकिन इनपुट एक सरणी को इंगित करता है, और जैसा कि ऊपर उल्लेख किया गया है, कोणीय परिवर्तन नहीं देखता है क्योंकि यह जांच नहीं करता है सरणी की सामग्री।

तो इसे ठीक करने के लिए मैं एक परिवर्तन का पता लगाने के लिए एंगुलर के लिए एक स्ट्रिंग में सरणी को परिवर्तित करता हूं, और फिर नेस्टेड घटक I में विभाजित करता हूं (',') स्ट्रिंग को फिर से एक सरणी में और इसके सुखद दिनों को फिर से।


1
नीरस Array.slice (0) दृष्टिकोण बहुत क्लीनर है।
क्रिस हैन्स

2

मैं उसी जरूरत पर ठोकर खाई। और मैंने इस पर बहुत कुछ पढ़ा है, यहाँ इस विषय पर मेरा तांबा है।

यदि आप पुश पर अपनी परिवर्तन पहचान चाहते हैं, तो आपके पास यह तब होगा जब आप किसी वस्तु के मूल्य को दाईं ओर बदलते हैं? और आपके पास यह भी होगा यदि आप किसी भी तरह से वस्तुओं को हटाते हैं।

जैसा कि पहले ही कहा गया है, changeDetectionStrategy.onPush का उपयोग करें

कहते हैं कि आपके पास यह घटक है, जिसमें बदलाव के साथ बदलाव करें।

<component [collection]="myCollection"></component>

तब आप एक आइटम को धक्का देंगे और परिवर्तन का पता लगाने को ट्रिगर करेंगे:

myCollection.push(anItem);
refresh();

या आप किसी आइटम को निकालेंगे और परिवर्तन का पता लगाने के लिए ट्रिगर करेंगे:

myCollection.splice(0,1);
refresh();

या आप एक आइटम के लिए एक attrbibute मान को बदल देंगे और परिवर्तन का पता लगाने को ट्रिगर करेंगे:

myCollection[5].attribute = 'new value';
refresh();

ताज़ा करने की सामग्री:

refresh() : void {
    this.myCollection = this.myCollection.slice();
}

स्लाइस विधि सटीक समान एरे को लौटाती है, और [=] साइन इसके लिए एक नया संदर्भ बनाते हैं, हर बार आपको इसकी आवश्यकता होने पर परिवर्तन का पता लगाने को ट्रिगर करता है। आसान और पठनीय :)

सादर,


2

मैंने यहां बताए गए सभी समाधानों की कोशिश की है, लेकिन किसी कारण से ngOnChanges()अभी भी मेरे लिए आग नहीं लगी। इसलिए मैंने this.ngOnChanges()उस सेवा को कॉल करने के साथ इसे बुलाया जो मेरे सरणियों को फिर से खोलती है और यह काम किया .... सही है? शायद ऩही। साफ? बिलकुल नहीं। काम करता है? हाँ!


1

ठीक है तो इसके लिए मेरा समाधान था:

this.arrayWeNeed.DoWhatWeNeedWithThisArray();
const tempArray = [...arrayWeNeed];
this.arrayWeNeed = [];
this.arrayWeNeed = tempArray;

और यह मुझे ngOnChanges ट्रिगर करता है


0

मुझे इसके लिए एक हैक बनाना था -

I created a Boolean Input variable and toggled it whenever array changed, which triggered change detection in the child component, hence achieving the purpose

0

मेरे मामले में यह ऑब्जेक्ट वैल्यू में परिवर्तन था जो ngOnChangeकैप्चर नहीं कर रहा था। एपीआई कॉल के जवाब में कुछ ऑब्जेक्ट वैल्यू को संशोधित किया गया है। ऑब्जेक्ट को पुन: व्यवस्थित करने से समस्या ठीक हो गई और ngOnChangeबच्चे के घटक में ट्रिगर होने का कारण बन गया ।

कुछ इस तरह

 this.pagingObj = new Paging(); //This line did the magic
 this.pagingObj.pageNumber = response.PageNumber;
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.