कोणीय 2 - मॉडल परिवर्तन के बाद अद्यतन नहीं देखें


129

मेरे पास एक सरल घटक है जो हर कुछ सेकंड में एक रीस्ट एपी को कॉल करता है और कुछ JSON डेटा वापस प्राप्त करता है। मैं अपने लॉग स्टेटमेंट और नेटवर्क ट्रैफ़िक से देख सकता हूं कि वापस जा रहा JSON डेटा बदल रहा है, और मेरा मॉडल अपडेट किया जा रहा है, हालाँकि, दृश्य नहीं बदल रहा है।

मेरा घटक जैसा दिखता है:

import {Component, OnInit} from 'angular2/core';
import {RecentDetectionService} from '../services/recentdetection.service';
import {RecentDetection} from '../model/recentdetection';
import {Observable} from 'rxjs/Rx';

@Component({
    selector: 'recent-detections',
    templateUrl: '/app/components/recentdetection.template.html',
    providers: [RecentDetectionService]
})



export class RecentDetectionComponent implements OnInit {

    recentDetections: Array<RecentDetection>;

    constructor(private recentDetectionService: RecentDetectionService) {
        this.recentDetections = new Array<RecentDetection>();
    }

    getRecentDetections(): void {
        this.recentDetectionService.getJsonFromApi()
            .subscribe(recent => { this.recentDetections = recent;
             console.log(this.recentDetections[0].macAddress) });
    }

    ngOnInit() {
        this.getRecentDetections();
        let timer = Observable.timer(2000, 5000);
        timer.subscribe(() => this.getRecentDetections());
    }
}

और मेरा विचार इस प्रकार है:

<div class="panel panel-default">
    <!-- Default panel contents -->
    <div class="panel-heading"><h3>Recently detected</h3></div>
    <div class="panel-body">
        <p>Recently detected devices</p>
    </div>

    <!-- Table -->
    <table class="table" style="table-layout: fixed;  word-wrap: break-word;">
        <thead>
            <tr>
                <th>Id</th>
                <th>Vendor</th>
                <th>Time</th>
                <th>Mac</th>
            </tr>
        </thead>
        <tbody  >
            <tr *ngFor="#detected of recentDetections">
                <td>{{detected.broadcastId}}</td>
                <td>{{detected.vendor}}</td>
                <td>{{detected.timeStamp | date:'yyyy-MM-dd HH:mm:ss'}}</td>
                <td>{{detected.macAddress}}</td>
            </tr>
        </tbody>
    </table>
</div>

मैं उस परिणाम से देख सकता हूं console.log(this.recentDetections[0].macAddress)कि हाल ही में किए गए ऑब्जेक्ट को अपडेट किया जा रहा है, लेकिन जब तक मैं पृष्ठ को लोड नहीं करता, तब तक दृश्य में तालिका कभी नहीं बदलती है।

मैं यह देखने के लिए संघर्ष कर रहा हूं कि मैं यहां क्या कर रहा हूं। क्या कोई मदद कर सकता है?


मैं कोड को अधिक स्वच्छ और कम जटिल बनाने की सलाह देता हूं: stackoverflow.com/a/48873980/571030
glukki

जवाबों:


216

हो सकता है कि आपकी सेवा में कोड किसी तरह से कोणीय क्षेत्र से बाहर हो जाए। यह परिवर्तन का पता लगाता है। यह काम करना चाहिए:

import {Component, OnInit, NgZone} from 'angular2/core';

export class RecentDetectionComponent implements OnInit {

    recentDetections: Array<RecentDetection>;

    constructor(private zone:NgZone, // <== added
        private recentDetectionService: RecentDetectionService) {
        this.recentDetections = new Array<RecentDetection>();
    }

    getRecentDetections(): void {
        this.recentDetectionService.getJsonFromApi()
            .subscribe(recent => { 
                 this.zone.run(() => { // <== added
                     this.recentDetections = recent;
                     console.log(this.recentDetections[0].macAddress) 
                 });
        });
    }

    ngOnInit() {
        this.getRecentDetections();
        let timer = Observable.timer(2000, 5000);
        timer.subscribe(() => this.getRecentDetections());
    }
}

परिवर्तन का पता लगाने के अन्य तरीकों के लिए , कोणीय में मैन्युअल रूप से परिवर्तन का पता लगाना देखें

परिवर्तन का पता लगाने के वैकल्पिक तरीके हैं

ChangeDetectorRef.detectChanges()

वर्तमान घटक और उसके बच्चों के लिए तुरंत परिवर्तन का पता लगाने के लिए

ChangeDetectorRef.markForCheck()

अगली बार कोणीय रन परिवर्तन का पता लगाने के लिए वर्तमान घटक को शामिल करना

ApplicationRef.tick()

पूरे आवेदन के लिए परिवर्तन का पता लगाने के लिए


9
एक अन्य विकल्प चेंजडेक्टर रीफ को इंजेक्ट करना और cdRef.detectChanges()इसके बजाय कॉल करना है zone.run()। यह अधिक कुशल हो सकता है, क्योंकि यह पूरे घटक वृक्ष की तरह परिवर्तन का पता नहीं चलाएगा zone.run()
मार्क राजकोक

1
इस बात से सहमत। केवल तभी उपयोग करें detectChanges()जब आप जो भी बदलाव करते हैं वह घटक और उसके वंशजों के लिए स्थानीय हो। मेरी समझ यह है कि detectChanges()घटक और उसके वंश की जाँच करेगा, लेकिन zone.run()और ApplicationRef.tick()पूरे घटक पेड़ की जाँच करेगा।
मार्क राजकोक

2
वास्तव में मैं क्या देख रहा था .. का उपयोग @Input()करना समझ में नहीं आया और न ही काम किया।
योरच

15
मुझे वास्तव में यह समझ में नहीं आता है कि हमें इस सभी मैनुअल सामान की आवश्यकता क्यों है या क्यों कोणीय 2 देवताओं ने इस तरह की आवश्यकता को छोड़ दिया है। @Input का मतलब यह क्यों नहीं है कि घटक वास्तव में वास्तव में इस बात पर निर्भर करता है कि मूल घटक ने अपने बदलते डेटा पर निर्भर जो कुछ भी कहा है? यह बेहद अशुभ लगता है कि यह सिर्फ काम नहीं करता है। मैं क्या खो रहा हूँ?

13
मेरे लिए काम किया। लेकिन यह एक और उदाहरण है कि क्यों मुझे लग रहा है कि कोणीय फ्रेमवर्क डेवलपर्स कोणीय ढांचे की जटिलता में खो गए हैं। कोणीय का उपयोग एक एप्लिकेशन डेवलपर के रूप में करने के लिए, आपको यह समझने के लिए बहुत समय बिताना होगा कि कोणीय ऐसा कैसे करता है या कैसे करता है और ऊपर बताई गई चीजों को कैसे हल करता है। Terrrible !!
कार्ल

32

यह मूल रूप से @Mark Rajcok की टिप्पणियों में एक उत्तर है, लेकिन मैं इसे यहां परीक्षण के रूप में रखना चाहता हूं और ChangeDetectorRef का उपयोग करके समाधान के रूप में काम किया , मुझे यहां एक अच्छा बिंदु दिखाई देता है:

एक और विकल्प इंजेक्शन ChangeDetectorRefऔर कॉल के cdRef.detectChanges()बजाय है zone.run()। यह अधिक कुशल हो सकता है, क्योंकि यह पूरे घटक वृक्ष की तरह परिवर्तन का पता नहीं चलाएगा zone.run()। - मार्क राजकोक

तो कोड की तरह होना चाहिए:

import {Component, OnInit, ChangeDetectorRef} from 'angular2/core';

export class RecentDetectionComponent implements OnInit {

    recentDetections: Array<RecentDetection>;

    constructor(private cdRef: ChangeDetectorRef, // <== added
        private recentDetectionService: RecentDetectionService) {
        this.recentDetections = new Array<RecentDetection>();
    }

    getRecentDetections(): void {
        this.recentDetectionService.getJsonFromApi()
            .subscribe(recent => { 
                this.recentDetections = recent;
                console.log(this.recentDetections[0].macAddress);
                this.cdRef.detectChanges(); // <== added
            });
    }

    ngOnInit() {
        this.getRecentDetections();
        let timer = Observable.timer(2000, 5000);
        timer.subscribe(() => this.getRecentDetections());
    }
}

संपादित करें : उप-वर्ग के.detectChanges() अंदर का उपयोग करके समस्या को नष्ट करने के प्रयास के लिए जारी किया जा सकता है : पता लगाना

unsubscribeघटक को नष्ट करने से पहले आपको इसे हल करने की आवश्यकता है , इसलिए पूर्ण कोड इस प्रकार होगा:

import {Component, OnInit, ChangeDetectorRef, OnDestroy} from 'angular2/core';

export class RecentDetectionComponent implements OnInit, OnDestroy {

    recentDetections: Array<RecentDetection>;
    private timerObserver: Subscription;

    constructor(private cdRef: ChangeDetectorRef, // <== added
        private recentDetectionService: RecentDetectionService) {
        this.recentDetections = new Array<RecentDetection>();
    }

    getRecentDetections(): void {
        this.recentDetectionService.getJsonFromApi()
            .subscribe(recent => { 
                this.recentDetections = recent;
                console.log(this.recentDetections[0].macAddress);
                this.cdRef.detectChanges(); // <== added
            });
    }

    ngOnInit() {
        this.getRecentDetections();
        let timer = Observable.timer(2000, 5000);
        this.timerObserver = timer.subscribe(() => this.getRecentDetections());
    }

    ngOnDestroy() {
        this.timerObserver.unsubscribe();
    }

}

this.cdRef.detectChanges (); मेरी समस्या ठीक कर दी। धन्यवाद!
मंगेश

आपके सुझाव के लिए धन्यवाद, लेकिन कॉल के कई प्रयासों के बाद मुझे त्रुटि मिलती है: विफलता: त्रुटि: ViewDestroyedError: नष्ट किए गए दृश्य का उपयोग करने का प्रयास करें: DetChanges मेरे लिए Zone.run () मुझे इस त्रुटि से बाहर आने में मदद करता है।
शिवम् श्रीवास्तव

8

उपयोग करने का प्रयास करें @Input() recentDetections: Array<RecentDetection>;

EDIT: कारण@Input()महत्वपूर्ण है क्योंकि आप टाइपस्क्रिप्ट / जावास्क्रिप्ट फ़ाइल में मान को व्यू (html) में बाँधना चाहते हैं। यदि@Input()डेकोरेटर केसाथ घोषित एक मूल्यबदल गया है, तो दृश्य खुद को अपडेट करेगा। यदि एक@Input()या@Output()डेकोरेटर को बदल दिया जाता है, तोngOnChanges-event ट्रिगर हो जाएगा, और दृश्य नए मूल्य के साथ खुद को अपडेट करेगा। आप कह सकते हैं कि@Input()वसीयत दो तरह से मूल्य को बांधती है।

कोणीय द्वारा इस लिंक पर इनपुट के लिए खोज: अधिक जानकारी के लिए शब्दावली

संपादित करें: कोणीय 2 विकास के बारे में अधिक जानने के बाद, मुझे लगा कि@Input()वास्तव में इसका समाधान नहीं है, और जैसा कि टिप्पणियों में बताया गया है,

@Input() केवल तभी लागू होता है जब घटक के बाहर से डेटा-बाइंडिंग द्वारा डेटा को परिवर्तित किया जाता है (तब माता-पिता में बाध्य डेटा परिवर्तित नहीं होता है) जब डेटा को घटक के भीतर कोड से बदला जाता है।

यदि आपके पास @ गुंटर का जवाब है तो यह समस्या का अधिक सटीक और सही समाधान है। मैं अभी भी इस उत्तर को यहां रखूंगा, लेकिन कृपया गुंटर के उत्तर को सही मानें।


2
वह यह था। मैंने पहले @ इंपुट () एनोटेशन देखा था, लेकिन इसे हमेशा फॉर्म इनपुट के साथ जोड़ा, कभी नहीं सोचा था कि मुझे यहां इसकी आवश्यकता होगी। चीयर्स।
मैट वॉटसन

1
@ जॉन एडिटोनल स्पष्टीकरण के लिए धन्यवाद। लेकिन आपने जो जोड़ा है वह केवल तभी लागू होता है जब घटक के बाहर से डेटा-बाइंडिंग द्वारा डेटा को परिवर्तित किया जाता है (जब पैरेंट में डेटा को परिवर्तित किया जाता है) तब नहीं जब डेटा को घटक के भीतर कोड से बदला जाता है।
गुंटर ज़ोचबॉयर

@Input()कुंजी शब्द का उपयोग करते समय एक संपत्ति बंधन जुड़ा हुआ है, और यह बंधन सुनिश्चित करता है कि मूल्य दृश्य में अपडेट किया गया है। मान जीवन चक्र की घटना का हिस्सा होगा। इस पोस्ट में की @Input()-वर्ड के बारे में कुछ और जानकारी दी गई है
John

मैं हार मान लूँगा। मैं यह नहीं देखता कि @Input()यहाँ कहाँ शामिल है या यह क्यों होना चाहिए और यह प्रश्न पर्याप्त जानकारी प्रदान नहीं करता है। वैसे भी आप के लिए धन्यवाद :)
गुंटर Zöchbauer

3
@Input()अधिक समाधान है जो समस्या की जड़ को छुपाता है। मुद्दों को क्षेत्रों और परिवर्तन का पता लगाने के साथ संबंधित होना चाहिए।
जादू

2

मेरे मामले में, मुझे एक समान समस्या थी। मैं एक फ़ंक्शन के अंदर अपने विचार को अपडेट कर रहा था जिसे एक मूल घटक द्वारा बुलाया जा रहा था, और मेरे मूल घटक में मैं @ViewChild (NameOfMyChieldComponent) का उपयोग करना भूल गया। इस बेवकूफी भरी गलती के लिए मुझे कम से कम 3 घंटे का समय लगा। अर्थात: मुझे उन विधियों में से किसी का उपयोग करने की आवश्यकता नहीं थी :

  • ChangeDetectorRef.detectChanges ()
  • ChangeDetectorRef.markForCheck ()
  • ApplicationRef.tick ()

1
क्या आप बता सकते हैं कि आपको अपना बच्चा घटक @ViewChild के साथ कैसे अपडेट किया गया? धन्यवाद
लुकास कोलंबो

मुझे संदेह है, माता-पिता एक बच्चे की कक्षा का एक तरीका कह रहे थे जो प्रदान नहीं किया गया था, और केवल स्मृति में अस्तित्व में था
ग्लूकी

1

जोन और परिवर्तन का पता लगाने से निपटने के बजाय - AsyncPipe को जटिलता से निपटने दें। यह अवलोकन योग्य सदस्यता, सदस्यता (स्मृति रिसाव को रोकने के लिए) और कोणीय कंधों पर परिवर्तन का पता लगाएगा।

अवलोकन योग्य बनाने के लिए अपनी कक्षा बदलें, जो नए अनुरोधों के परिणामों का उत्सर्जन करेगा:

export class RecentDetectionComponent implements OnInit {

    recentDetections$: Observable<Array<RecentDetection>>;

    constructor(private recentDetectionService: RecentDetectionService) {
    }

    ngOnInit() {
        this.recentDetections$ = Observable.interval(5000)
            .exhaustMap(() => this.recentDetectionService.getJsonFromApi())
            .do(recent => console.log(recent[0].macAddress));
    }
}

और AsyncPipe का उपयोग करने के लिए अपना दृष्टिकोण अपडेट करें:

<tr *ngFor="let detected of recentDetections$ | async">
    ...
</tr>

जोड़ना चाहते हैं, कि एक विधि के साथ एक सेवा करना बेहतर है जो intervalतर्क लेगा , और:

  • नए अनुरोध बनाएं ( exhaustMapऊपर दिए गए कोड की तरह उपयोग करके );
  • अनुरोधों की त्रुटियों को संभालना;
  • ऑफ़लाइन होते हुए नए अनुरोध करने से ब्राउज़र को रोकें।
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.