AngularJS: $ स्कोप कॉल करते समय त्रुटि $ डाइजेस्ट पहले से ही प्रगति पर रोकना। $ लागू होना ()


838

मुझे लग रहा है कि मुझे कोणीय में एक आवेदन के निर्माण के बाद से अपने पृष्ठ को अपने दायरे में मैन्युअल रूप से और अधिक अपडेट करना होगा।

मुझे ऐसा करने का एकमात्र तरीका $apply()मेरे नियंत्रकों और निर्देशों के दायरे से कॉल करना है । इसके साथ समस्या यह है कि यह कंसोल के लिए एक त्रुटि फेंकता रहता है जो पढ़ता है:

त्रुटि: $ डाइजेस्ट पहले से ही प्रगति पर है

क्या किसी को पता है कि इस त्रुटि से कैसे बचें या एक ही चीज़ को प्राप्त न करें लेकिन एक अलग तरीके से?


34
यह वास्तव में निराशाजनक बात है कि हमें अधिक से अधिक $ लागू होने की आवश्यकता है।
OZ_

मुझे यह त्रुटि तब भी हो रही है, जब मैं एक कॉलबैक में $ लागू कॉल कर रहा हूं। मैं उनके सर्वर पर डेटा का उपयोग करने के लिए एक तीसरे पक्ष के पुस्तकालय का उपयोग कर रहा हूं, इसलिए मैं $ http का लाभ नहीं उठा सकता हूं, और न ही मैं चाहता हूं कि मुझे $ http का उपयोग करने के लिए उनकी लाइब्रेरी को फिर से लिखना होगा।
ट्रेवर

45
उपयोग$timeout()
ओनूर येल्ड्रिअम

6
$ समयबाह्य (fn) + 1 का उपयोग करें, यह समस्या को ठीक कर सकता है, $ गुंजाइश! $ $ चरण सबसे अच्छा समाधान नहीं है।
हूई टैन

1
केवल कोड / कॉल गुंजाइश लपेट दें। $ से लागू भीतर समय समाप्ति (नहीं $ का समय समाप्त) AJAX के कार्य (नहीं $ http) और घटनाओं (नहीं ng-*)। सुनिश्चित करें, यदि आप इसे किसी फ़ंक्शन के भीतर से कॉल कर रहे हैं (जिसे टाइमआउट / ajax / इवेंट के माध्यम से कहा जाता है), कि यह शुरू में लोड पर भी नहीं चल रहा है।
पैट्रिक

जवाबों:


660

इस पैटर्न का उपयोग न करें - यह समाप्त हो जाएगा क्योंकि इससे अधिक त्रुटियां हल होंगी। हालांकि आपको लगता है कि यह कुछ तय है, यह नहीं था।

आप जाँच कर सकते हैं कि $digestक्या पहले से जाँच चल रही है $scope.$$phase

if(!$scope.$$phase) {
  //$digest or $apply
}

$scope.$$phaseवापस आ जाएगी "$digest"या "$apply"अगर एक $digestया $applyप्रगति पर है। मेरा मानना ​​है कि इन राज्यों के बीच का अंतर $digestमौजूदा दायरे और इसके बच्चों की घड़ियों $applyको प्रोसेस करेगा और सभी स्कॉपर्स पर नजर रखने वालों को प्रोसेस करेगा।

@ Dnc253 के बिंदु पर, यदि आप स्वयं को बार $digest- $applyबार कॉल करते हुए पाते हैं, तो आप गलत कर रहे हैं। मुझे आमतौर पर लगता है कि मुझे तब पचाने की ज़रूरत है जब मुझे एंगुलर की पहुँच के बाहर DOM इवेंट फायरिंग के परिणामस्वरूप स्कोप की स्थिति को अपडेट करने की आवश्यकता होती है। उदाहरण के लिए, जब एक ट्विटर बूटस्ट्रैप मोडल छिप जाता है। कभी-कभी जब कोई $digestकार्य प्रगति पर होता है, तो कभी-कभी DOM ईवेंट नहीं होता है। इसलिए मैं इस चेक का उपयोग करता हूं।

अगर कोई किसी को जानता है तो मैं एक बेहतर तरीका जानना पसंद करूंगा।


टिप्पणियों से: @anddoutoi द्वारा

angular.js एंटी पैटर्न

  1. ऐसा मत करो if (!$scope.$$phase) $scope.$apply(), इसका मतलब $scope.$apply()है कि कॉल स्टैक में आपका पर्याप्त उच्च नहीं है।

230
मुझे लगता है जैसे $ डाइजेस्ट / $ लागू होना चाहिए डिफ़ॉल्ट रूप से ऐसा करना चाहिए
रॉय Truelove

21
ध्यान दें कि कुछ मामलों में मुझे जांच करनी है, लेकिन वर्तमान गुंजाइश और रूट गुंजाइश। मुझे रूट पर $ $ चरण के लिए मान मिल रहा है, लेकिन मेरे दायरे पर नहीं। सोचें कि इसका निर्देशन के अलग-अलग दायरे से क्या लेना-देना है, लेकिन ..
रॉय ट्रुवेलोव

106
"करना बंद करो if (!$scope.$$phase) $scope.$apply()", github.com/angular/angular.js/wiki/Anti-Patterns
anddoutoi

34
@anddoutoi: सहमत; आप लिंक कर रहे हैं यह बहुत स्पष्ट है यह समाधान नहीं है; हालांकि, मैं अनिश्चित हूं कि "आप कॉल स्टैक में पर्याप्त उच्च नहीं हैं" से क्या मतलब है। क्या आप इसका मतलब जानते है?
ट्रेवर

13
@threed: aaronfrost द्वारा उत्तर देखें। सही तरीका यह है कि अगले चक्र में पाचन को ट्रिगर करने के लिए डेफर का उपयोग किया जाए। अन्यथा घटना गुम हो जाएगी और गुंजाइश बिल्कुल भी अपडेट नहीं होगी।
मारेक

663

इस बहुत ही विषय पर कोणीय लोगों के साथ हाल ही में चर्चा से: भविष्य के प्रमाण के कारणों के लिए, आपको उपयोग नहीं करना चाहिए$$phase

जब इसे करने के लिए "सही" तरीके से दबाया जाता है, तो इसका उत्तर वर्तमान में है

$timeout(function() {
  // anything you want can go here and will safely be run on the next digest.
})

मैं हाल ही में फेसबुक, गूगल, और ट्विटर एपीआई को लपेटने के लिए कोणीय सेवाएं लिखते समय इस में भाग गया, जो अलग-अलग डिग्री के लिए, कॉलबैक में सौंप दिया है।

यहाँ एक सेवा के भीतर से एक उदाहरण है। (संक्षिप्तता के लिए, शेष सेवा - जो चर सेट करती हैं, $ समय समाप्त हो गई आदि - को छोड़ दिया गया है।)

window.gapi.client.load('oauth2', 'v2', function() {
    var request = window.gapi.client.oauth2.userinfo.get();
    request.execute(function(response) {
        // This happens outside of angular land, so wrap it in a timeout 
        // with an implied apply and blammo, we're in action.
        $timeout(function() {
            if(typeof(response['error']) !== 'undefined'){
                // If the google api sent us an error, reject the promise.
                deferred.reject(response);
            }else{
                // Resolve the promise with the whole response if ok.
                deferred.resolve(response);
            }
        });
    });
});

ध्यान दें कि $ टाइमआउट के लिए देरी तर्क वैकल्पिक है और 0 के लिए डिफ़ॉल्ट अगर सेट किए बिना छोड़ जाएगा ( $ टाइमआउट कॉल $ browser.defer जो करता है, तो देरी सेट नहीं है 0 करने के लिए डिफ़ॉल्ट )

थोड़ा गैर-सहज ज्ञान युक्त, लेकिन इसका जवाब है कि कोणीय लिखने वाले लोगों से, इसलिए यह मेरे लिए काफी अच्छा है!


5
मैं अपने निर्देशों में कई बार भाग चुका हूं। रीडैक्टर के लिए एक लिख रहा था और यह पूरी तरह से काम करने के लिए निकला। मैं ब्रैड ग्रीन के साथ एक मुलाक़ात में था और उसने कहा कि जेएस की मूल अवलोकन क्षमता का उपयोग करते हुए और बिना कमी वाले ब्राउज़र के लिए पॉलीफ़िल का उपयोग करके कोणीय 2.0 कोई पाचन चक्र के साथ विशाल होगा। उस समय हमें ऐसा करने की आवश्यकता नहीं होगी। :)
माइकल जे। काल्किंस

कल मैंने एक ऐसा मुद्दा देखा है जहाँ $ timeout के अंदर selectize.refreshItems () कॉल करने से भयानक पुनरावर्ती पाचन त्रुटि उत्पन्न हुई। किसी भी विचार कैसे हो सकता है?
इविन

3
आप का उपयोग करते हैं $timeoutबल्कि देशी की तुलना में setTimeout, तुम क्यों इस्तेमाल नहीं करते $windowदेशी के बजाय window?
लीजेई

2
@LeeGee: $timeoutइस मामले में उपयोग की बात यह है कि $timeoutयह सुनिश्चित करता है कि कोणीय दायरे को ठीक से अपडेट किया गया है। यदि $ डाइजेस्ट प्रगति पर नहीं है, तो यह नए $ डाइजेस्ट को चलाने का कारण बनेगा।
भय

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

324

पाचन चक्र एक तुल्यकालिक कॉल है। यह ब्राउज़र के इवेंट लूप पर तब तक नियंत्रण नहीं रखेगा जब तक कि यह पूरा नहीं हो जाता। इससे निपटने के लिए कुछ तरीके हैं। इससे निपटने का सबसे आसान तरीका यह है कि बिल्ट इन $ टाइमआउट का उपयोग किया जाए, और दूसरा तरीका यह है कि यदि आप अंडरस्कोर या लॉश का उपयोग कर रहे हैं (और आपको होना चाहिए), तो निम्न कॉल करें:

$timeout(function(){
    //any code in here will automatically have an apply run afterwards
});

या अगर आपने दर्ज किया है:

_.defer(function(){$scope.$apply();});

हमने कई वर्कअराउंड की कोशिश की, और हम अपने सभी नियंत्रकों, निर्देशों और यहां तक ​​कि कुछ कारखानों में $ $ रूटस्कॉप इंजेक्शन लगाने से नफरत करते थे। तो, $ टाइमआउट और _.defer अब तक हमारा पसंदीदा रहा है। ये विधियाँ अगले एनीमेशन लूप तक प्रतीक्षा करने के लिए कोणीय को सफलतापूर्वक बताती हैं, जो गारंटी देगा कि वर्तमान गुंजाइश। $ लागू लागू हो गया है।


2
क्या यह $ टाइमआउट (...) का उपयोग करने के लिए तुलनीय है? मैंने अगले घटना चक्र को टालने के लिए कई मामलों में $ समयबाह्य का उपयोग किया है और यह ठीक काम करने लगता है - किसी को भी पता है कि क्या कोई कारण नहीं है $ timeout का उपयोग करने के लिए?
ट्रेवर

9
यह वास्तव में केवल तभी उपयोग किया जाना चाहिए जब आप पहले से ही उपयोग कर रहे हों underscore.js। यह समाधान केवल अपने deferकार्य का उपयोग करने के लिए पूरे अंडरस्कोर लाइब्रेरी को आयात करने के लायक नहीं है । मैं $timeoutसमाधान को बहुत पसंद करता हूं क्योंकि सभी के पास पहले से ही $timeoutकोणीय के माध्यम से पहुंच है , अन्य पुस्तकालयों पर कोई निर्भरता के बिना।
टेनिस

10
सच ... लेकिन अगर आप अंडरस्कोर या लॉश का उपयोग नहीं कर रहे हैं ... आपको यह मूल्यांकन करने की आवश्यकता है कि आप क्या कर रहे हैं। उन दो कामवासियों ने कोड को देखने के तरीके को बदल दिया है।
ठंढा

2
हमने रेस्टैंगुलर के लिए एक निर्भरता के रूप में दर्ज किया है (हम जल्द ही एनजी मार्ग के पक्ष में रेस्टैंगुलर को खत्म करने जा रहे हैं)। मुझे लगता है कि यह एक अच्छा जवाब है, लेकिन लोगों को अंडरस्कोर / लॉश का उपयोग करना चाहते हैं यह मानना ​​बहुत अच्छा नहीं है। हर तरह से उन libs ठीक हैं ... अगर तुम उन्हें पर्याप्त उपयोग ... इन दिनों मैं ES5 तरीकों जो बाहर कारण मैं के 98% पोंछ का उपयोग करने के लिए इस्तेमाल अंडरस्कोर शामिल हैं।
ब्रैडग्रेन

2
आप सही हैं @SgtPooki मैंने $ टाइमआउट का उपयोग करने के विकल्प को शामिल करने के लिए उत्तर को भी संशोधित किया। $ समय समाप्त हो जाएगा और _.defer दोनों अगले एनीमेशन लूप तक इंतजार करेंगे, जो यह सुनिश्चित करेगा कि वर्तमान गुंजाइश। $ apply समाप्त हो गया है। मुझे ईमानदार रखने के लिए धन्यवाद, और मुझे यहाँ जवाब अपडेट करने के लिए मिल रहा है।
ठंढी

267

यहां कई उत्तरों में अच्छी सलाह दी गई है, लेकिन इससे भ्रम भी हो सकता है। सीधे शब्दों में का उपयोग कर $timeoutरहा है नहीं सबसे अच्छा है और न ही सही समाधान। इसके अलावा, यह अवश्य पढ़ें कि यदि आप प्रदर्शन या मापनीयता से संबंधित हैं।

वो बातें जो आपको पता होनी चाहिए

  • $$phase फ्रेमवर्क के लिए निजी है और उसके लिए अच्छे कारण हैं।

  • $timeout(callback)तब तक प्रतीक्षा करेंगे जब तक कि वर्तमान पाचन चक्र (यदि कोई हो) किया जाता है, तो कॉलबैक निष्पादित करें, फिर अंत में पूर्ण चलाएं $apply

  • $timeout(callback, delay, false)वही करेगा (कॉलबैक को निष्पादित करने से पहले एक वैकल्पिक देरी के साथ), लेकिन एक $apply(तीसरे तर्क) को आग नहीं देगा जो प्रदर्शन को बचाता है यदि आपने अपने कोणीय मॉडल ($ गुंजाइश) को संशोधित नहीं किया है।

  • $scope.$apply(callback)$rootScope.$digestइनवोक, अन्य बातों के अलावा, जिसका अर्थ है कि यह एप्लिकेशन और उसके सभी बच्चों के मूल दायरे को फिर से परिभाषित करेगा, भले ही आप एक अलग दायरे में हों।

  • $scope.$digest()बस अपने मॉडल को देखने के लिए सिंक करेगा, लेकिन अपने माता-पिता के दायरे को नहीं पचाएगा, जो आपके HTML के एक अलग हिस्से पर एक अलग-अलग गुंजाइश (ज्यादातर निर्देश से) के साथ काम करते समय बहुत सारे प्रदर्शन को बचा सकता है। $ डाइजेस्ट कॉलबैक नहीं लेता है: आप कोड निष्पादित करते हैं, फिर पचते हैं।

  • $scope.$evalAsync(callback)1.2 को कोणीयर्ज के साथ पेश किया गया है, और संभवतः आपकी अधिकांश परेशानियों को हल करेगा। कृपया इसके बारे में और जानने के लिए अंतिम पैराग्राफ देखें।

  • यदि आप प्राप्त करते हैं $digest already in progress error, तो आपकी वास्तुकला गलत है: या तो आपको अपने दायरे को फिर से निर्धारित करने की आवश्यकता नहीं है, या आपको उस के प्रभारी नहीं होना चाहिए (नीचे देखें)।

कैसे अपने कोड संरचना करने के लिए

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

function editModel() {
  $scope.someVar = someVal;
  /* Do not apply your scope here since we don't know if that
     function is called synchronously from Angular or from an
     asynchronous code */
}

// Processed by Angular, for instance called by a ng-click directive
$scope.applyModelSynchronously = function() {
  // No need to digest
  editModel();
}

// Any kind of asynchronous code, for instance a server request
callServer(function() {
  /* That code is not watched nor digested by Angular, thus we
     can safely $apply it */
  $scope.$apply(editModel);
});

और यदि आप जानते हैं कि आप क्या कर रहे हैं और एक बड़े कोणीय अनुप्रयोग के भाग के दौरान एक अलग छोटे निर्देश पर काम कर रहे हैं, तो आप प्रदर्शन को बचाने के लिए $ $ के बजाय $ डाइजेस्ट पसंद कर सकते हैं।

Angularjs 1.2 से अपडेट करें

एक नई, शक्तिशाली विधि को किसी भी $ दायरे में जोड़ा गया है $evalAsync:। मूल रूप से, यह वर्तमान पाचन चक्र के भीतर अपने कॉलबैक को निष्पादित करेगा यदि कोई हो रहा है, अन्यथा एक नया पाचन चक्र कॉलबैक को निष्पादित करना शुरू कर देगा।

यह तब भी उतना अच्छा नहीं है जितना $scope.$digestकि यदि आप वास्तव में जानते हैं कि आपको केवल अपने HTML के एक पृथक हिस्से को सिंक्रनाइज़ करने की आवश्यकता है (क्योंकि एक नया $applyशुरू हो जाएगा यदि कोई भी प्रगति में नहीं है), लेकिन यह सबसे अच्छा समाधान है जब आप किसी फ़ंक्शन को निष्पादित कर रहे हैं जो आप जानते नहीं कर सकते, तो सिंक्रोनस रूप से या नहीं निष्पादित किया जाएगा एक संसाधन संभावित कैश्ड प्राप्त करने में कठिनाई के बाद, उदाहरण के लिए: कभी कभी यह एक सर्वर के लिए एक async कॉल की आवश्यकता होगी, अन्यथा संसाधन तुल्यकालिक स्थानीय स्तर पर लाए जाने के लिए किया जाएगा।

इन मामलों में और अन्य सभी जहां आपके पास था !$scope.$$phase, का उपयोग करना सुनिश्चित करें$scope.$evalAsync( callback )


4
$timeoutपारित होने में आलोचनात्मक है। क्या आप बचने के और कारण दे सकते हैं $timeout?
mldev

88

इस प्रक्रिया को बनाए रखने के लिए थोड़ा सहायक तरीका हैडी:

function safeApply(scope, fn) {
    (scope.$$phase || scope.$root.$$phase) ? fn() : scope.$apply(fn);
}

6
आपकी तिजोरी ने मुझे यह समझने में मदद की कि किसी भी चीज़ की तुलना में बहुत अधिक क्या हो रहा था। पोस्ट करने के लिए धन्यवाद।
जेसन मोर

4
मैं एक ही काम करने वाला था, लेकिन इसका मतलब यह नहीं है कि एक मौका है कि हम fn () में किए गए बदलावों को $ डाइजेस्ट नहीं करेंगे? क्या फंक्शन में देरी करना बेहतर नहीं होगा, गुंजाइश मानकर। $$ फेज === '$ डाइजेस्ट'?
स्पेंसर अल्जीरिया

मैं सहमत हूं, कभी-कभी $ $ () का उपयोग पाचन को ट्रिगर करने के लिए किया जाता है, बस fn को खुद से कॉल करना ... क्या इसका परिणाम समस्या नहीं होगा?
CMCDragonkai

1
मुझे लगता है कि scope.$apply(fn);होना चाहिए scope.$apply(fn());क्योंकि fn () फ़ंक्शन को निष्पादित करेगा और fn को नहीं। कृपया मेरी मदद करें जहां मैं गलत हूं
madhu131313

1
@ZenOut $ लागू करने के लिए कॉल कार्यों सहित कई विभिन्न प्रकार के तर्कों का समर्थन करता है। यदि कोई फ़ंक्शन पास किया गया है, तो यह फ़ंक्शन का मूल्यांकन करता है।
बॉक्समेइन

33

मुझे उदाहरण के लिए कोडमिरर जैसी तीसरी पार्टियों की लिपियों और क्रैपानो के साथ भी यही समस्या थी, और यहां तक ​​कि यहां बताए गए सुरक्षित तरीकों का उपयोग करने से मेरे लिए त्रुटि हल नहीं हुई।

लेकिन क्या हल किया है यह $ timeout सेवा का उपयोग कर रहा है (पहले इसे इंजेक्ट करना न भूलें)।

इस प्रकार, कुछ इस तरह:

$timeout(function() {
  // run my code safely here
})

और अगर आपके कोड के अंदर आप उपयोग कर रहे हैं

इस

शायद क्योंकि यह एक कारखाने के निर्देशक के नियंत्रक के अंदर है या बस किसी तरह के बंधन की आवश्यकता है, तो आप कुछ ऐसा करेंगे:

.factory('myClass', [
  '$timeout',
  function($timeout) {

    var myClass = function() {};

    myClass.prototype.surprise = function() {
      // Do something suprising! :D
    };

    myClass.prototype.beAmazing = function() {
      // Here 'this' referes to the current instance of myClass

      $timeout(angular.bind(this, function() {
          // Run my code safely here and this is not undefined but
          // the same as outside of this anonymous function
          this.surprise();
       }));
    }

    return new myClass();

  }]
)

32

Http://docs.angularjs.org/error/$rootScope:inprog देखें

समस्या तब उत्पन्न होती है जब आपके पास कॉल होता $applyहै जो कभी-कभी कोणीय कोड के बाहर अतुल्यकालिक रूप से चलाया जाता है (जब $ लागू का उपयोग किया जाना चाहिए) और कभी-कभी तुल्यकालिक रूप से कोणीय कोड के अंदर होता है (जो $digest already in progressत्रुटि का कारण बनता है )।

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

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

समाधान

संक्षेप में, ऐसा करने के बजाय:

... your controller code...

$http.get('some/url', function(data){
    $scope.$apply(function(){
        $scope.mydate = data.mydata;
    });
});

... more of your controller code...

यह करो:

... your controller code...

$http.get('some/url', function(data){
    $timeout(function(){
        $scope.mydate = data.mydata;
    });
});

... more of your controller code...

केवल तभी कॉल करें $applyजब आप जानते हैं कि यह कोड चलने वाला कोड हमेशा कोणीय कोड के बाहर चलाया जाएगा (जैसे कि आपकी कॉल $ अप्लाई एक कॉलबैक के अंदर होगी जिसे आपके कोणीय कोड के बाहर कोड कहा जाता है)।

जब तक किसी व्यक्ति को $timeoutओवर इस्तमाल करने के कुछ प्रभावशाली नुकसान के बारे में पता $applyनहीं होता है , मैं यह नहीं देखता कि आप $timeoutइसके बजाय हमेशा (शून्य विलंब के साथ) का उपयोग क्यों नहीं कर सकते $apply, क्योंकि यह लगभग एक ही काम करेगा।


धन्यवाद, इसने मेरे मामले के लिए काम किया जहां मैं $applyखुद को नहीं बुला रहा हूं लेकिन फिर भी त्रुटि हो रही है।
ariscris

5
मुख्य अंतर यह है कि $applyसमकालिक (इसके कॉलबैक को निष्पादित किया जाता है, फिर $ लागू होने वाला कोड) जबकि $timeoutऐसा नहीं है: टाइमआउट के बाद वर्तमान कोड निष्पादित किया जाता है, फिर एक नया स्टैक इसकी कॉलबैक से शुरू होता है, जैसे कि आप उपयोग कर रहे थे setTimeout। यदि आप एक ही मॉडल को दो बार अपडेट कर रहे थे तो ग्राफिक ग्लिच हो सकता है: $timeoutफिर से अपडेट करने से पहले रिफ्रेश होने के लिए व्यू का इंतजार करेंगे।
फ्लोरिनॉन

धन्यवाद वास्तव में, नस्ल। मेरे पास $ $ घड़ी गतिविधि के परिणामस्वरूप एक विधि थी, और मेरे बाहरी फ़िल्टर को निष्पादित करने से पहले यूआई को अपडेट करने की कोशिश कर रहा था। यह कहते हुए कि $ टाइमआउट फंक्शन के अंदर मेरे लिए काम किया।
djmarquette

28

जब आपको यह त्रुटि मिलती है, तो मूल रूप से इसका मतलब है कि यह पहले से ही आपके दृष्टिकोण को अपडेट करने की प्रक्रिया में है। आपको वास्तव में $apply()अपने नियंत्रक के भीतर कॉल करने की आवश्यकता नहीं होनी चाहिए । यदि आपका दृष्टिकोण अपडेट नहीं हो रहा है जैसा कि आप अपेक्षा करते हैं, और फिर आपको कॉल करने के बाद यह त्रुटि मिलती है $apply(), तो इसका सबसे अधिक संभावना है कि आप मॉडल को सही ढंग से अपडेट नहीं कर रहे हैं। यदि आप कुछ विवरण पोस्ट करते हैं, तो हम मुख्य समस्या का पता लगा सकते हैं।


हे, मैंने पूरा दिन यह जानने के लिए बिताया कि एंगुलरजेएस केवल "जादुई रूप से" बाइंडिंग नहीं देख सकता है और मुझे उसे कभी-कभी $ लागू () के साथ धक्का देना चाहिए।
ओजेड_

क्या मतलब है you're not updating the the model correctly? $scope.err_message = 'err message';सही अद्यतन नहीं है?
OZ_

2
$apply()जब आपको कॉल करने की आवश्यकता होती है, तब केवल तब होता है जब आप कोणीय के "बाहरी" (जैसे jQuery प्लगइन से) मॉडल को अपडेट करते हैं। यह सही नहीं लग रहे दृश्य के जाल में गिरना आसान है, और इसलिए आप $apply()हर जगह एस का एक गुच्छा फेंकते हैं , जो तब ओपी में देखी गई त्रुटि के साथ समाप्त होता है। जब मैं कहता हूं कि you're not updating the the model correctlyमेरा मतलब है कि सभी व्यावसायिक तर्क सही तरीके से किसी भी चीज को पॉप्युलेट नहीं कर रहे हैं, जो उस दायरे में हो सकता है, जो उस दृश्य की ओर जाता है, जो अपेक्षित नहीं है।
dnc253

@ dnc253 मैं सहमत हूं, और मैंने जवाब लिखा है। अब मुझे जो भी पता है उसे जानकर, मैं $ टाइमआउट (फ़ंक्शन () {...}) का उपयोग करूंगा; यह वैसा ही करता है जैसा कि _.defer करता है। वे दोनों अगले एनीमेशन लूप को टाल देते हैं।
ठंढी


11

आप evalAsync का भी उपयोग कर सकते हैं। यह पचने के कुछ समय बाद चलेगा!

scope.evalAsync(function(scope){
    //use the scope...
});

10

सबसे पहले, इसे इस तरह से ठीक न करें

if ( ! $scope.$$phase) { 
  $scope.$apply(); 
}

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

इसके बजाय, उपयोग करें $timeout

    $timeout(function(){ 
  // Any code in here will automatically have an $scope.apply() run afterwards 
$scope.myvar = newValue; 
  // And it just works! 
});

यदि आप अंडरस्कोर या लॉश का उपयोग कर रहे हैं, तो आप डिफर () का उपयोग कर सकते हैं:

_.defer(function(){ 
  $scope.$apply(); 
});

9

यदि आप इस तरह से उपयोग करते हैं तो कभी-कभी आपको त्रुटियाँ मिलेंगी ( https://stackoverflow.com/a/12859093/801426 )।

इसे इस्तेमाल करे:

if(! $rootScope.$root.$$phase) {
...

5
दोनों का उपयोग करना! $ गुंजाइश। $ $ चरण और! $ गुंजाइश। $ जड़। $ $ चरण (नहीं! $ rootScc। $ रूट। $ $ चरण) मेरे लिए काम करता है। +1
asprotte

2
$rootScopeऔर anyScope.$rootएक ही आदमी हैं। $rootScope.$rootबेमानी है।
फ्लोरीबोन


5

प्रयोग करके देखें

$scope.applyAsync(function() {
    // your code
});

के बजाय

if(!$scope.$$phase) {
  //$digest or $apply
}

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

नोट: $ डाइजेस्ट के भीतर, $ applyAsync () केवल तभी फ्लश होगा जब करंट स्कोप $ रूटस्स्कोप हो। इसका मतलब यह है कि यदि आप चाइल्ड स्कोप पर $ डाइजेस्ट कहते हैं, तो यह $ applyAsync () कतार को स्पष्ट रूप से फ्लश नहीं करेगा।

उदाहरण के:

  $scope.$applyAsync(function () {
                if (!authService.authenticated) {
                    return;
                }

                if (vm.file !== null) {
                    loadService.setState(SignWizardStates.SIGN);
                } else {
                    loadService.setState(SignWizardStates.UPLOAD_FILE);
                }
            });

संदर्भ:

1. स्कोप। $ applyAsync () बनाम स्कोप। AngularJS 1.3 में $ evalAsync ()

  1. AngularJs डॉक्स

4

मैं आपको सलाह दूंगा कि आप पाचन चक्र को ट्रिगर करने के बजाय एक कस्टम घटना का उपयोग करें।

मुझे पता चला है कि कस्टम ईवेंट्स को प्रसारित करना और इस ईवेंट के लिए श्रोताओं को पंजीकृत करना एक कार्रवाई को ट्रिगर करने के लिए एक अच्छा समाधान है जो आप चाहते हैं कि आप एक पाचन चक्र में हैं या नहीं।

एक कस्टम ईवेंट बनाकर आप अपने कोड के साथ अधिक कुशल भी हो रहे हैं क्योंकि आप केवल उक्त इवेंट के लिए सब्सक्राइब किए गए श्रोताओं को ट्रिगर कर रहे हैं और स्कोप के लिए बाध्य सभी घड़ियों को ट्रिगर नहीं कर रहे हैं जैसे कि आपने स्कोप लागू किया है।

$scope.$on('customEventName', function (optionalCustomEventArguments) {
   //TODO: Respond to event
});


$scope.$broadcast('customEventName', optionalCustomEventArguments);

3

yearofmoo ने हमारे लिए एक पुन: प्रयोज्य $ safeApply फ़ंक्शन बनाने में एक महान काम किया:

https://github.com/yearofmoo/AngularJS-Scope.SafeApply

उपयोग:

//use by itself
$scope.$safeApply();

//tell it which scope to update
$scope.$safeApply($scope);
$scope.$safeApply($anotherScope);

//pass in an update function that gets called when the digest is going on...
$scope.$safeApply(function() {

});

//pass in both a scope and a function
$scope.$safeApply($anotherScope,function() {

});

//call it on the rootScope
$rootScope.$safeApply();
$rootScope.$safeApply($rootScope);
$rootScope.$safeApply($scope);
$rootScope.$safeApply($scope, fn);
$rootScope.$safeApply(fn);

2

मैं उन जगहों के $evalबजाय कॉल करके इस समस्या को हल करने में सक्षम $applyहूं जहां मुझे पता है कि $digestफ़ंक्शन चल रहा होगा।

डॉक्स के अनुसार , $applyमूल रूप से यह होता है:

function $apply(expr) {
  try {
    return $eval(expr);
  } catch (e) {
    $exceptionHandler(e);
  } finally {
    $root.$digest();
  }
}

मेरे मामले में, ng-clickएक दायरे के भीतर एक परिवर्तनशील चर, और उस चर पर एक $ घड़ी अन्य चर को बदलती है जो होना है $applied। यह अंतिम चरण त्रुटि "पहले से ही प्रगति में पचाने" का कारण बनता है।

घड़ी की अभिव्यक्ति के अंदर की जगह के $applyसाथ $evalगुंजाइश चर उम्मीद के अनुसार अद्यतन हो जाते हैं।

इसलिए, ऐसा प्रतीत होता है कि अगर एंगुलर के भीतर कुछ अन्य परिवर्तन के कारण डाइजेस्ट वैसे भी चल रहे हैं, तो $evalइन सभी को आपको करने की आवश्यकता है।



1

यह समझते हुए कि कोणीय दस्तावेज़ $$phaseएक विरोधी पैटर्न की जाँच कहते हैं , मैंने पाने $timeoutऔर _.deferकाम करने की कोशिश की ।

टाइमआउट और आस्थगित विधियाँ फ़्यूज़ की{{myVar}} तरह डोम में अप्रकाशित सामग्री का एक फ्लैश बनाती हैं । मेरे लिए यह स्वीकार्य नहीं था। यह मुझे बिना डॉग्मिकली कहे जाने के लिए छोड़ देता है कि कुछ एक हैक है, और एक उपयुक्त विकल्प नहीं है।

केवल एक चीज जो हर बार काम करती है:

if(scope.$$phase !== '$digest'){ scope.$digest() }

मैं इस पद्धति के खतरे को नहीं समझता, या इसे टिप्पणियों और कोणीय टीम के लोगों द्वारा हैक के रूप में क्यों वर्णित किया गया है। आदेश सटीक और पढ़ने में आसान लगता है:

"जब तक एक पहले से ही नहीं हो रहा है तब तक पाचन करें"

CoffeeScript में यह और भी सुंदर है:

scope.$digest() unless scope.$$phase is '$digest'

इससे क्या समस्या है? वहाँ एक विकल्प है कि एक FOUT नहीं बना होगा? $ safeApply ठीक दिखता है, लेकिन $$phaseनिरीक्षण विधि का भी उपयोग करता है।


1
मैं इस प्रश्न के लिए एक सूचित प्रतिक्रिया देखना पसंद करूंगा!
बेन व्हीलर

यह एक हैक है क्योंकि इसका मतलब है कि आप संदर्भ को याद करते हैं या कोड को इस बिंदु को नहीं समझते हैं: या तो आप कोणीय पाचन चक्र के भीतर हैं और आपको इसकी आवश्यकता नहीं है, या आप उस के बाहर अतुल्यकालिक हैं और फिर आपको इसकी आवश्यकता है। यदि आपको पता नहीं है कि कोड के उस बिंदु में, तो आप इसे पचाने के लिए जिम्मेदार नहीं हैं
floribon

1

यह मेरी सेवा है:

angular.module('myApp', []).service('Utils', function Utils($timeout) {
    var Super = this;

    this.doWhenReady = function(scope, callback, args) {
        if(!scope.$$phase) {
            if (args instanceof Array)
                callback.apply(scope, Array.prototype.slice.call(args))
            else
                callback();
        }
        else {
            $timeout(function() {
                Super.doWhenReady(scope, callback, args);
            }, 250);
        }
    };
});

और यह इसके उपयोग के लिए एक उदाहरण है:

angular.module('myApp').controller('MyCtrl', function ($scope, Utils) {
    $scope.foo = function() {
        // some code here . . .
    };

    Utils.doWhenReady($scope, $scope.foo);

    $scope.fooWithParams = function(p1, p2) {
        // some code here . . .
    };

    Utils.doWhenReady($scope, $scope.fooWithParams, ['value1', 'value2']);
};

1

मैं इस पद्धति का उपयोग कर रहा हूं और यह पूरी तरह से ठीक काम करता है। यह बस उस समय की प्रतीक्षा करता है जब चक्र समाप्त हो गया और फिर ट्रिगर हो गया apply()। बस apply(<your scope>)आप चाहते हैं कि कहीं से भी समारोह को बुलाओ ।

function apply(scope) {
  if (!scope.$$phase && !scope.$root.$$phase) {
    scope.$apply();
    console.log("Scope Apply Done !!");
  } 
  else {
    console.log("Scheduling Apply after 200ms digest cycle already in progress");
    setTimeout(function() {
        apply(scope)
    }, 200);
  }
}

1

जब मैंने डिबगर को अक्षम किया, तो त्रुटि अब नहीं हो रही है। मेरे मामले में , यह डिबगर कोड के निष्पादन को रोकने के कारण था।


0

उपरोक्त उत्तर के समान है लेकिन इसने मेरे लिए ईमानदारी से काम किया है ... एक सेवा में:

    //sometimes you need to refresh scope, use this to prevent conflict
    this.applyAsNeeded = function (scope) {
        if (!scope.$$phase) {
            scope.$apply();
        }
    };

0

आप उपयोग कर सकते हैं

$timeout

त्रुटि को रोकने के लिए।

 $timeout(function () {
                        var scope = angular.element($("#myController")).scope();
                        scope.myMethod();
                        scope.$scope();
                    },1);

क्या होगा अगर मैं $ टाइमआउट का उपयोग नहीं करना चाहता हूं
rahim.nagori

0

यह मुद्दा मूल रूप से तब आ रहा है, जब हम कोणीय को पाचन चक्र को चलाने का अनुरोध कर रहे हैं, भले ही इसकी प्रक्रिया जो कि कोणीय को समझने के लिए मुद्दा बना रही है। सांत्वना में परिणाम अपवाद।
1. इसमें कॉल स्कोप की कोई समझदारी नहीं है। $ timeout फंक्शन के अंदर $ apply () होता है क्योंकि आंतरिक रूप से यह ऐसा ही करता है।
2. कोड वैनिला जावास्क्रिप्ट फ़ंक्शन के साथ जाता है क्योंकि इसका मूल नहीं कोणीय कोणीय परिभाषित है अर्थात setTimeout
3. ऐसा करने के लिए आप इसका उपयोग कर सकते हैं

यदि (गुंजाइश $ $ चरण) {
गुंजाइश। $ evalAsync (फ़ंक्शन () {

}); }


0
        let $timeoutPromise = null;
        $timeout.cancel($timeoutPromise);
        $timeoutPromise = $timeout(() => {
            $scope.$digest();
        }, 0, false);

इस त्रुटि से बचने और $ लागू होने से बचने के लिए यहां अच्छा समाधान है

यदि आप बाहरी ईवेंट के आधार पर कॉल कर रहे हैं, तो आप इसे डेबिट (0) के साथ जोड़ सकते हैं। ऊपर 'डेबिट' है जिसका हम उपयोग कर रहे हैं, और कोड का पूरा उदाहरण

.factory('debounce', [
    '$timeout',
    function ($timeout) {

        return function (func, wait, apply) {
            // apply default is true for $timeout
            if (apply !== false) {
                apply = true;
            }

            var promise;
            return function () {
                var cntx = this,
                    args = arguments;
                $timeout.cancel(promise);
                promise = $timeout(function () {
                    return func.apply(cntx, args);
                }, wait, apply);
                return promise;
            };
        };
    }
])

और कोड ही किसी घटना को सुनने के लिए और $ पचाने के लिए $ स्कोप की जरूरत है

        let $timeoutPromise = null;
        let $update = debounce(function () {
            $timeout.cancel($timeoutPromise);
            $timeoutPromise = $timeout(() => {
                $scope.$digest();
            }, 0, false);
        }, 0, false);

        let $unwatchModelChanges = $scope.$root.$on('updatePropertiesInspector', function () {
            $update();
        });


        $scope.$on('$destroy', () => {
            $timeout.cancel($update);
            $timeout.cancel($timeoutPromise);
            $unwatchModelChanges();
        });

-3

यह मिला: https://coderwall.com/p/ngisma जहां नाथन वॉकर (पृष्ठ के निचले भाग के पास) $ rootScope में एक फंकेटर को सुझाव देता है कि वह func 'safeApply', कोड बनाएँ:

yourAwesomeModule.config([
  '$provide', function($provide) {
    return $provide.decorator('$rootScope', [
      '$delegate', function($delegate) {
        $delegate.safeApply = function(fn) {
          var phase = $delegate.$$phase;
          if (phase === "$apply" || phase === "$digest") {
            if (fn && typeof fn === 'function') {
              fn();
            }
          } else {
            $delegate.$apply(fn);
          }
        };
        return $delegate;
      }
    ]);
  }
]);

-7

इससे आपकी समस्या का समाधान हो जाएगा:

if(!$scope.$$phase) {
  //TODO
}

अगर (! $ स्कोप। $$ फेज) $ स्कोप नहीं है। $ apply।
MGot90
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.