एक कोणीय सेवा कार्य () में $ गुंजाइश इंजेक्षन


107

मेरे पास एक सेवा है:

angular.module('cfd')
  .service('StudentService', [ '$http',
    function ($http) {
    // get some data via the $http
    var path = 'data/people/students.json';
    var students = $http.get(path).then(function (resp) {
      return resp.data;
    });     
    //save method create a new student if not already exists
    //else update the existing object
    this.save = function (student) {
      if (student.id == null) {
        //if this is new student, add it in students array
        $scope.students.push(student);
      } else {
        //for existing student, find this student using id
        //and update it.
        for (i in students) {
          if (students[i].id == student.id) {
            students[i] = student;
          }
        }
      }
    };

लेकिन जब मैं फोन करता हूं save(), मेरे पास पहुंच नहीं है $scope, और मिलता है ReferenceError: $scope is not defined। तो तार्किक कदम (मेरे लिए), के साथ बचाने () प्रदान करने के लिए है $scope, और इस प्रकार मुझे भी इसे प्रदान / इंजेक्ट करना होगा service। तो अगर मैं ऐसा करता हूं:

  .service('StudentService', [ '$http', '$scope',
                      function ($http, $scope) {

मुझे निम्नलिखित त्रुटि मिलती है:

त्रुटि: [$ इंजेक्टर: अनप्र] अज्ञात प्रदाता: $ स्कोपप्रोविडर <- $ स्कोप <- स्टूडेंट सर्विस

त्रुटि में लिंक (वाह कि स्वच्छ है!) मुझे पता है कि यह इंजेक्टर संबंधित है, और js फ़ाइलों की घोषणा के आदेश के साथ करना पड़ सकता है। मैंने उन्हें पुन: व्यवस्थित करने की कोशिश की है index.html, लेकिन मुझे लगता है कि यह कुछ अधिक सरल है, जैसे कि मैं उन्हें इंजेक्ट कर रहा हूं।

कोणीय-यूआई और कोणीय-यूआई-राउटर का उपयोग करना

जवाबों:


183

$scopeकि आप नियंत्रकों में इंजेक्ट किया जा रहा देखें कुछ सेवा (इंजेक्शन सामान के बाकी की तरह) नहीं है, लेकिन एक स्कोप वस्तु है। कई स्कोप ऑब्जेक्ट्स बनाए जा सकते हैं (आमतौर पर प्रोटोटाइप मूल रूप से पेरेंट स्कोप से विरासत में मिले हैं)। सभी स्कोप की जड़ है $rootScopeऔर आप $new()किसी भी दायरे (सहित $rootScope) की विधि का उपयोग करके एक नया चाइल्ड-स्कोप बना सकते हैं ।

एक स्कोप का उद्देश्य प्रस्तुति और आपके ऐप के व्यापारिक तर्क को "एक साथ गोंद" करना है। यह $scopeएक सेवा में पारित करने के लिए बहुत मतलब नहीं है ।

सेवाएं डेटा साझा करने के लिए (अन्य नियंत्रकों के बीच) के रूप में उपयोग की जाने वाली सिंगलटन ऑब्जेक्ट्स हैं (उदाहरण के लिए कई नियंत्रकों के बीच) और आमतौर पर कोड के पुन: प्रयोज्य टुकड़ों को एन्कैप्सुलेट करते हैं (क्योंकि उन्हें आपके ऐप के किसी भी हिस्से में "सेवाओं" को इंजेक्ट किया जा सकता है और उनकी आवश्यकता होती है: नियंत्रकों निर्देश, फिल्टर, अन्य सेवाएं आदि)।

मुझे यकीन है, विभिन्न दृष्टिकोण आपके लिए काम करेंगे। एक यह है:
चूंकि StudentServiceछात्र डेटा से निपटने के लिए प्रभारी है, तो आप StudentServiceछात्रों की एक सरणी रख सकते हैं और जो कोई भी दिलचस्पी ले सकता है, उसे "शेयर" करें (जैसे कि आपका $scope)। यह और भी अधिक समझ में आता है, अगर अन्य विचार / नियंत्रक / फ़िल्टर / सेवाएं हैं जिनकी उस जानकारी तक पहुंच की आवश्यकता है (यदि अभी कोई अधिकार नहीं है, तो आश्चर्यचकित न हों यदि वे जल्द ही पॉप अप करना शुरू कर दें)।
हर बार एक नया छात्र जोड़ा जाता है (सेवा की save()विधि का उपयोग करके ), सेवा के छात्रों की अपनी सरणी को अपडेट किया जाएगा और उस सरणी को साझा करने वाली प्रत्येक अन्य वस्तु स्वचालित रूप से भी अपडेट हो जाएगी।

ऊपर वर्णित दृष्टिकोण के आधार पर, आपका कोड इस तरह दिख सकता है:

angular.
  module('cfd', []).

  factory('StudentService', ['$http', '$q', function ($http, $q) {
    var path = 'data/people/students.json';
    var students = [];

    // In the real app, instead of just updating the students array
    // (which will be probably already done from the controller)
    // this method should send the student data to the server and
    // wait for a response.
    // This method returns a promise to emulate what would happen 
    // when actually communicating with the server.
    var save = function (student) {
      if (student.id === null) {
        students.push(student);
      } else {
        for (var i = 0; i < students.length; i++) {
          if (students[i].id === student.id) {
            students[i] = student;
            break;
          }
        }
      }

      return $q.resolve(student);
    };

    // Populate the students array with students from the server.
    $http.get(path).then(function (response) {
      response.data.forEach(function (student) {
        students.push(student);
      });
    });

    return {
      students: students,
      save: save
    };     
  }]).

  controller('someCtrl', ['$scope', 'StudentService', 
    function ($scope, StudentService) {
      $scope.students = StudentService.students;
      $scope.saveStudent = function (student) {
        // Do some $scope-specific stuff...

        // Do the actual saving using the StudentService.
        // Once the operation is completed, the $scope's `students`
        // array will be automatically updated, since it references
        // the StudentService's `students` array.
        StudentService.save(student).then(function () {
          // Do some more $scope-specific stuff, 
          // e.g. show a notification.
        }, function (err) {
          // Handle the error.
        });
      };
    }
]);

इस दृष्टिकोण का उपयोग करते समय आपको एक बात ध्यान रखनी चाहिए कि सेवा के एरे को फिर से असाइन न करें, क्योंकि तब कोई भी अन्य घटक (जैसे स्कोप) अभी भी मूल एरे को संदर्भित करेगा और आपका ऐप टूट जाएगा।
उदाहरण के लिए StudentService:

/* DON'T DO THAT   */  
var clear = function () { students = []; }

/* DO THIS INSTEAD */  
var clear = function () { students.splice(0, students.length); }

यह लघु डेमो भी देखें ।


थोड़ा अद्यतन:

किसी सेवा का उपयोग करने के बारे में बात करते समय उत्पन्न होने वाले भ्रम से बचने के लिए कुछ शब्द, लेकिन इसे service()फ़ंक्शन के साथ नहीं बनाया जा सकता है ।

डॉक्स को$provide उद्धृत करना :

एक कोणीय सेवा एक सेवा कारखाने द्वारा बनाई गई एक सिंगलटन ऑब्जेक्ट है । ये सेवा कारखाने ऐसे कार्य हैं जो बदले में, एक सेवा प्रदाता द्वारा बनाए जाते हैं । सेवा प्रदाताओं निर्माता कार्य हैं। जब तात्कालिक रूप से उन्हें एक संपत्ति कहा जाना चाहिए $get, जो सेवा कारखाने का कार्य रखती है ।
[...]
... $provideसेवा में एक प्रदाता को निर्दिष्ट किए बिना सेवाओं को पंजीकृत करने के लिए अतिरिक्त सहायक विधियाँ हैं:

  • प्रदाता (प्रदाता) - $ इंजेक्टर के साथ एक सेवा प्रदाता को पंजीकृत करता है
  • स्थिरांक (obj) - एक मूल्य / वस्तु को पंजीकृत करता है जिसे प्रदाताओं और सेवाओं द्वारा पहुँचा जा सकता है।
  • मूल्य (obj) - एक मूल्य / वस्तु को पंजीकृत करता है जिसे केवल सेवाओं द्वारा पहुँचा जा सकता है, प्रदाताओं द्वारा नहीं।
  • factory (fn) - एक सेवा कारखाने के कार्य को पंजीकृत करता है, fn, जिसे एक सेवा प्रदाता वस्तु में लपेटा जाएगा, जिसकी $ get property में दिए गए factory function होंगे।
  • सेवा (वर्ग) - एक कंस्ट्रक्टर फ़ंक्शन को पंजीकृत करता है, वह वर्ग जो एक सेवा प्रदाता ऑब्जेक्ट में लपेटा जाएगा, जिसकी $ प्रॉपर्टी दी गई कंस्ट्रक्टर फ़ंक्शन का उपयोग करके एक नई ऑब्जेक्ट को तुरंत हटा देगी।

असल में, यह क्या कहता है कि प्रत्येक कोणीय सेवा का उपयोग करके पंजीकृत किया जाता है $provide.provider(), लेकिन सरल सेवाओं के लिए "शॉर्टकट" विधियां हैं (जिनमें से दो हैं service()और factory())।
यह सब "एक सेवा के लिए" उबलता है, इसलिए इससे बहुत फर्क नहीं पड़ता है कि आप किस विधि का उपयोग करते हैं (जब तक कि आपकी सेवा की आवश्यकताओं को उस पद्धति द्वारा कवर किया जा सकता है)।

बीटीडब्ल्यू, providerबनाम serviceबनाम factoryएंगुलर नई-कॉमर्स के लिए सबसे भ्रामक अवधारणाओं में से एक है, लेकिन सौभाग्य से चीजों को आसान बनाने के लिए बहुत सारे संसाधन (यहां एसओ पर) हैं। (बस आसपास खोज करें।)

(मुझे आशा है कि इसे साफ कर दिया जाएगा - मुझे बताएं कि क्या यह नहीं है।)


1
एक सवाल। आप सेवा कहते हैं, लेकिन आपका कोड उदाहरण कारखाने का उपयोग करता है। मैं सिर्फ कारखानों, सेवाओं और प्रदाताओं के बीच के अंतर को समझने की शुरुआत कर रहा हूं, बस यह सुनिश्चित करना चाहता हूं कि एक कारखाने के साथ जाना सबसे अच्छा विकल्प है, क्योंकि मैं एक सेवा का उपयोग कर रहा था। अपने उदाहरण से बहुत कुछ सीखा। फिडेल और बहुत स्पष्ट स्पष्टीकरण के लिए धन्यवाद।
क्रिस फ्रिसिना

3
@chrisFrisina: थोड़ा सा स्पष्टीकरण के साथ उत्तर अपडेट करें। मूल रूप से, यदि आप उपयोग करते हैं serviceया factory- तो आप इसे और कोणीय सेवा के साथ समाप्त करेंगे , इससे बहुत अंतर नहीं पड़ता । बस सुनिश्चित करें कि आप समझते हैं कि प्रत्येक कैसे काम करता है और यदि यह आपकी आवश्यकताओं के अनुरूप है।
गल्पक

अच्छा लेख! इससे मुझे बहुत सहायता प्राप्त हुई !

धन्यवाद भाई! यहाँ इसी तरह के मामले पर एक अच्छा लेख है stsc3000.github.io/blog/2013/10/26/…
Terafor

@ExpertSystem $scope.studentsखाली होने वाला है, अगर ajax कॉल खत्म नहीं होती है? या $scope.studentsआंशिक रूप से भरा जा रहा है, अगर यह कोड ब्लॉक प्रगति पर है? students.push(student);
Yc Zhang

18

$scopeसेवा के भीतर संशोधन करने की कोशिश करने के बजाय , आप $watchपरिवर्तनों के लिए अपनी सेवा पर संपत्ति देखने के लिए अपने नियंत्रक के भीतर कार्यान्वित कर सकते हैं और फिर किसी संपत्ति को अपडेट कर सकते हैं $scope। यहाँ एक उदाहरण है जिसे आप नियंत्रक में आज़मा सकते हैं:

angular.module('cfd')
    .controller('MyController', ['$scope', 'StudentService', function ($scope, StudentService) {

        $scope.students = null;

        (function () {
            $scope.$watch(function () {
                return StudentService.students;
            }, function (newVal, oldVal) {
                if ( newValue !== oldValue ) {
                    $scope.students = newVal;
                }
            });
        }());
    }]);

ध्यान देने वाली एक बात यह है कि आपकी सेवा के भीतर, studentsसंपत्ति दिखाई देने के लिए, उसे सेवा ऑब्जेक्ट पर या इस thisतरह होना चाहिए:

this.students = $http.get(path).then(function (resp) {
  return resp.data;
});

12

अच्छी तरह से (एक लंबा) ... यदि आप किसी सेवा के अंदर पहुंचने का आग्रह करते हैं $scope, तो आप कर सकते हैं:

एक गेट्टर / सेटर सेवा बनाएं

ngapp.factory('Scopes', function (){
  var mem = {};
  return {
    store: function (key, value) { mem[key] = value; },
    get: function (key) { return mem[key]; }
  };
});

इसे इंजेक्ट करें और इसमें कंट्रोलर स्कोप को स्टोर करें

ngapp.controller('myCtrl', ['$scope', 'Scopes', function($scope, Scopes) {
  Scopes.store('myCtrl', $scope);
}]);

अब, एक और सेवा के अंदर गुंजाइश प्राप्त करें

ngapp.factory('getRoute', ['Scopes', '$http', function(Scopes, $http){
  // there you are
  var $scope = Scopes.get('myCtrl');
}]);

स्कोप कैसे नष्ट हो रहे हैं?
जे.के.

9

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


मैं समझ गया आप क्या कह रहे हैं। हालाँकि, मेरे मामले में, मेरे पास कई कंट्रोलर हैं और मैं बहुत ही समान $ घड़ियों के साथ उनके स्कोप को कॉन्फ़िगर करना चाहूंगा। आप ऐसा कैसे करेंगे / कहां करेंगे? वर्तमान में, मैं वास्तव में एक सेवा के पैरामीटर को पास करता हूं जो $ घड़ियां सेट करता है।
मोरिट्ज़

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

@TIMINeutron जो दायरे से गुजरने से बहुत बेहतर लगता है, मैं कोशिश करूंगा कि अगली बार परिदृश्य सामने आए! धन्यवाद!
मोरिट्ज़

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

3

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

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

var students = $http.get(path).then(function (resp) {
  return resp.data;
}); // then() returns a promise object, not resp.data

इसके आस-पास जाने का एक सरल तरीका है और यह एक कॉलबैक फ़ंक्शन की आपूर्ति करता है।

.service('StudentService', [ '$http',
    function ($http) {
    // get some data via the $http
    var path = '/students';

    //save method create a new student if not already exists
    //else update the existing object
    this.save = function (student, doneCallback) {
      $http.post(
        path, 
        {
          params: {
            student: student
          }
        }
      )
      .then(function (resp) {
        doneCallback(resp.data); // when the async http call is done, execute the callback
      });  
    }
.controller('StudentSaveController', ['$scope', 'StudentService', function ($scope, StudentService) {
  $scope.saveUser = function (user) {
    StudentService.save(user, function (data) {
      $scope.message = data; // I'm assuming data is a string error returned from your REST API
    })
  }
}]);

फार्म:

<div class="form-message">{{message}}</div>

<div ng-controller="StudentSaveController">
  <form novalidate class="simple-form">
    Name: <input type="text" ng-model="user.name" /><br />
    E-mail: <input type="email" ng-model="user.email" /><br />
    Gender: <input type="radio" ng-model="user.gender" value="male" />male
    <input type="radio" ng-model="user.gender" value="female" />female<br />
    <input type="button" ng-click="reset()" value="Reset" />
    <input type="submit" ng-click="saveUser(user)" value="Save" />
  </form>
</div>

इसने आपके कुछ व्यापारिक तर्क को संक्षिप्तता के लिए हटा दिया और मैंने वास्तव में कोड का परीक्षण नहीं किया है, लेकिन ऐसा कुछ काम करेगा। मुख्य अवधारणा नियंत्रक से सेवा में कॉलबैक पारित कर रही है जिसे भविष्य में बाद में कॉल किया जाता है। यदि आप NodeJS से परिचित हैं तो यह एक ही अवधारणा है।


यह दृष्टिकोण अनुशंसित नहीं है। देखें कि प्रतिज्ञा .thenविधि से कॉलबैक एंटी-पैटर्न क्यों हैं
georgeawg

0

एक ही विधेय में मिला। मैं निम्नलिखित के साथ समाप्त हुआ। इसलिए यहाँ मैं फ़ैक्टरी में स्कोप ऑब्जेक्ट को इंजेक्ट नहीं कर रहा हूँ, लेकिन कंट्रोलर में $ स्कोप को स्वयं http सेवा द्वारा लौटाए गए वादे की अवधारणा का उपयोग करके सेट कर रहा हूँ

(function () {
    getDataFactory = function ($http)
    {
        return {
            callWebApi: function (reqData)
            {
                var dataTemp = {
                    Page: 1, Take: 10,
                    PropName: 'Id', SortOrder: 'Asc'
                };

                return $http({
                    method: 'GET',
                    url: '/api/PatientCategoryApi/PatCat',
                    params: dataTemp, // Parameters to pass to external service
                    headers: { 'Content-Type': 'application/Json' }
                })                
            }
        }
    }
    patientCategoryController = function ($scope, getDataFactory) {
        alert('Hare');
        var promise = getDataFactory.callWebApi('someDataToPass');
        promise.then(
            function successCallback(response) {
                alert(JSON.stringify(response.data));
                // Set this response data to scope to use it in UI
                $scope.gridOptions.data = response.data.Collection;
            }, function errorCallback(response) {
                alert('Some problem while fetching data!!');
            });
    }
    patientCategoryController.$inject = ['$scope', 'getDataFactory'];
    getDataFactory.$inject = ['$http'];
    angular.module('demoApp', []);
    angular.module('demoApp').controller('patientCategoryController', patientCategoryController);
    angular.module('demoApp').factory('getDataFactory', getDataFactory);    
}());

0

गुंजाइश चर के साथ काम करने के लिए कोड नियंत्रक में जाना चाहिए, और सेवा कॉल सेवा पर जाएं।

आप $rootScopeका उपयोग करने के उद्देश्य से इंजेक्ट कर सकते हैं $rootScope.$broadcastऔर $rootScope.$on

अन्यथा इंजेक्शन लगाने से बचें $rootScope। देख

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