अगर (($ स्कोप। $$ चरण) $ स्कोप का उपयोग क्यों किया जा रहा है। $ लागू () एक विरोधी पैटर्न!


92

कभी-कभी मुझे $scope.$applyअपने कोड में उपयोग करने की आवश्यकता होती है और कभी-कभी यह "पहले से ही प्रगति में पचा" त्रुटि फेंकता है। इसलिए मैंने इसके चारों ओर एक रास्ता ढूंढना शुरू किया और यह प्रश्न पाया: एंगुलरजेएस: $ स्कोप को बुलाकर प्रगति में पहले से ही $ पचाने को रोकें। $ लागू करें () । हालांकि टिप्पणियों में (और कोणीय विकी पर) आप पढ़ सकते हैं:

अगर (! $ स्कोप। $$ फेज) $ स्कोप नहीं है। $ apply।

तो अब मेरे दो सवाल हैं:

  1. वास्तव में यह एक प्रतिमान क्यों है?
  2. मैं सुरक्षित रूप से $ गुंजाइश का उपयोग कैसे कर सकता हूं।

"पहले से ही प्रगति में डाइजेस्ट" को रोकने के लिए एक और "समाधान" त्रुटि $ टाइमआउट का उपयोग कर रहा है:

$timeout(function() {
  //...
});

क्या वह रास्ता है? क्या यह सुरक्षित है? तो यहां असली सवाल है: मैं "प्रगति में पहले से ही पचा" त्रुटि की संभावना को पूरी तरह से कैसे समाप्त कर सकता हूं ?

पुनश्च: मैं केवल $ गुंजाइश का उपयोग कर रहा हूँ। $ गैर-कोणीयुलर कॉलबैक में लागू होते हैं जो समकालिक नहीं होते हैं। (जहाँ तक मैं जानता हूँ कि वे परिस्थितियाँ हैं जहाँ आपको $ गुंजाइश का उपयोग करना चाहिए। यदि आप चाहते हैं कि आपके परिवर्तन लागू हों तो $ लागू करें)


मेरे अनुभव से, आपको हमेशा पता होना चाहिए, यदि आप scopeकोणीय के भीतर से या कोणीय के बाहर से हेरफेर कर रहे हैं । तो इसके अनुसार आप हमेशा जानते हैं, अगर आपको कॉल करने की आवश्यकता है scope.$applyया नहीं। और यदि आप दोनों कोणीय / गैर-कोणीय scopeहेरफेर के लिए समान कोड का उपयोग कर रहे हैं, तो आप इसे गलत कर रहे हैं, इसे हमेशा अलग किया जाना चाहिए ... इसलिए मूल रूप से यदि आप किसी ऐसे मामले में भाग लेते हैं जहां आपको जांच करने की आवश्यकता है scope.$$phase, तो आपका कोड नहीं है एक सही तरीके से डिज़ाइन किया गया है, और हमेशा इसे 'सही तरीके से' करने का एक तरीका है
doodeec

1
मैं केवल गैर-कोणीय कॉलबैक (!) में इसका उपयोग कर रहा हूं, यही कारण है कि मैं भ्रमित हूं
डोमिनिक गोल्टरमैन

2
अगर यह गैर-कोणीय होता, तो यह digest already in progressत्रुटि नहीं करता
Doodeec

1
बिल्कुल यही मैने सोचा। बात यह है: यह हमेशा त्रुटि फेंक नहीं करता है। केवल एक बार एक समय में। मेरा संदेह यह है कि आवेदन एक और डाइजेस्ट के साथ बाय चांस से टकराता है। क्या यह संभव है?
डोमिनिक गोल्टरमैन

मुझे नहीं लगता कि यह संभव है अगर कॉलबैक सख्ती से गैर-कोणीय हो
डोडेक

जवाबों:


113

कुछ और खुदाई के बाद मैं इस सवाल को हल करने में सक्षम था कि क्या यह हमेशा उपयोग करने के लिए सुरक्षित है $scope.$apply। छोटा जवाब हां है।

लंबा जवाब:

आपके ब्राउज़र ने जावास्क्रिप्ट को कैसे निष्पादित किया है, इसके कारण यह संभव नहीं है कि दो डाइजेस्ट कॉल संयोग से टकरा जाएं ।

हमारे द्वारा लिखा गया जावास्क्रिप्ट कोड सभी एक बार में नहीं चलता है, इसके बजाय यह घुमावों में निष्पादित होता है। इनमें से प्रत्येक मोड़ शुरू से अंत तक निर्बाध रूप से चलता है, और जब एक मोड़ चल रहा होता है, तो हमारे ब्राउज़र में और कुछ नहीं होता है। ( http://jimhoskins.com/2012/12/17/angularjs-and-apply.html से )

इसलिए त्रुटि "पहले से ही प्रगति में पचती है" केवल एक स्थिति में हो सकती है: जब एक $ लागू दूसरे $ लागू के अंदर जारी किया जाता है, जैसे:

$scope.apply(function() {
  // some code...
  $scope.apply(function() { ... });
});

यह स्थिति तब उत्पन्न नहीं हो सकती है जब हम $ गुंजाइश का उपयोग करते हैं। शुद्ध गैर-कोणीयज कॉलबैक में उदाहरण के लिए कॉलबैक setTimeout। तो निम्न कोड 100% बुलेटप्रूफ है और ए करने की कोई आवश्यकता नहीं हैif (!$scope.$$phase) $scope.$apply()

setTimeout(function () {
    $scope.$apply(function () {
        $scope.message = "Timeout called!";
    });
}, 2000);

यहां तक ​​कि यह एक सुरक्षित है:

$scope.$apply(function () {
    setTimeout(function () {
        $scope.$apply(function () {
            $scope.message = "Timeout called!";
        });
    }, 2000);
});

क्या सुरक्षित नहीं है (क्योंकि $ समयबाह्य - जैसे सभी कोणीय हेल्पर्स - पहले से ही $scope.$applyआपके लिए कॉल ):

$timeout(function () {
    $scope.$apply(function () {
        $scope.message = "Timeout called!";
    });
}, 2000);

यह भी बताता है कि क्यों का उपयोग if (!$scope.$$phase) $scope.$apply()एक विरोधी पैटर्न है। यदि आप $scope.$applyसही तरीके से उपयोग करते हैं तो आपको इसकी आवश्यकता नहीं है : setTimeoutउदाहरण के लिए शुद्ध जेएस कॉलबैक में ।

अधिक विस्तृत विवरण के लिए http://jimhoskins.com/2012/12/17/angularjs-and-apply.html पढ़ें ।


मुझे एक उदाहरण मिला, जहां मैं एक सेवा का निर्माण करता $document.bind('keydown', function(e) { $rootScope.$apply(function() { // a passed through function from the controller gets executed here }); });हूं जिसके साथ मुझे वास्तव में नहीं पता कि मुझे यहां $ आवेदन क्यों करना है, क्योंकि मैं $ document.bind का उपयोग कर रहा हूं ..
बेट्टी सेंट

क्योंकि $ दस्तावेज़ केवल "ब्राउज़र की विंडो के लिए एक jQuery या jqLite आवरण है। वस्तु ऑब्जेक्ट।" और इस प्रकार लागू किया गया है: function $DocumentProvider(){ this.$get = ['$window', function(window){ return jqLite(window.document); }]; }वहाँ कोई आवेदन नहीं है।
डोमिनिक गोल्टरमैन

11
$timeoutशब्दार्थ का अर्थ है देरी के बाद चलने वाला कोड। यह एक कार्यात्मक रूप से सुरक्षित चीज़ हो सकती है, लेकिन यह एक हैक है। $ लागू का उपयोग करने का एक सुरक्षित तरीका होना चाहिए जब आप यह जानने में असमर्थ हों कि क्या $digestचक्र चल रहा है या आप पहले से ही अंदर हैं $apply
जॉन स्ट्राइकर

1
एक और कारण है कि इसका बुरा क्यों है: यह आंतरिक चर ($ $ चरण) का उपयोग करता है जो सार्वजनिक एपीआई का हिस्सा नहीं हैं और वे कोणीय के नए संस्करण में बदल सकते हैं और इस प्रकार आपके कोड को तोड़ सकते हैं। समवर्ती घटना ट्रिगर के साथ आपकी समस्या हालांकि दिलचस्प है
डोमिनिक गोलटरमैन

4
नए दृष्टिकोण का उपयोग $ गुंजाइश का उपयोग करना है। $ evalAsync () जो संभव हो या अगले चक्र में वर्तमान पाचन चक्र में सुरक्षित रूप से निष्पादित करता है। संदर्भ लें bennadel.com/blog/…
jaymjarri

16

यह निश्चित रूप से अब एक विरोधी पैटर्न है। अगर आपने $ $ चरण की जांच की तो भी मुझे एक पाचन झटका लगा है। आप $$उपसर्गों द्वारा निरूपित आंतरिक एपीआई तक पहुँचने के लिए नहीं हैं ।

आपको उपयोग करना चाहिए

 $scope.$evalAsync();

जैसा कि यह Angular ^ 1.4 में पसंदीदा तरीका है और विशेष रूप से एप्लिकेशन लेयर के लिए API के रूप में सामने आता है।


9

किसी भी मामले में जब आपका पाचन प्रगति में है और आप पचाने के लिए किसी अन्य सेवा को धक्का देते हैं, तो यह बस एक त्रुटि देता है अर्थात पहले से ही प्रगति में पचता है। इसलिए इसे ठीक करने के लिए आपके पास दो विकल्प हैं। आप मतदान जैसी प्रगति में किसी भी पचाने के लिए जाँच कर सकते हैं।

पेहला

if ($scope.$root.$$phase != '$apply' && $scope.$root.$$phase != '$digest') {
    $scope.$apply();
}

यदि उपरोक्त शर्त सही है, तो आप अपना $ दायरा लागू कर सकते हैं

दूसरे समाधान का उपयोग $ टाइमआउट है

$timeout(function() {
  //...
})

यह दूसरे को पचाने में $ $ समय पूरा नहीं होने देगा।


1
downvoted; यह प्रश्न विशेष रूप से पूछता है कि आप जिस चीज़ का वर्णन यहाँ कर रहे हैं, उसे करने के लिए क्यों नहीं, इसके चारों ओर हैक करने का दूसरा तरीका नहीं है। जब उपयोग करने के लिए @gaul द्वारा उत्कृष्ट उत्तर देखें $scope.$apply();
प्योरसाइडर

हालांकि सवाल का जवाब नहीं: $timeoutकुंजी है! यह काम करता है और बाद में मैंने पाया कि यह भी सिफारिश की है।
हिमल नाग राणा

मुझे पता है कि इस 2 साल बाद टिप्पणी करने के लिए काफी देर हो चुकी है, लेकिन $ समयबाह्य का उपयोग करते समय सावधान रहें, क्योंकि यह आपके प्रदर्शन में बहुत अधिक खर्च कर सकता है यदि आपके पास अच्छा आवेदन संरचना नहीं है
cpoDesign

9

scope.$applyएक $digestचक्र को ट्रिगर करता है जो 2-तरफ़ा डेटा बाइंडिंग के लिए मूलभूत है

एक $digestवस्तुओं के लिए चक्र के चेक मॉडल (सटीक होना करने के लिए यानी $watch) से जुड़ा हुआ $scopeहै, तो उनके मूल्यों को बदल दिया है का आकलन करने और अगर यह एक परिवर्तन तो पता लगाता है यह दृश्य को अपडेट करने के लिए आवश्यक कदम उठाता है।

अब जब आप उपयोग करते हैं तो आपको $scope.$apply"पहले से ही प्रगति में" एक त्रुटि का सामना करना पड़ता है, इसलिए यह काफी स्पष्ट है कि $ डाइजेस्ट चल रहा है लेकिन इससे क्या हुआ?

ans -> हर $httpकॉल, सभी एनजी-क्लिक, दोहराना, दिखाना, छिपाना आदि एक $digestचक्र को ट्रिगर करते हैं और हर $ SCOPE का सबसे अच्छा काम करता है।

अर्थात आपके पृष्ठ में 4 नियंत्रक या निर्देश A, B, C, D हैं

यदि आपके पास $scopeउनमें से प्रत्येक में 4 गुण हैं तो आपके पृष्ठ पर कुल 16 $ गुंजाइश गुण हैं।

यदि आप $scope.$applyनियंत्रक डी में ट्रिगर करते हैं तो एक $digestचक्र सभी 16 मूल्यों की जांच करेगा !!! प्लस सभी $ rootScope गुण।

उत्तर -> लेकिन बच्चे और एक ही स्कोप को $scope.$digestट्रिगर करता है $digestइसलिए यह केवल 4 गुणों की जांच करेगा। इसलिए यदि आप सुनिश्चित हैं कि D में परिवर्तन A, B, C को प्रभावित नहीं करेगा तो $scope.$digest नहीं का उपयोग करें $scope.$apply

तो एक मात्र एनजी-क्लिक या एनजी-शो / छिपाने के लिए $digest100 से अधिक गुणों पर एक चक्र को ट्रिगर किया जा सकता है, भले ही उपयोगकर्ता ने किसी भी घटना को निकाल नहीं दिया हो !


2
हाँ मुझे यह देर से परियोजना में दुर्भाग्य से महसूस हुआ। अगर मैं शुरू से यह जानता था तो कोणीय का उपयोग नहीं किया जाएगा। सभी मानक निर्देश एक $ गुंजाइश आग। $ लागू होते हैं, जो बदल जाता है $ rootScope कॉल। $ डाइजेस्ट, जो सभी स्कोप पर गंदे चेक करता है। गरीब डिजाइन निर्णय अगर आप मुझसे पूछें। मुझे इस बात का नियंत्रण होना चाहिए कि गालियों की जांच क्या होनी चाहिए, क्योंकि मुझे पता है कि डेटा इस लिंक से जुड़ा हुआ है!
मूनस्टोम जूल

0

उपयोग करें $timeout, यह अनुशंसित तरीका है।

मेरा परिदृश्य यह है कि मुझे WebSocket से प्राप्त डेटा के आधार पर पृष्ठ पर आइटम बदलने की आवश्यकता है। और चूंकि यह $ समयबाह्य के बिना कोणीय के बाहर है, केवल मॉडल को बदल दिया जाएगा, लेकिन दृश्य नहीं। क्योंकि कोणीय को पता नहीं है कि डेटा का टुकड़ा बदल दिया गया है। $timeoutमूल रूप से $ पाचन के अगले दौर में परिवर्तन करने के लिए कोणीय को बता रहा है।

मैंने निम्नलिखित की कोशिश की और यह काम करता है। मेरे लिए अंतर यह है कि $ टाइमआउट स्पष्ट है।

setTimeout(function(){
    $scope.$apply(function(){
        // changes
    });
},0)

यह आपके सॉकेट कोड को $ लागू करने के लिए बहुत साफ है (बहुत कुछ एंगुलर कोड पर AJAX की तरह है, यानी $http)। अन्यथा आपको इस कोड को सभी जगह दोहराना होगा।
तैमूरफल

यह निश्चित रूप से अनुशंसित नहीं है। साथ ही, आपको कभी-कभार ऐसा करने में कोई त्रुटि मिलेगी यदि $ गुंजाइश $ $ चरण है। इसके बजाय, आपको $ गुंजाइश का उपयोग करना चाहिए। $ evalAsync ();
फ्लेवर्सस्केप

$scope.$applyयदि आप उपयोग कर रहे हैं setTimeoutया $timeout
कुणाल

-1

मुझे बहुत अच्छा समाधान मिला:

.factory('safeApply', [function($rootScope) {
    return function($scope, fn) {
        var phase = $scope.$root.$$phase;
        if (phase == '$apply' || phase == '$digest') {
            if (fn) {
                $scope.$eval(fn);
            }
        } else {
            if (fn) {
                $scope.$apply(fn);
            } else {
                $scope.$apply();
            }
        }
    }
}])

जहां आपको आवश्यकता हो, वहां पर इंजेक्ट करें:

.controller('MyCtrl', ['$scope', 'safeApply',
    function($scope, safeApply) {
        safeApply($scope); // no function passed in
        safeApply($scope, function() { // passing a function in
        });
    }
])
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.