javascript - कोणीय 2-जांच के बाद अभिव्यक्ति बदल गई है-आकार बदलने के साथ div चौड़ाई के लिए बाध्यकारी
angular responsive-design (2)
मैंने इस त्रुटि पर कुछ पढ़ाई और जांच की है, लेकिन यह सुनिश्चित नहीं है कि मेरी स्थिति के लिए सही उत्तर क्या है। मैं समझता हूं कि देव मोड में, पहचान का पता दो बार चलता है, लेकिन मैं समस्या को हल करने के लिए enableProdMode()
का उपयोग करने में अनिच्छुक हूं।
यहां एक साधारण उदाहरण दिया गया है जहां तालिका में कोशिकाओं की संख्या बढ़नी चाहिए क्योंकि div की चौड़ाई फैली हुई है। (ध्यान दें कि div की चौड़ाई केवल स्क्रीन चौड़ाई का कार्य नहीं है, इसलिए @Media आसानी से लागू नहीं किया जा सकता है)
मेरा HTML निम्नानुसार दिखता है (widget.template.html):
<div #widgetParentDiv class="Content">
<p>Sample widget</p>
<table><tr>
<td>Value1</td>
<td *ngIf="widgetParentDiv.clientWidth>350">Value2</td>
<td *ngIf="widgetParentDiv.clientWidth>700">Value3</td>
</tr></table>
यह अपने आप पर कुछ भी नहीं करता है। मुझे लगता है कि ऐसा इसलिए है क्योंकि परिवर्तन का पता लगाने के कारण कुछ भी नहीं हो रहा है। हालांकि, जब मैं निम्न पंक्ति को निम्न पंक्ति में बदलता हूं, और कॉल प्राप्त करने के लिए एक खाली फ़ंक्शन बनाता हूं, तो यह काम करना शुरू कर देता है, लेकिन कभी-कभी मुझे 'त्रुटि जांचने के बाद अभिव्यक्ति बदल जाती है'
<div #widgetParentDiv class="Content">
gets replaced with
<div #widgetParentDiv (window:resize)=parentResize(10) class="Content">
मेरा सबसे अच्छा अनुमान यह है कि इस संशोधन के साथ, पता लगाने का पता लगाना शुरू हो गया है और सबकुछ जवाब देना शुरू कर देता है, हालांकि, जब चौड़ाई तेजी से बदलती है तो अपवाद फेंक दिया जाता है क्योंकि परिवर्तन की पहचान के पिछले पुनरावृत्ति को div की चौड़ाई बदलने से पहले पूरा करने में अधिक समय लगता है।
- क्या परिवर्तन पहचान को ट्रिगर करने के लिए कोई बेहतर तरीका है?
- क्या मुझे परिवर्तन का पता लगाने के लिए एक समारोह के माध्यम से आकार बदलने की घटना को कैप्चर करना चाहिए?
- स्वीकृत div की चौड़ाई तक पहुंचने के लिए #widthParentDiv का उपयोग कर रहा है?
- क्या कोई बेहतर समग्र समाधान है?
मेरी परियोजना पर अधिक जानकारी के लिए कृपया इसी तरह के प्रश्न देखें।
धन्यवाद
अन्य तरीकों से मैंने इसे हल करने के लिए उपयोग किया:
import { Component, ChangeDetectorRef } from '@angular/core';
@Component({
selector: 'your-seelctor',
template: 'your-template',
})
export class YourComponent{
constructor(public cdRef:ChangeDetectorRef) { }
ngAfterViewInit() {
this.cdRef.detectChanges();
}
}
अपनी समस्या को हल करने के लिए, आपको प्रत्येक आकार बदलने की घटना के बाद किसी घटक प्रॉपर्टी में div
के आकार को प्राप्त करने और स्टोर करने की आवश्यकता होती है, और उस प्रॉपर्टी का उपयोग टेम्पलेट में करें। इस तरह, मूल्य निरंतर रहेगा जब परिवर्तन का दूसरा दौर देव मोड में चलता है।
मैं आपके टेम्पलेट में जोड़ने (window:resize)
बजाय @HostListener
का उपयोग करने की भी सलाह देता हूं। div
संदर्भ प्राप्त करने के लिए हम @ViewChild
का उपयोग करेंगे। और प्रारंभिक मान सेट करने के लिए हम जीवन चक्र हुक ngAfterViewInit()
का उपयोग करेंगे।
import {Component, ViewChild, HostListener} from '@angular/core';
@Component({
selector: 'my-app',
template: `<div #widgetParentDiv class="Content">
<p>Sample widget</p>
<table><tr>
<td>Value1</td>
<td *ngIf="divWidth > 350">Value2</td>
<td *ngIf="divWidth > 700">Value3</td>
</tr>
</table>`,
})
export class AppComponent {
divWidth = 0;
@ViewChild('widgetParentDiv') parentDiv:ElementRef;
@HostListener('window:resize') onResize() {
// guard against resize before view is rendered
if(this.parentDiv) {
this.divWidth = this.parentDiv.nativeElement.clientWidth;
}
}
ngAfterViewInit() {
this.divWidth = this.parentDiv.nativeElement.clientWidth;
}
}
बहुत बुरा है कि काम नहीं करता है। हमें मिला
जांच के बाद अभिव्यक्ति बदल गई है। पिछला मान: 'झूठा'। वर्तमान मूल्य: 'सत्य'।
त्रुटि हमारे NgIf
अभिव्यक्तियों के बारे में शिकायत कर रही है - पहली बार यह चलती है, divWidth
0 है, तो ngAfterViewInit()
चलता है और मान को 0 के अलावा किसी अन्य चीज़ में बदलता है, फिर परिवर्तन पहचान का दूसरा दौर चलता है (देव मोड में)। शुक्र है, एक आसान / ज्ञात समाधान है, और यह एक बार एकमात्र मुद्दा है, ओपी में जारी एक जारी मुद्दा नहीं:
ngAfterViewInit() {
// wait a tick to avoid one-time devMode
// unidirectional-data-flow-violation error
setTimeout(_ => this.divWidth = this.parentDiv.nativeElement.clientWidth);
}
ध्यान दें कि एक तकनीक का इंतजार करने की यह तकनीक यहां प्रलेखित है: https://angular.io/docs/ts/latest/cookbook/component-communication.html#!#parent-to-view-child
अक्सर, ngAfterViewInit()
और ngAfterViewChecked()
हमें setTimeout()
चाल को नियोजित करने की आवश्यकता होगी क्योंकि घटक के दृश्य के बाद इन विधियों को कॉल किया जाता है।
यहां एक काम करने वाला plunker ।
हम इसे बेहतर बना सकते हैं। मुझे लगता है कि हमें आकार बदलने की घटनाओं को थ्रॉटल करना चाहिए जैसे कि कोणीय परिवर्तन का पता लगाना, हर 100-250 एमएमएस कहता है, बल्कि हर बार एक आकार बदलने का आयोजन होता है। जब उपयोगकर्ता विंडो का आकार बदल रहा है, तो ऐप को आलसी होने से रोकना चाहिए, क्योंकि अभी, प्रत्येक आकार बदलने की घटना में परिवर्तन का पता लगाने का कारण बनता है (देव मोड में दो बार)। आप पिछले प्लंकर को निम्न विधि जोड़कर इसे सत्यापित कर सकते हैं:
ngDoCheck() {
console.log('change detection');
}
@HostListener
घटनाओं को आसानी से थ्रोटल कर सकते हैं, इसलिए आकार बदलने के लिए @HostListener
का उपयोग करने के बजाय, हम एक अवलोकन कर सकते हैं:
Observable.fromEvent(window, 'resize')
.throttleTime(200)
.subscribe(_ => this.divWidth = this.parentDiv.nativeElement.clientWidth );
यह काम करता है, लेकिन ... इसके साथ प्रयोग करते समय, मैंने कुछ बहुत ही रोचक खोज की ... भले ही हम आकार बदलने की घटना को थ्रॉटल करते हैं, फिर भी एंजुलर चेंज डिटेक्शन तब भी चलता है जब एक आकार बदलने का आयोजन होता है। हां, थ्रॉटलिंग इस बात को प्रभावित नहीं करती है कि पहचान कितनी बार बदलती है। (टोबीस बॉश ने इसकी पुष्टि की: https://github.com/angular/angular/issues/1773#issuecomment-102078250 ।)
अगर घटना थ्रॉटल समय से गुजरती है तो मैं केवल रन को पहचानने के लिए बदलना चाहता हूं। और मुझे इस घटक पर चलाने के लिए केवल परिवर्तन पहचान की आवश्यकता है। समाधान कोणीय क्षेत्र के बाहर देखने योग्य बनाने के लिए है, फिर सदस्यता कॉलबैक के अंदर मैन्युअल रूप से परिवर्तन पहचान को कॉल करें:
constructor(private ngzone: NgZone, private cdref: ChangeDetectorRef) {}
ngAfterViewInit() {
// set initial value, but wait a tick to avoid one-time devMode
// unidirectional-data-flow-violation error
setTimeout(_ => this.divWidth = this.parentDiv.nativeElement.clientWidth);
this.ngzone.runOutsideAngular( () =>
Observable.fromEvent(window, 'resize')
.throttleTime(200)
.subscribe(_ => {
this.divWidth = this.parentDiv.nativeElement.clientWidth;
this.cdref.detectChanges();
})
);
}
यहां एक काम करने वाला plunker ।
प्लंकर में मैंने एक counter
जोड़ा जो जीवनशैली हुक ngDoCheck()
का उपयोग करके प्रत्येक परिवर्तन पहचान चक्र को ngDoCheck()
। आप देख सकते हैं कि इस विधि को नहीं कहा जा रहा है - प्रतिबिंबित घटनाओं पर काउंटर वैल्यू नहीं बदलता है।
detectChanges()
इस घटक और उसके बच्चों पर परिवर्तन का पता चलाएगा। यदि आप रूट घटक से परिवर्तन पहचान को चलाएंगे (यानी, एक पूर्ण परिवर्तन पहचान जांच चलाएं) तो इसके बजाय ApplicationRef.tick()
उपयोग करें (यह प्लंकर में टिप्पणी की गई है)। ध्यान दें कि tick()
ngDoCheck()
को कॉल करने का कारण बन जाएगा।
यह एक बड़ा सवाल है। मैंने अलग-अलग समाधानों की कोशिश करने में बहुत समय बिताया और मैंने बहुत कुछ सीखा। इस सवाल को पोस्ट करने के लिए धन्यवाद।