AngularJS: सेवा चर कैसे देखें?


414

मेरी एक सेवा है, कहो:

factory('aService', ['$rootScope', '$resource', function ($rootScope, $resource) {
  var service = {
    foo: []
  };

  return service;
}]);

और मैं fooHTML में दी गई सूची को नियंत्रित करने के लिए उपयोग करना चाहूंगा :

<div ng-controller="FooCtrl">
  <div ng-repeat="item in foo">{{ item }}</div>
</div>

कंट्रोलर के लिए यह पता लगाने के लिए कि कब aService.fooअद्यतन किया गया है मैंने इस पैटर्न को एक साथ जोड़ दिया है जहां मैं नियंत्रक से aService जोड़ता हूं $scopeऔर फिर निम्न करता हूं $scope.$watch():

function FooCtrl($scope, aService) {                                                                                                                              
  $scope.aService = aService;
  $scope.foo = aService.foo;

  $scope.$watch('aService.foo', function (newVal, oldVal, scope) {
    if(newVal) { 
      scope.foo = newVal;
    }
  });
}

यह लंबे समय से हाथ लगता है, और मैं इसे सेवा के चर का उपयोग करने वाले प्रत्येक नियंत्रक में दोहरा रहा हूं। क्या साझा चर देखने का एक बेहतर तरीका है?


1
आप AService और उसके सभी गुणों को गहराई से देखने के लिए सही करने के लिए $ वॉच सेट के लिए एक तीसरा पैरामीटर पास कर सकते हैं।
सिरोत्तमहट्ट

7
$ गुंजाइश ।foo = aService.foo पर्याप्त है, आप ऊपर की रेखा खो सकते हैं। और यह $ घड़ी के अंदर क्या करता है, इससे कोई मतलब नहीं है, यदि आप $ गुंजाइश के लिए एक नया मान असाइन करना चाहते हैं। तो बस इसे करें ...
जिन

4
क्या आप aService.foohtml मार्कअप में संदर्भ दे सकते हैं ? (इस तरह: plnkr.co/edit/aNrw5Wo4Q0IxR2loipl5?p=preview )
thetallweeks

1
मैंने कॉलबैक या $ घड़ियों के बिना एक उदाहरण जोड़ा है, नीचे उत्तर देखें ( jsfiddle.net/zymotik/853wv7s )
Zymotik

1
@ मायकेगल्डहिल, आप सही कह रहे हैं। मुझे लगता है कि यह जावास्क्रिप्ट की प्रकृति के कारण है, आप इस पैटर्न को कई अन्य स्थानों पर देख सकते हैं (न केवल कोणीय में, बल्कि जेएस में आमतौर पर बोलते हुए)। एक तरफ, आप मूल्य को स्थानांतरित करते हैं (और यह बाध्य नहीं होता है) और दूसरी तरफ आप किसी वस्तु को स्थानांतरित करते हैं (या मूल्य जो वस्तु को संदर्भित करता है ...), और इसीलिए गुण सही तरीके से अपडेट किए जाते हैं (जैसे पूरी तरह से) ऊपर Zymotik के उदाहरण में दिखाया गया है)।
क्रिस्टोफ़ विडाल

जवाबों:


277

आप हमेशा अच्छे पुराने पर्यवेक्षक पैटर्न का उपयोग कर सकते हैं यदि आप अत्याचार और उपरि से बचना चाहते हैं $watch

सेवा में:

factory('aService', function() {
  var observerCallbacks = [];

  //register an observer
  this.registerObserverCallback = function(callback){
    observerCallbacks.push(callback);
  };

  //call this when you know 'foo' has been changed
  var notifyObservers = function(){
    angular.forEach(observerCallbacks, function(callback){
      callback();
    });
  };

  //example of when you may want to notify observers
  this.foo = someNgResource.query().$then(function(){
    notifyObservers();
  });
});

और नियंत्रक में:

function FooCtrl($scope, aService){
  var updateFoo = function(){
    $scope.foo = aService.foo;
  };

  aService.registerObserverCallback(updateFoo);
  //service now in control of updating foo
};

21
@Moo इस $destoryघटना की गुंजाइश पर सुनते हैं और एक अपंजीकृत विधि जोड़ते हैंaService
जेमी

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

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

11
$watchबनाम ऑब्जर्वर पैटर्न का चयन केवल यह करना है कि मतदान करना है या धक्का देना है, और मूल रूप से प्रदर्शन की बात है, इसलिए प्रदर्शन के मामले में इसका उपयोग करें। मैं पर्यवेक्षक पैटर्न का उपयोग करता हूं जब अन्यथा मुझे जटिल वस्तुओं को "गहरा" करना होगा। मैं एकल सेवा मूल्यों को देखने के बजाय पूरी सेवाओं को $ दायरे में संलग्न करता हूं। मैं शैतान की तरह कोणीय की $ घड़ी से बचता हूं, निर्देशों में और देशी कोणीय डेटा-बंधन में ऐसा होने के लिए पर्याप्त है।
dtheodor

107
इस कारण से कि हम कोणीय की तरह एक रूपरेखा का उपयोग कर रहे हैं, अपने स्वयं के पर्यवेक्षक पैटर्न को नहीं पकाना है।
कोड व्हिस्परर

230

इस तरह के परिदृश्य में, जहाँ कई / अनकॉन्ड ऑब्जेक्ट को बदलावों में रुचि हो सकती है, $rootScope.$broadcastबदले जा रहे आइटम से उपयोग करें ।

श्रोताओं की अपनी रजिस्ट्री बनाने के बजाय (जिसे विभिन्न $ विध्वंस पर साफ करना पड़ता है), आपको $broadcastप्रश्न में सेवा से सक्षम होना चाहिए ।

आपको अभी भी $onप्रत्येक श्रोता में हैंडलर को कोड करना होगा लेकिन पैटर्न को कई कॉल्स से डिकॉप किया गया है $digestऔर इस तरह लंबे समय तक चलने वाले वॉचरों के जोखिम से बचा जाता है।

इस तरह, श्रोता भी अपने व्यवहार में बदलाव किए बिना DOM और / या अलग-अलग चाइल्ड स्कोप से आ-जा सकते हैं ।

** अद्यतन: उदाहरण **

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

.factory('UserService', [ '$rootScope', function($rootScope) {
   var service = <whatever you do for the object>

   service.save = function(data) {
     .. validate data and update model ..
     // notify listeners and provide the data that changed [optional]
     $rootScope.$broadcast('user:updated',data);
   }

   // alternatively, create a callback function and $broadcast from there if making an ajax call

   return service;
}]);

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

.controller('UserCtrl', [ 'UserService', '$scope', function(UserService, $scope) {

  var user = UserService.getUser();

  // if you don't want to expose the actual object in your scope you could expose just the values, or derive a value for your purposes
   $scope.name = user.firstname + ' ' +user.lastname;

   $scope.$on('user:updated', function(event,data) {
     // you could inspect the data to see if what you care about changed, or just update your own scope
     $scope.name = user.firstname + ' ' + user.lastname;
   });

   // different event names let you group your code and logic by what happened
   $scope.$on('user:logout', function(event,data) {
     .. do something differently entirely ..
   });

 }]);

इसका एक फायदा यह है कि कई घड़ियों को खत्म कर दिया गया है। यदि आप ऊपर दिए गए उदाहरण की तरह फ़ील्ड्स या व्युत्पन्न मानों का संयोजन कर रहे थे, तो आपको फ़र्स्टनाम और लास्टनाम दोनों के गुण देखने होंगे। GetUser () फ़ंक्शन देखने से केवल तभी काम होगा जब उपयोगकर्ता ऑब्जेक्ट को अद्यतनों पर प्रतिस्थापित किया गया था, अगर उपयोगकर्ता ऑब्जेक्ट केवल इसके गुणों को अपडेट करता है तो यह आग नहीं लगाएगा। किस मामले में आपको एक गहरी घड़ी करनी होगी और यह अधिक गहन है।

$ प्रसारण संदेश को उस दायरे से भेजता है जिसे नीचे किसी भी चाइल्ड स्कोप में कहा जाता है। इसलिए $ rootScope से इसे कॉल करने पर हर स्कोप पर आग लग जाएगी। यदि आप अपने नियंत्रक के दायरे से $ प्रसारण के लिए थे, उदाहरण के लिए, यह केवल उन स्कोप में आग लगाएगा जो आपके नियंत्रक दायरे से प्राप्त होते हैं। $ एमिट विपरीत दिशा में जाता है और डोम घटना के समान व्यवहार करता है कि यह गुंजाइश श्रृंखला को बुलबुले में रखता है।

ध्यान रखें कि ऐसे परिदृश्य हैं जहां $ ब्रॉडकास्ट बहुत मायने रखता है, और ऐसे परिदृश्य हैं जहां $ वॉच एक बेहतर विकल्प है - खासकर अगर एक विशिष्ट दायरे में एक बहुत विशिष्ट घड़ी अभिव्यक्ति के साथ।


1
$ पाचन चक्र से बाहर निकलना एक अच्छी बात है, खासकर अगर आप जो बदलाव देख रहे हैं वह ऐसा मूल्य नहीं है जो सीधे और सीधे डोम में जाएगा।
एक्सएम

क्या बचने के लिए दूर है .save () विधि। ओवरस्किल की तरह लगता है जब आप साझा सेवा में एक एकल चर के अद्यतन की निगरानी कर रहे हैं। जब हम परिवर्तन करते हैं तो क्या हम साझा सेवा के भीतर से चर को देख सकते हैं और प्रसारित कर सकते हैं?
जेरीकुर

मैंने नियंत्रकों के बीच डेटा साझा करने के लिए काफी कुछ तरीके आजमाए, लेकिन यह एकमात्र ऐसा काम है। अच्छा खेला, सर।

मैं अन्य जवाबों के लिए इसे पसंद करता हूं, यह कम हैसी लगता है , धन्यवाद
जेएमके

9
यह सही डिज़ाइन पैटर्न है, यदि आपके उपभोगकर्ता नियंत्रक के पास डेटा के कई संभावित स्रोत हैं; दूसरे शब्दों में, यदि आपके पास MIMO स्थिति (मल्टीपल इनपुट / मल्टीपल आउटपुट) है। यदि आप केवल एक-से-कई पैटर्न का उपयोग कर रहे हैं, तो आपको प्रत्यक्ष ऑब्जेक्ट रेफ़रिंग का उपयोग करना चाहिए और कोणीय ढांचे को आपके लिए दो-तरफ़ा बाइंडिंग करने देना चाहिए। Horkyze ने इसे नीचे लिंक किया है, और यह स्वचालित दो-तरफ़ा बाइंडिंग का एक अच्छा विवरण है, और इसकी सीमाएँ हैं: stsc3000.github.io/blog/2013/10/26/…
चार्ल्स

47

मैं @dtheodot के समान दृष्टिकोण का उपयोग कर रहा हूं लेकिन कॉलबैक पास करने के बजाय कोणीय वादे का उपयोग कर रहा हूं

app.service('myService', function($q) {
    var self = this,
        defer = $q.defer();

    this.foo = 0;

    this.observeFoo = function() {
        return defer.promise;
    }

    this.setFoo = function(foo) {
        self.foo = foo;
        defer.notify(self.foo);
    }
})

फिर जहां भी सेवा पर myService.setFoo(foo)अपडेट करने के लिए सिर्फ विधि का उपयोग करें foo। अपने नियंत्रक में आप इसका उपयोग कर सकते हैं:

myService.observeFoo().then(null, null, function(foo){
    $scope.foo = foo;
})

thenसफलता और त्रुटि कॉलबैक के पहले दो तर्क हैं, तीसरा कॉलबैक है।

$ Q का संदर्भ।


मैट पिल्ग्गी द्वारा वर्णित $ प्रसारण पर इस पद्धति का क्या फायदा होगा?
Fabio

वैसे दोनों विधियों के अपने उपयोग हैं। मेरे लिए प्रसारण के लाभ मानव पठनीयता और एक ही घटना के लिए अधिक स्थानों पर सुनने की संभावना होगी। मुझे लगता है कि मुख्य नुकसान यह है कि प्रसारण सभी वंशजों को संदेश भेज रहा है इसलिए यह एक प्रदर्शन मुद्दा हो सकता है।
क्रिम्म

2
मुझे एक समस्या आ रही थी जहाँ $scope.$watchसर्विस वैरिएबल पर काम करना उचित नहीं लग रहा था (मैं जिस स्कोप को देख रहा था वह एक ऐसा मोडल था जो विरासत में मिला था $rootScope) - यह काम किया। कूल चाल, साझा करने के लिए धन्यवाद!
15

4
इस दृष्टिकोण के साथ आप अपने आप को कैसे साफ करेंगे? क्या गुंजाइश नष्ट होने पर वादे से पंजीकृत कॉलबैक को निकालना संभव है?
अब्रीस

अच्छा प्रश्न। मैं ईमानदारी से नहीं जानता। मैं इस बात पर कुछ परीक्षण करने की कोशिश करूंगा कि आप वादे से अधिसूचित कॉलबैक कैसे निकाल सकते हैं।
क्रि

41

घड़ियों या प्रेक्षक कॉलबैक के बिना ( http://jsfiddle.net/zymotik/853wvv7s/ ):

जावास्क्रिप्ट:

angular.module("Demo", [])
    .factory("DemoService", function($timeout) {

        function DemoService() {
            var self = this;
            self.name = "Demo Service";

            self.count = 0;

            self.counter = function(){
                self.count++;
                $timeout(self.counter, 1000);
            }

            self.addOneHundred = function(){
                self.count+=100;
            }

            self.counter();
        }

        return new DemoService();

    })
    .controller("DemoController", function($scope, DemoService) {

        $scope.service = DemoService;

        $scope.minusOneHundred = function() {
            DemoService.count -= 100;
        }

    });

एचटीएमएल

<div ng-app="Demo" ng-controller="DemoController">
    <div>
        <h4>{{service.name}}</h4>
        <p>Count: {{service.count}}</p>
    </div>
</div>

यह जावास्क्रिप्ट काम करता है क्योंकि हम मूल्य के बजाय सेवा से एक वस्तु वापस कर रहे हैं। जब एक जावास्क्रिप्ट ऑब्जेक्ट को एक सेवा से वापस किया जाता है, तो कोणीय अपने सभी गुणों के लिए घड़ियों को जोड़ता है।

यह भी ध्यान दें कि मैं 'var self = this' का उपयोग कर रहा हूं क्योंकि जब $ टाइमआउट निष्पादित होता है, तो मुझे मूल ऑब्जेक्ट का संदर्भ रखना होगा, अन्यथा 'this' विंडो ऑब्जेक्ट को संदर्भित करेगा।


3
यह एक महान दृष्टिकोण है! क्या पूरी सेवा के बजाय किसी सेवा की संपत्ति को दायरे में बांधने का कोई तरीका है? सिर्फ करने से $scope.count = service.countकाम नहीं चलता।
jvannistelrooy

आप एक (अनियंत्रित) ऑब्जेक्ट के अंदर की संपत्ति को भी घोंसला बना सकते हैं ताकि यह संदर्भ द्वारा पारित हो जाए। $scope.data = service.data <p>Count: {{ data.count }}</p>
एलेक्स रॉस

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

धन्यवाद @CodeMoose, मैंने इसे और भी सरल कर दिया है जो आज भी AngularJS / JavaScript के लिए नया है।
ज़ोमोटिक

2
भगवान आपका भला करे। मैं कह रहा हूं कि मैं लाखों घंटे बर्बाद कर चुका हूं। क्योंकि मैं 1.5 से संघर्ष कर रहा था और 1 से 2 तक शिथिलता और कोणीय भी डेटा साझा करना चाहता था
आमना

29

मैं इस सवाल पर कुछ इसी तरह की तलाश में ठोकर खाई, लेकिन मुझे लगता है कि यह क्या हो रहा है, साथ ही कुछ अतिरिक्त समाधानों के बारे में पूरी तरह से स्पष्टीकरण के योग्य है।

जब एक कोणीय अभिव्यक्ति आपके द्वारा उपयोग किए एचटीएमएल में मौजूद है, जैसे कोणीय स्वचालित रूप से एक सेट $watchके लिए $scope.foo, और जब भी एचटीएमएल अद्यतन करेगा $scope.fooबदल जाता है।

<div ng-controller="FooCtrl">
  <div ng-repeat="item in foo">{{ item }}</div>
</div>

यहां मुद्दा यह है कि दो चीजों में से एक को प्रभावित कर रहा है aService.foo जैसे कि परिवर्तन पूर्ववत हैं। ये दो संभावनाएँ हैं:

  1. aService.foo हर बार एक नए सरणी में सेट हो रहा है, जिससे इसका संदर्भ पुराना हो गया है।
  2. aService.fooइस तरह से अद्यतन किया जा रहा है कि अद्यतन पर एक $digestचक्र ट्रिगर नहीं होता है।

समस्या 1: उल्लिखित संदर्भ

पहली संभावना को देखते हुए, यह मानते हुए कि $digestलागू किया जा रहा है, अगर aService.fooहमेशा एक ही सरणी होती थी, तो स्वचालित रूप से सेट$watch परिवर्तनों का पता लगाएगा, जैसा कि नीचे दिए गए कोड स्निपेट में दिखाया गया है।

समाधान 1-ए: सुनिश्चित करें कि प्रत्येक अद्यतन पर सरणी या वस्तु एक ही वस्तु है

जैसा कि आप देख सकते हैं, एनजी-रिपीट माना जाता है कि बदलाव aService.fooहोने पर अपडेट नहीं होता aService.fooहै, लेकिन एनजी-रिपीट इससे जुड़ा होता aService2.foo है । इसका कारण यह है कि हमारा संदर्भ aService.fooपुराना है, लेकिन हमारा संदर्भ पुराना aService2.fooनहीं है। हमने प्रारंभिक सरणी के साथ एक संदर्भ बनाया $scope.foo = aService.foo;था, जिसे तब सेवा द्वारा खारिज कर दिया गया था, यह अगले अद्यतन पर है, जिसका अर्थ है$scope.foo अब उस सरणी को संदर्भित नहीं अब हम चाहते थे।

हालांकि, हालांकि यह सुनिश्चित करने के लिए कई तरीके हैं कि प्रारंभिक संदर्भ को चातुर्य में रखा जाए, कभी-कभी वस्तु या सरणी को बदलना आवश्यक हो सकता है। या फिर क्या हुआ अगर सेवा संपत्ति संदर्भ एक तरह एक आदिम Stringया Number? उन मामलों में, हम केवल एक संदर्भ पर भरोसा नहीं कर सकते। तो हम क्या कर सकते हैं?

पहले दिए गए जवाबों में से कई उस समस्या के लिए कुछ समाधान देते हैं। हालांकि, मैं व्यक्तिगत रूप से जिन और thetallweeks द्वारा सुझाई गई सरल पद्धति का उपयोग करने के लिए टिप्पणी में हूं :

html मार्कअप में बस aService.foo का संदर्भ लें

समाधान 1-बी: सेवा को गुंजाइश में संलग्न करें, और {service}.{property}HTML में संदर्भ ।

मतलब, बस यह करें:

HTML:

<div ng-controller="FooCtrl">
  <div ng-repeat="item in aService.foo">{{ item }}</div>
</div>

जे एस:

function FooCtrl($scope, aService) {
    $scope.aService = aService;
}

इस तरह, प्रत्येक पर $watchहल होगाaService.foo$digest , जिसे सही ढंग से अद्यतन मूल्य मिलेगा।

यह उस तरह का है जो आप अपने वर्कअराउंड के साथ करने की कोशिश कर रहे थे, लेकिन रास्ते के बारे में बहुत कम दौर में। आपने $watchनियंत्रक में एक अनावश्यक जोड़ दिया जो स्पष्ट रूप fooसे $scopeजब भी बदलता है , डालता है । आपको लगता है कि अतिरिक्त की जरूरत नहीं है $watchजब आप संलग्न aServiceके बजाय aService.fooकरने के लिए $scopeबाँध स्पष्ट रूप से करने के लिए, और aService.fooमार्कअप में।


अब यह सब ठीक है और अच्छा मानकर एक $digestचक्र लगाया जा रहा है। उपरोक्त मेरे उदाहरणों में, मैंने $intervalसरणियों को अद्यतन करने के लिए कोणीय सेवा का उपयोग किया , जो $digestप्रत्येक अद्यतन के बाद स्वचालित रूप से एक लूप को बंद कर देता है । लेकिन अगर सेवा चर (जो भी कारण से) "कोणीय दुनिया" के अंदर अद्यतन नहीं हो रहे हैं, तो क्या होगा। दूसरे शब्दों में, हम एक है $digestचक्र स्वचालित रूप से सक्रिय किया जा रहा है जब भी सेवा संपत्ति बदलता है?


समस्या 2: गुम $digest

यहाँ समाधान में से कई इस मुद्दे को हल करेंगे, लेकिन मैं कोड व्हिस्परर से सहमत हूं :

यही कारण है कि हम एंगुलर जैसे ढांचे का उपयोग कर रहे हैं, अपने स्वयं के पर्यवेक्षक पैटर्न को नहीं पकाना है

इसलिए, मैं उपयोग करना जारी रखना पसंद करूंगा aService.foo HTML मार्कअप में संदर्भ जैसा कि ऊपर दिए गए दूसरे उदाहरण में दिखाया गया है, और नियंत्रक के भीतर अतिरिक्त कॉलबैक दर्ज नहीं करना है।

समाधान 2: सेटर और गेट्टर का उपयोग करें $rootScope.$apply()

मैं हैरान था कि किसी ने अभी तक एक सेटर और गेट्टर के उपयोग का सुझाव नहीं दिया है । इस क्षमता को ECMAScript5 में पेश किया गया था, और इस तरह से यह अब सालों से चली आ रही है। बेशक, इसका मतलब है कि, यदि किसी भी कारण से, आपको वास्तव में पुराने ब्राउज़रों का समर्थन करने की आवश्यकता है, तो यह विधि काम नहीं करेगी, लेकिन मुझे ऐसा लगता है कि गेटर्स और सेटरर्स का जावास्क्रिप्ट में बहुत अधिक उपयोग किया जाता है। इस विशेष मामले में, वे काफी उपयोगी हो सकते हैं:

factory('aService', [
  '$rootScope',
  function($rootScope) {
    var realFoo = [];

    var service = {
      set foo(a) {
        realFoo = a;
        $rootScope.$apply();
      },
      get foo() {
        return realFoo;
      }
    };
  // ...
}

यहाँ मैंने सेवा फ़ंक्शन में एक 'निजी' चर जोड़ा realFoo:। इस पर क्रमशः कार्यों get foo()और set foo()कार्यों का उपयोग करके अद्यतन और पुनर्प्राप्त किया जाता हैservice ऑब्जेक्ट जाता है।

$rootScope.$apply()सेट फ़ंक्शन के उपयोग पर ध्यान दें । यह सुनिश्चित करता है कि कोणीय किसी भी परिवर्तन से अवगत होगा service.foo। यदि आपको 'इनप्रोग' त्रुटियां मिलती हैं, तो यह उपयोगी संदर्भ पृष्ठ देखें , या यदि आप कोणीय> = 1.3 का उपयोग करते हैं, तो आप इसका उपयोग कर सकते हैं $rootScope.$applyAsync()

इसके अलावा अगर aService.fooयह बहुत बार अद्यतन किया जा रहा है, तो इससे सावधान रहें , क्योंकि यह प्रदर्शन को काफी प्रभावित कर सकता है। यदि प्रदर्शन एक समस्या होगी, तो आप सेटर का उपयोग करके अन्य उत्तरों के समान एक पर्यवेक्षक पैटर्न सेट कर सकते हैं।


3
यह सही, और सबसे आसान उपाय है। जैसा कि @NanoWizard कहते हैं, $ डाइजेस्ट servicesउन संपत्तियों के लिए नहीं है जो सेवा से संबंधित हैं।
सर्पदोरुक तहमाज़ 21

28

जहाँ तक मैं बता सकता हूँ, तुम न के रूप में विस्तार से कुछ करना है। आपने पहले से ही सेवा से अपने दायरे में फू को सौंपा है और चूंकि फू एक सरणी है (और किसी वस्तु को संदर्भ के अनुसार सौंपा गया है!)। तो, आपको जो कुछ करने की ज़रूरत है वह कुछ इस तरह है:

function FooCtrl($scope, aService) {                                                                                                                              
  $scope.foo = aService.foo;

 }

यदि कुछ, इसी Ctrl में अन्य चर foo बदलने पर निर्भर है, तो हाँ, आपको foo देखने और उस चर में परिवर्तन करने के लिए एक घड़ी की आवश्यकता होगी। लेकिन जब तक यह एक साधारण संदर्भ है देखना अनावश्यक है। उम्मीद है की यह मदद करेगा।


35
मैंने कोशिश की, और मैं $watchएक आदिम के साथ काम नहीं कर सका । इसके बजाय, मैंने सेवा पर एक विधि को परिभाषित किया जो कि आदिम मूल्य लौटाएगा: somePrimitive() = function() { return somePrimitive }और मैंने उस पद्धति को $ गुंजाइश संपत्ति सौंपी $scope.somePrimitive = aService.somePrimitive;:। फिर मैंने HTML में स्कोप मेथड का इस्तेमाल किया: <span>{{somePrimitive()}}</span>
Mark Rajcok

4
@MarkRajcok कोई आदिम का उपयोग नहीं करता है। उन्हें एक वस्तु में जोड़ें। प्रिमिटिव म्यूटेबल नहीं हैं और इसलिए 2way डेटा बाइंडिंग काम नहीं करेगी
जिमी केन

3
@ जिमीकेन, हाँ, प्राइमेटिव्स को 2-वे डेटाबाइंडिंग के लिए उपयोग नहीं किया जाना चाहिए, लेकिन मुझे लगता है कि सवाल सेवा चर देखने के बारे में था, न कि 2-वे बाइंडिंग सेट करने के लिए। यदि आपको केवल एक सेवा संपत्ति / चर देखने की आवश्यकता है, तो एक वस्तु की आवश्यकता नहीं है - एक आदिम का उपयोग किया जा सकता है।
मार्क राजकोक

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

4
यह भी मेरे लिए काम नहीं करता है। बस असाइन करने $scope.foo = aService.fooसे स्कोप वैरिएबल स्वचालित रूप से अपडेट नहीं होता है।
डार्विन टेक

9

आप $ rootScope में सेवा डाल सकते हैं और देख सकते हैं:

myApp.run(function($rootScope, aService){
    $rootScope.aService = aService;
    $rootScope.$watch('aService', function(){
        alert('Watch');
    }, true);
});

आपके नियंत्रक में:

myApp.controller('main', function($scope){
    $scope.aService.foo = 'change';
});

अन्य विकल्प बाहरी पुस्तकालय का उपयोग करना है जैसे: https://github.com/melanke/Watch.JS

इसके साथ काम करता है: IE 9+, FF 4+, SF 5+, WebKit, CH 7+, OP 12+, BESEN, Node.JS, Rhino 1.7+

आप एक, कई या सभी ऑब्जेक्ट विशेषताओं के परिवर्तनों का निरीक्षण कर सकते हैं।

उदाहरण:

var ex3 = {
    attr1: 0,
    attr2: "initial value of attr2",
    attr3: ["a", 3, null]
};   
watch(ex3, function(){
    alert("some attribute of ex3 changes!");
});
ex3.attr3.push("new value");​

2
मैं यह समझ नहीं पाऊंगा कि टॉप-मोस्ट वोटेड नहीं है !!! यह सबसे सुरुचिपूर्ण समाधान (IMO) है क्योंकि यह सूचनात्मक-एन्ट्रापी को कम करता है और संभवतः अतिरिक्त मध्यस्थ संचालकों की आवश्यकता को कम करता है। अगर मैं कर सकता हूं तो मैं इसे अधिक वोट
कोडी

$ RootScope में अपने सभी सेवाओं को जोड़ना, इसके लाभ और यह संभावित नुकसान हैं, यहाँ कुछ हद तक विस्तृत हैं: stackoverflow.com/questions/14573023/…
Zymotik

6

आप फ़ैक्टरी के भीतर होने वाले परिवर्तनों को स्वयं देख सकते हैं और फिर एक परिवर्तन प्रसारित कर सकते हैं

angular.module('MyApp').factory('aFactory', function ($rootScope) {
    // Define your factory content
    var result = {
        'key': value
    };

    // add a listener on a key        
    $rootScope.$watch(function () {
        return result.key;
    }, function (newValue, oldValue, scope) {
        // This is called after the key "key" has changed, a good idea is to broadcast a message that key has changed
        $rootScope.$broadcast('aFactory:keyChanged', newValue);
    }, true);

    return result;
});

फिर अपने कंट्रोलर में:

angular.module('MyApp').controller('aController', ['$rootScope', function ($rootScope) {

    $rootScope.$on('aFactory:keyChanged', function currentCityChanged(event, value) {
        // do something
    });
}]);

इस तरीके से आप अपने विवरण के भीतर सभी संबंधित कारखाने कोड डालते हैं, तब आप केवल बाहर से प्रसारण पर भरोसा कर सकते हैं


6

== UPDATED ==

$ घड़ी में अब बहुत सरल है।

यहाँ कलम

HTML:

<div class="container" data-ng-app="app">

  <div class="well" data-ng-controller="FooCtrl">
    <p><strong>FooController</strong></p>
    <div class="row">
      <div class="col-sm-6">
        <p><a href="" ng-click="setItems([ { name: 'I am single item' } ])">Send one item</a></p>
        <p><a href="" ng-click="setItems([ { name: 'Item 1 of 2' }, { name: 'Item 2 of 2' } ])">Send two items</a></p>
        <p><a href="" ng-click="setItems([ { name: 'Item 1 of 3' }, { name: 'Item 2 of 3' }, { name: 'Item 3 of 3' } ])">Send three items</a></p>
      </div>
      <div class="col-sm-6">
        <p><a href="" ng-click="setName('Sheldon')">Send name: Sheldon</a></p>
        <p><a href="" ng-click="setName('Leonard')">Send name: Leonard</a></p>
        <p><a href="" ng-click="setName('Penny')">Send name: Penny</a></p>
      </div>
    </div>
  </div>

  <div class="well" data-ng-controller="BarCtrl">
    <p><strong>BarController</strong></p>
    <p ng-if="name">Name is: {{ name }}</p>
    <div ng-repeat="item in items">{{ item.name }}</div>
  </div>

</div>

जावास्क्रिप्ट:

var app = angular.module('app', []);

app.factory('PostmanService', function() {
  var Postman = {};
  Postman.set = function(key, val) {
    Postman[key] = val;
  };
  Postman.get = function(key) {
    return Postman[key];
  };
  Postman.watch = function($scope, key, onChange) {
    return $scope.$watch(
      // This function returns the value being watched. It is called for each turn of the $digest loop
      function() {
        return Postman.get(key);
      },
      // This is the change listener, called when the value returned from the above function changes
      function(newValue, oldValue) {
        if (newValue !== oldValue) {
          // Only update if the value changed
          $scope[key] = newValue;
          // Run onChange if it is function
          if (angular.isFunction(onChange)) {
            onChange(newValue, oldValue);
          }
        }
      }
    );
  };
  return Postman;
});

app.controller('FooCtrl', ['$scope', 'PostmanService', function($scope, PostmanService) {
  $scope.setItems = function(items) {
    PostmanService.set('items', items);
  };
  $scope.setName = function(name) {
    PostmanService.set('name', name);
  };
}]);

app.controller('BarCtrl', ['$scope', 'PostmanService', function($scope, PostmanService) {
  $scope.items = [];
  $scope.name = '';
  PostmanService.watch($scope, 'items');
  PostmanService.watch($scope, 'name', function(newVal, oldVal) {
    alert('Hi, ' + newVal + '!');
  });
}]);

1
अगर मुझे एक से अधिक वेरिएबल सुनने की जरूरत है, तो मुझे PostmanService पसंद है लेकिन मुझे कंट्रोलर पर $ वॉच फंक्शन कैसे बदलना चाहिए?
जेडी

हाय जेडी, सिर ऊपर करने के लिए धन्यवाद! मैंने पेन और उत्तर को अपडेट किया। मैं उस के लिए एक और घड़ी समारोह जोड़ने के लिए पुनर्संयोजन करता हूं। इसलिए, मैंने PostmanService में एक नई सुविधा जोड़ी। मुझे आशा है कि यह मदद करता है :)
hayatbiralem

वास्तव में, हाँ यह है :) यदि आप समस्या का अधिक विवरण साझा करते हैं तो शायद मैं आपकी मदद कर सकता हूं।
हयातबीरलेम

4

Dtheodor के उत्तर पर निर्माण आप नीचे दिए गए समान का उपयोग कर सकते हैं ताकि यह सुनिश्चित किया जा सके कि आप कॉलबैक को अनइंस्टॉल करना न भूलें ... $scopeहालांकि एक सेवा को पारित करने में कुछ आपत्ति कर सकते हैं ।

factory('aService', function() {
  var observerCallbacks = [];

  /**
   * Registers a function that will be called when
   * any modifications are made.
   *
   * For convenience the callback is called immediately after registering
   * which can be prevented with `preventImmediate` param.
   *
   * Will also automatically unregister the callback upon scope destory.
   */
  this.registerObserver = function($scope, cb, preventImmediate){
    observerCallbacks.push(cb);

    if (preventImmediate !== true) {
      cb();
    }

    $scope.$on('$destroy', function () {
      observerCallbacks.remove(cb);
    });
  };

  function notifyObservers() {
    observerCallbacks.forEach(function (cb) {
      cb();
    });
  };

  this.foo = someNgResource.query().$then(function(){
    notifyObservers();
  });
});

Array.remove एक विस्तार विधि है जो इस तरह दिखता है:

/**
 * Removes the given item the current array.
 *
 * @param  {Object}  item   The item to remove.
 * @return {Boolean}        True if the item is removed.
 */
Array.prototype.remove = function (item /*, thisp */) {
    var idx = this.indexOf(item);

    if (idx > -1) {
        this.splice(idx, 1);

        return true;
    }
    return false;
};

2

यहाँ मेरा सामान्य दृष्टिकोण है।

mainApp.service('aService',[function(){
        var self = this;
        var callbacks = {};

        this.foo = '';

        this.watch = function(variable, callback) {
            if (typeof(self[variable]) !== 'undefined') {
                if (!callbacks[variable]) {
                    callbacks[variable] = [];
                }
                callbacks[variable].push(callback);
            }
        }

        this.notifyWatchersOn = function(variable) {
            if (!self[variable]) return;
            if (!callbacks[variable]) return;

            angular.forEach(callbacks[variable], function(callback, key){
                callback(self[variable]);
            });
        }

        this.changeFoo = function(newValue) {
            self.foo = newValue;
            self.notifyWatchersOn('foo');
        }

    }]);

आपके नियंत्रक में

function FooCtrl($scope, aService) {
    $scope.foo;

    $scope._initWatchers = function() {
        aService.watch('foo', $scope._onFooChange);
    }

    $scope._onFooChange = function(newValue) {
        $scope.foo = newValue;
    }

    $scope._initWatchers();

}

FooCtrl.$inject = ['$scope', 'aService'];

2

मेरे जैसे लोगों के लिए बस एक सरल उपाय की तलाश में, यह लगभग वही करता है जो आप नियंत्रकों में सामान्य $ घड़ी का उपयोग करने से उम्मीद करते हैं। अंतर केवल इतना है, कि यह जावास्क्रिप्ट संदर्भ में स्ट्रिंग का मूल्यांकन करता है न कि किसी विशिष्ट दायरे पर। आपको अपनी सेवा में $ rootScope इंजेक्षन करना होगा, हालांकि इसका उपयोग केवल पाचन चक्र में ठीक से करने के लिए किया जाता है।

function watch(target, callback, deep) {
    $rootScope.$watch(function () {return eval(target);}, callback, deep);
};

2

इसी तरह के एक मुद्दे का सामना करते हुए मैंने एक कार्यक्षेत्र देखा और समारोह को सेवा चर लौटा दिया। मैंने एक js fiddle बनाई है । आप नीचे कोड पा सकते हैं।

    var myApp = angular.module("myApp",[]);

myApp.factory("randomService", function($timeout){
    var retValue = {};
    var data = 0;

    retValue.startService = function(){
        updateData();
    }

    retValue.getData = function(){
        return data;
    }

    function updateData(){
        $timeout(function(){
            data = Math.floor(Math.random() * 100);
            updateData()
        }, 500);
    }

    return retValue;
});

myApp.controller("myController", function($scope, randomService){
    $scope.data = 0;
    $scope.dataUpdated = 0;
    $scope.watchCalled = 0;
    randomService.startService();

    $scope.getRandomData = function(){
        return randomService.getData();    
    }

    $scope.$watch("getRandomData()", function(newValue, oldValue){
        if(oldValue != newValue){
            $scope.data = newValue;
            $scope.dataUpdated++;
        }
            $scope.watchCalled++;
    });
});

2

मैं इस सवाल पर आया था, लेकिन यह मेरी समस्या थी कि मैं सेट इन्टरवल का उपयोग कर रहा था जब मुझे कोणीय $ अंतराल प्रदाता का उपयोग करना चाहिए था। यह सेटटाइमआउट के लिए भी मामला है (इसके बजाय $ टाइमआउट का उपयोग करें)। मुझे पता है कि यह ओपी के सवाल का जवाब नहीं है, लेकिन यह कुछ मदद कर सकता है, क्योंकि इससे मुझे मदद मिली।


आप उपयोग कर सकते हैं setTimeout, या कोई अन्य गैर-कोणीय कार्य कर सकते हैं, लेकिन कॉलबैक में कोड को लपेटना न भूलें $scope.$apply()
मैग्नेट्रोननी

2

मैंने एक समान समस्या के साथ दूसरे धागे पर वास्तव में बहुत अच्छा समाधान पाया है, लेकिन पूरी तरह से अलग दृष्टिकोण। स्रोत: AngularJS: $ rootScope मान को बदलने पर निर्देश के भीतर $ घड़ी काम नहीं कर रही है

मूल रूप से समाधान वहाँ का उपयोग करने के लिए नहीं कहता है $watchक्योंकि यह बहुत भारी समाधान है। इसके बजाय वे उपयोग करने का प्रस्ताव देते हैं $emitऔर $on

मेरी समस्या मेरी सेवा में एक चर देखने और निर्देशन में प्रतिक्रिया करने की थी । और उपरोक्त विधि के साथ यह बहुत आसान है!

मेरा मॉड्यूल / सेवा उदाहरण:

angular.module('xxx').factory('example', function ($rootScope) {
    var user;

    return {
        setUser: function (aUser) {
            user = aUser;
            $rootScope.$emit('user:change');
        },
        getUser: function () {
            return (user) ? user : false;
        },
        ...
    };
});

इसलिए मूल रूप से मैं अपने को देखता हूं user - जब भी यह नए मूल्य पर सेट होता है तो मैं $emitएक user:changeस्थिति।

अब मेरे मामले में, मैंने जिस निर्देश का इस्तेमाल किया है:

angular.module('xxx').directive('directive', function (Auth, $rootScope) {
    return {
        ...
        link: function (scope, element, attrs) {
            ...
            $rootScope.$on('user:change', update);
        }
    };
});

अब निर्देश में मैं दिए गए परिवर्तन पर$rootScope और क्रमशः सुनता हूं - मैं क्रमशः प्रतिक्रिया करता हूं। बहुत आसान और सुरुचिपूर्ण!


1

// सेवा: (यहां कुछ खास नहीं)

myApp.service('myService', function() {
  return { someVariable:'abc123' };
});

// ctrl:

myApp.controller('MyCtrl', function($scope, myService) {

  $scope.someVariable = myService.someVariable;

  // watch the service and update this ctrl...
  $scope.$watch(function(){
    return myService.someVariable;
  }, function(newValue){
    $scope.someVariable = newValue;
  });
});

1

एक अजीब सा बदसूरत, लेकिन मैंने एक टॉगल के लिए अपनी सेवा में गुंजाइश चर का पंजीकरण जोड़ा है:

myApp.service('myService', function() {
    var self = this;
    self.value = false;
    self.c2 = function(){};
    self.callback = function(){
        self.value = !self.value; 
       self.c2();
    };

    self.on = function(){
        return self.value;
    };

    self.register = function(obj, key){ 
        self.c2 = function(){
            obj[key] = self.value; 
            obj.$apply();
        } 
    };

    return this;
});

और फिर नियंत्रक में:

function MyCtrl($scope, myService) {
    $scope.name = 'Superhero';
    $scope.myVar = false;
    myService.register($scope, 'myVar');
}

धन्यवाद। थोड़ा सा सवाल: आप thisउस सेवा के बजाय क्यों लौटते हैं self?
shrekuu

4
क्योंकि गलतियां कभी-कभी हो जाती हैं। ;-)
nclu

return this;अपने कंस्ट्रक्टरों से बाहर अच्छा अभ्यास ;-)
कोडी

1

इस प्लंकर पर एक नज़र डालें: यह सबसे सरल उदाहरण है जिसके बारे में मैं सोच सकता था

http://jsfiddle.net/HEdJF/

<div ng-app="myApp">
    <div ng-controller="FirstCtrl">
        <input type="text" ng-model="Data.FirstName"><!-- Input entered here -->
        <br>Input is : <strong>{{Data.FirstName}}</strong><!-- Successfully updates here -->
    </div>
    <hr>
    <div ng-controller="SecondCtrl">
        Input should also be here: {{Data.FirstName}}<!-- How do I automatically updated it here? -->
    </div>
</div>



// declare the app with no dependencies
var myApp = angular.module('myApp', []);
myApp.factory('Data', function(){
   return { FirstName: '' };
});

myApp.controller('FirstCtrl', function( $scope, Data ){
    $scope.Data = Data;
});

myApp.controller('SecondCtrl', function( $scope, Data ){
    $scope.Data = Data;
});

0

मैंने यहां कुछ भयानक पर्यवेक्षक पैटर्न देखे हैं जो बड़े अनुप्रयोगों पर मेमोरी लीक का कारण बनते हैं।

मुझे थोड़ी देर हो सकती है लेकिन यह इतना सरल है।

यदि आप फ़ंक्शन का उपयोग करना चाहते हैं, तो घड़ी समारोह संदर्भ परिवर्तनों (आदिम प्रकार) के लिए देखता है।

someArray.push(someObj); someArray = someArray.splice(0);

यह संदर्भ को अद्यतन करेगा और घड़ी को कहीं से भी अपडेट करेगा। एक सेवा पाने वाला विधि सहित। कुछ भी जो एक आदिम है स्वचालित रूप से अपडेट किया जाएगा।


0

मुझे भाग में देर हो गई है लेकिन मुझे ऊपर पोस्ट किए गए उत्तर की तुलना में ऐसा करने का एक अच्छा तरीका मिला। सेवा चर का मान रखने के लिए एक चर निर्दिष्ट करने के बजाय, मैंने कार्यक्षेत्र से जुड़ा एक फ़ंक्शन बनाया, जो सेवा चर लौटाता है।

नियंत्रक

$scope.foo = function(){
 return aService.foo;
}

मुझे लगता है कि यह वही करेगा जो आप चाहते हैं। मेरा नियंत्रक इस कार्यान्वयन के साथ मेरी सेवा के मूल्य की जाँच करता रहता है। ईमानदारी से, यह चयनित उत्तर की तुलना में बहुत सरल है।


क्यों इसे डाउनवोट किया गया था .. मैंने भी कई बार इसी तरह की तकनीक का इस्तेमाल किया है और इसने काम किया है।
अपरिष्कृत

0

मैंने दो सरल उपयोगिता सेवाएं लिखी हैं जो मुझे सेवा गुणों के परिवर्तनों को ट्रैक करने में मदद करती हैं।

यदि आप लंबे स्पष्टीकरण को छोड़ना चाहते हैं, तो आप स्ट्रेट से jsfiddle पर जा सकते हैं

  1. WatchObj

mod.service('WatchObj', ['$rootScope', WatchObjService]);

function WatchObjService($rootScope) {
  // returns watch function
  // obj: the object to watch for
  // fields: the array of fields to watch
  // target: where to assign changes (usually it's $scope or controller instance)
  // $scope: optional, if not provided $rootScope is use
  return function watch_obj(obj, fields, target, $scope) {
    $scope = $scope || $rootScope;
    //initialize watches and create an array of "unwatch functions"
    var watched = fields.map(function(field) {
      return $scope.$watch(
        function() {
          return obj[field];
        },
        function(new_val) {
          target[field] = new_val;
        }
      );
    });
    //unregister function will unregister all our watches
    var unregister = function unregister_watch_obj() {
      watched.map(function(unregister) {
        unregister();
      });
    };
    //automatically unregister when scope is destroyed
    $scope.$on('$destroy', unregister);
    return unregister;
  };
}

इस सेवा का उपयोग निम्न तरीके से नियंत्रक में किया जाता है: मान लीजिए कि आपके पास गुण 'प्रोप 1', 'प्रॉप 2', 'प्रूव' के साथ एक सेवा "परीक्षण सेवा" है। आप 'प्रोप 1' और 'प्रोप 2' के दायरे को देखना और असाइन करना चाहते हैं। घड़ी सेवा के साथ यह ऐसा दिखेगा:

app.controller('TestWatch', ['$scope', 'TestService', 'WatchObj', TestWatchCtrl]);

function TestWatchCtrl($scope, testService, watch) {
  $scope.prop1 = testService.prop1;
  $scope.prop2 = testService.prop2;
  $scope.prop3 = testService.prop3;
  watch(testService, ['prop1', 'prop2'], $scope, $scope);
}

  1. एप्‍लिकेशन लागू करें ओब्ज महान है, लेकिन यदि आपकी सेवा में अतुल्यकालिक कोड है तो यह पर्याप्त नहीं है। उस स्थिति के लिए, मैं एक दूसरी उपयोगिता का उपयोग करता हूं जो इस तरह दिखती है:

mod.service('apply', ['$timeout', ApplyService]);

function ApplyService($timeout) {
  return function apply() {
    $timeout(function() {});
  };
}

मैं $ पचाने वाले लूप को ट्रिगर करने के लिए अपने async कोड के अंत में इसे ट्रिगर करूंगा। उसके जैसा:

app.service('TestService', ['apply', TestService]);

function TestService(apply) {
  this.apply = apply;
}
TestService.prototype.test3 = function() {
  setTimeout(function() {
    this.prop1 = 'changed_test_2';
    this.prop2 = 'changed2_test_2';
    this.prop3 = 'changed3_test_2';
    this.apply(); //trigger $digest loop
  }.bind(this));
}

तो, सभी एक साथ उस तरह दिखेंगे (आप इसे चला सकते हैं या फ़िडेल खोल सकते हैं ):

// TEST app code

var app = angular.module('app', ['watch_utils']);

app.controller('TestWatch', ['$scope', 'TestService', 'WatchObj', TestWatchCtrl]);

function TestWatchCtrl($scope, testService, watch) {
  $scope.prop1 = testService.prop1;
  $scope.prop2 = testService.prop2;
  $scope.prop3 = testService.prop3;
  watch(testService, ['prop1', 'prop2'], $scope, $scope);
  $scope.test1 = function() {
    testService.test1();
  };
  $scope.test2 = function() {
    testService.test2();
  };
  $scope.test3 = function() {
    testService.test3();
  };
}

app.service('TestService', ['apply', TestService]);

function TestService(apply) {
  this.apply = apply;
  this.reset();
}
TestService.prototype.reset = function() {
  this.prop1 = 'unchenged';
  this.prop2 = 'unchenged2';
  this.prop3 = 'unchenged3';
}
TestService.prototype.test1 = function() {
  this.prop1 = 'changed_test_1';
  this.prop2 = 'changed2_test_1';
  this.prop3 = 'changed3_test_1';
}
TestService.prototype.test2 = function() {
  setTimeout(function() {
    this.prop1 = 'changed_test_2';
    this.prop2 = 'changed2_test_2';
    this.prop3 = 'changed3_test_2';
  }.bind(this));
}
TestService.prototype.test3 = function() {
  setTimeout(function() {
    this.prop1 = 'changed_test_2';
    this.prop2 = 'changed2_test_2';
    this.prop3 = 'changed3_test_2';
    this.apply();
  }.bind(this));
}
//END TEST APP CODE

//WATCH UTILS
var mod = angular.module('watch_utils', []);

mod.service('apply', ['$timeout', ApplyService]);

function ApplyService($timeout) {
  return function apply() {
    $timeout(function() {});
  };
}

mod.service('WatchObj', ['$rootScope', WatchObjService]);

function WatchObjService($rootScope) {
  // target not always equals $scope, for example when using bindToController syntax in 
  //directives
  return function watch_obj(obj, fields, target, $scope) {
    // if $scope is not provided, $rootScope is used
    $scope = $scope || $rootScope;
    var watched = fields.map(function(field) {
      return $scope.$watch(
        function() {
          return obj[field];
        },
        function(new_val) {
          target[field] = new_val;
        }
      );
    });
    var unregister = function unregister_watch_obj() {
      watched.map(function(unregister) {
        unregister();
      });
    };
    $scope.$on('$destroy', unregister);
    return unregister;
  };
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div class='test' ng-app="app" ng-controller="TestWatch">
  prop1: {{prop1}}
  <br>prop2: {{prop2}}
  <br>prop3 (unwatched): {{prop3}}
  <br>
  <button ng-click="test1()">
    Simple props change
  </button>
  <button ng-click="test2()">
    Async props change
  </button>
  <button ng-click="test3()">
    Async props change with apply
  </button>
</div>

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