javascript - मॉडल डेटा और व्यवहार कहां रखा जाए?




model-view-controller angularjs dci (8)

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

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

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

बेशक मॉडल डेटा और व्यवहार सादे जावास्क्रिप्ट ऑब्जेक्ट्स या किसी भी "क्लास" पैटर्न के साथ कार्यान्वित किया जा सकता है। लेकिन AngularJS ऐसा करने का तरीका क्या होगा? सेवाओं का उपयोग करना?

तो यह इस सवाल के लिए नीचे आता है:

AngularJS सर्वोत्तम प्रथाओं के बाद, आप नियंत्रक से decoupled मॉडल कैसे कार्यान्वित करते हैं?


Answers

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

वर्तमान में ऐसा लगता है कि कोणीय सेवा कुछ अवधारणाओं में से एक है जो इसे अगली पीढ़ी के कोणीय बना देती है, इसलिए सेवाओं के लिए सभी तर्कों को स्थानांतरित करने के सामान्य दिशानिर्देश का पालन करना संभवतः स्मार्ट है। हालांकि, मैं तर्क दूंगा कि आप कोणीय सेवाओं पर प्रत्यक्ष निर्भरता के बिना भी decoupled मॉडल बना सकते हैं। केवल आवश्यक निर्भरताओं और जिम्मेदारियों के साथ स्वयं निहित वस्तुओं को बनाना संभवतः जाने का तरीका है। स्वचालित परीक्षण करते समय यह जीवन को बहुत आसान बनाता है। इन जिम्मेदारीएं इन दिनों एक काम है, लेकिन यह बहुत समझ में आता है!

यहां एक पटर का एक उदाहरण दिया गया है जिसे मैं डोम से ऑब्जेक्ट मॉडल को डीकॉप्लिंग करने के लिए अच्छा मानता हूं।

http://www.syntaxsuccess.com/viewarticle/548ebac8ecdac75c8a09d58e

एक महत्वपूर्ण लक्ष्य है कि अपने कोड को इस तरह से व्यवस्थित करना है जो एक दृश्य परीक्षण के रूप में उपयोग के रूप में उपयोग करना आसान बनाता है। यदि आप प्राप्त करते हैं कि आप यथार्थवादी और उपयोगी परीक्षण लिखने के लिए अच्छी तरह से तैनात हैं।


मैंने इस ब्लॉग पोस्ट में उस सटीक मुद्दे से निपटने की कोशिश की है।

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

पोस्ट में $ http , $ संसाधन , और Restangular का उपयोग करके तीन दृष्टिकोण शामिल हैं।

नौकरी मॉडल पर एक कस्टम getResult() विधि के साथ, प्रत्येक के लिए कुछ उदाहरण कोड यहां दिया गया है:

Restangular (आसान peasy):

angular.module('job.models', [])
  .service('Job', ['Restangular', function(Restangular) {
    var Job = Restangular.service('jobs');

    Restangular.extendModel('jobs', function(model) {
      model.getResult = function() {
        if (this.status == 'complete') {
          if (this.passed === null) return "Finished";
          else if (this.passed === true) return "Pass";
          else if (this.passed === false) return "Fail";
        }
        else return "Running";
      };

      return model;
    });

    return Job;
  }]);

$ संसाधन (थोड़ा और अधिक शांत):

angular.module('job.models', [])
    .factory('Job', ['$resource', function($resource) {
        var Job = $resource('/api/jobs/:jobId', { full: 'true', jobId: '@id' }, {
            query: {
                method: 'GET',
                isArray: false,
                transformResponse: function(data, header) {
                    var wrapped = angular.fromJson(data);
                    angular.forEach(wrapped.items, function(item, idx) {
                        wrapped.items[idx] = new Job(item);
                    });
                    return wrapped;
                }
            }
        });

        Job.prototype.getResult = function() {
            if (this.status == 'complete') {
                if (this.passed === null) return "Finished";
                else if (this.passed === true) return "Pass";
                else if (this.passed === false) return "Fail";
            }
            else return "Running";
        };

        return Job;
    }]);

$ http (कट्टर):

angular.module('job.models', [])
    .service('JobManager', ['$q', '$http', 'Job', function($q, $http, Job) {
        return {
            getAll: function(limit) {
                var deferred = $q.defer();

                $http.get('/api/jobs?limit=' + limit + '&full=true').success(function(data) {
                    var jobs = [];
                    for (var i = 0; i < data.objects.length; i ++) {
                        jobs.push(new Job(data.objects[i]));
                    }
                    deferred.resolve(jobs);
                });

                return deferred.promise;
            }
        };
    }])
    .factory('Job', function() {
        function Job(data) {
            for (attr in data) {
                if (data.hasOwnProperty(attr))
                    this[attr] = data[attr];
            }
        }

        Job.prototype.getResult = function() {
            if (this.status == 'complete') {
                if (this.passed === null) return "Finished";
                else if (this.passed === true) return "Pass";
                else if (this.passed === false) return "Fail";
            }
            else return "Running";
        };

        return Job;
    });

ब्लॉग पोस्ट स्वयं तर्क के बारे में अधिक विस्तार से आगे बढ़ता है कि आप प्रत्येक दृष्टिकोण का उपयोग क्यों कर सकते हैं, साथ ही आपके नियंत्रकों में मॉडल का उपयोग करने के तरीके के कोड उदाहरण:

AngularJS डेटा मॉडल: $ http वीएस $ संसाधन वीएस Restangular

एक संभावना है कि कोणीय 2.0 डेटा मॉडलिंग के लिए एक अधिक मजबूत समाधान प्रदान करेगा जो सभी को एक ही पृष्ठ पर प्राप्त करता है।


यदि आप एकाधिक नियंत्रकों द्वारा उपयोग करने योग्य कुछ चाहते हैं तो आपको सेवाओं का उपयोग करना चाहिए। यहां एक सरल योगदान उदाहरण है:

myApp.factory('ListService', function() {
  var ListService = {};
  var list = [];
  ListService.getItem = function(index) { return list[index]; }
  ListService.addItem = function(item) { list.push(item); }
  ListService.removeItem = function(item) { list.splice(list.indexOf(item), 1) }
  ListService.size = function() { return list.length; }

  return ListService;
});

function Ctrl1($scope, ListService) {
  //Can add/remove/get items from shared list
}

function Ctrl2($scope, ListService) {
  //Can add/remove/get items from shared list
}

डीसीआई एक प्रतिमान है और इस तरह ऐसा करने का कोई कोणीय जेएस तरीका नहीं है, या तो भाषा डीसीआई का समर्थन करती है या नहीं। जेएस डीसीआई का समर्थन करता है अगर आप स्रोत परिवर्तन का उपयोग करने के इच्छुक हैं और यदि आप नहीं हैं तो कुछ कमियों के साथ। एक सी # कक्षा के मुकाबले डीसीआई के पास निर्भरता इंजेक्शन के साथ और कुछ नहीं करना है और निश्चित रूप से यह सेवा भी नहीं है। तो डीसीआई को एंजुलसजेएस के साथ करने का सबसे अच्छा तरीका डीसीआई जेएस रास्ता करना है, जो कि डीसीआई को पहली जगह कैसे तैयार किया जाता है, के करीब है। जब तक आप स्रोत परिवर्तन नहीं करते हैं, तो आप इसे पूरी तरह से करने में सक्षम नहीं होंगे क्योंकि भूमिका विधियां संदर्भ के बाहर भी ऑब्जेक्ट का हिस्सा होंगी, लेकिन आमतौर पर डीसीआई के आधार पर विधि इंजेक्शन के साथ समस्या होती है। यदि आप डीसीआई के लिए आधिकारिक साइट पर fullOO.info देखते हैं तो आप रूबी कार्यान्वयन पर एक नज़र डाल सकते हैं, वे विधि इंजेक्शन का भी उपयोग करते हैं या आप डीसीआई पर अधिक जानकारी के लिए here देख सकते हैं। यह ज्यादातर रूबी उदाहरणों के साथ है लेकिन डीसीआई सामान उस पर अज्ञेयवादी है। डीसीआई की चाबियों में से एक यह है कि सिस्टम जो करता है वह सिस्टम से अलग होता है। तो डेटा ऑब्जेक्ट बहुत गूंगा है लेकिन संदर्भ भूमिका में एक भूमिका के लिए बाध्य होने के बाद कुछ व्यवहार उपलब्ध कराते हैं। एक भूमिका केवल पहचानकर्ता है, और कुछ भी नहीं, उस पहचानकर्ता के माध्यम से किसी ऑब्जेक्ट तक पहुंचने पर, रोल विधियां उपलब्ध हैं। कोई भूमिका वस्तु / वर्ग नहीं है। विधि इंजेक्शन के साथ भूमिका विधियों की स्कॉप्लिंग बिल्कुल वर्णित लेकिन बंद नहीं है। जेएस में एक संदर्भ का एक उदाहरण हो सकता है

function transfer(source,destination){
   source.transfer = function(amount){
        source.withdraw(amount);
        source.log("withdrew " + amount);
        destination.receive(amount);
   };
   destination.receive = function(amount){
      destination.deposit(amount);
      destination.log("deposited " + amount);
   };
   this.transfer = function(amount){
    source.transfer(amount);
   };
}


जैसा कि अन्य पोस्टर्स द्वारा बताया गया है, एंगुलर मॉडलिंग के लिए ऑफ-ऑफ-द-बॉक्स बेस क्लास प्रदान करता है, लेकिन कोई उपयोगी रूप से कई फ़ंक्शन प्रदान कर सकता है:

  1. एक विश्वसनीय API के साथ बातचीत करने और नई वस्तुओं को बनाने के तरीके
  2. मॉडल के बीच संबंध स्थापित करना
  3. बैकएंड पर बने रहने से पहले डेटा मान्य करना; रीयल-टाइम त्रुटियों को प्रदर्शित करने के लिए भी उपयोगी है
  4. अपर्याप्त HTTP अनुरोध करने से बचने के लिए कैशिंग और आलसी लोडिंग
  5. राज्य मशीन हुक (सहेजने, अद्यतन करने, बनाने, नए, आदि के पहले / बाद में)

एक पुस्तकालय जो इन सभी चीजों को अच्छी तरह से करता है ngActiveResource ( https://github.com/FacultyCreative/ngActiveResource ) है। पूर्ण प्रकटीकरण - मैंने इस पुस्तकालय को लिखा - और मैंने इसे कई एंटरप्राइज़-स्केल अनुप्रयोगों के निर्माण में सफलतापूर्वक उपयोग किया है। यह अच्छी तरह से परीक्षण किया गया है, और एक एपीआई प्रदान करता है जो रेल डेवलपर्स से परिचित होना चाहिए।

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


मैं वर्तमान में इस पैटर्न की कोशिश कर रहा हूं, हालांकि, डीसीआई नहीं, एक शास्त्रीय सेवा / मॉडल decoupling प्रदान करता है (वेब ​​सेवाओं (उर्फ मॉडल सीआरयूडी) से बात करने के लिए सेवाओं के साथ, और वस्तु गुणों और विधियों को परिभाषित मॉडल)।

ध्यान दें कि जब भी मॉडल ऑब्जेक्ट को अपनी संपत्तियों पर काम करने की विधियों की आवश्यकता होती है, तो मैं केवल इस पैटर्न का उपयोग करता हूं, कि मैं शायद हर जगह उपयोग करूंगा (जैसे बेहतर गेटर / सेटर्स)। मैं व्यवस्थित रूप से हर सेवा के लिए ऐसा करने की वकालत नहीं कर रहा हूं।

संपादित करें: मुझे लगता है कि यह पैटर्न "कोणीय मॉडल सादा पुरानी जावास्क्रिप्ट ऑब्जेक्ट" मंत्र के खिलाफ होगा, लेकिन ऐसा लगता है कि यह पैटर्न बिल्कुल ठीक है।

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

सेवा / ElementServices.js (ध्यान दें कि घोषणा में तत्व को इंजेक्शन दिया गया है)

MyApp.service('ElementServices', function($http, $q, Element)
{
    this.getById = function(id)
    {
        return $http.get('/element/' + id).then(
            function(response)
            {
                //this is where the Element model is used
                return new Element(response.data);
            },
            function(response)
            {
                return $q.reject(response.data.error);
            }
        );
    };
    ... other CRUD methods
}

मॉडल / Element.js (ऑब्जेक्ट सृजन के लिए बने कोणीय फैक्ट्री का उपयोग करके)

MyApp.factory('Element', function()
{
    var Element = function(data) {
        //set defaults properties and functions
        angular.extend(this, {
            id:null,
            collection1:[],
            collection2:[],
            status:'NEW',
            //... other properties

            //dummy isNew function that would work on two properties to harden code
            isNew:function(){
                return (this.status=='NEW' || this.id == null);
            }
        });
        angular.extend(this, data);
    };
    return Element;
});

राज्य (और मॉडल) $ गुंजाइश में संग्रहीत हैं

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

प्रत्येक $ स्कोप में माता-पिता का दायरा होता है, रूट रूटस्कोप तक सभी तरह से एक पेड़ संरचना बनाते हैं जो आपके डोम को ढीला रूप से प्रतिबिंबित करता है। जब आप एक निर्देश कहते हैं जिसके लिए एक नया $ स्कोप की आवश्यकता होती है, जैसे कि एनजी-कंट्रोलर, एक नया $ स्कोप ऑब्जेक्ट बनाया जाएगा और पेड़ में जोड़ा जाएगा।

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

नियंत्रक $ गुंजाइश शुरू करते हैं

नियंत्रक का उद्देश्य $ गुंजाइश शुरू करना है । एक ही नियंत्रक पृष्ठ के विभिन्न हिस्सों में कई $ स्कोप ऑब्जेक्ट्स प्रारंभ कर सकता है। नियंत्रक को तुरंत चालू किया जाता है, $ स्कोप ऑब्जेक्ट सेट करता है और फिर बाहर निकलता है। आप पेज के विभिन्न हिस्सों में कई $ स्कोप शुरू करने के लिए एक ही नियंत्रक का उपयोग कर सकते हैं।

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

प्रोटोटाइपिकल स्कॉप्स

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

$ गुंजाइश सिर्फ एक सादा जावास्क्रिप्ट वस्तु है। एकाधिक वर्तमान छवि वस्तुओं के साथ एक सरणी बनाने के लिए यह एकाधिक $ स्कोप ऑब्जेक्ट्स बनाने के लिए और अधिक अपर्याप्त नहीं है। यह आपके कोड को व्यवस्थित करने का एक समझदार तरीका है।

इस तरह कोणीय पुराने "मैं अपना डेटा कहां स्टोर करूं" समस्या से दूर करता हूं जिसे हम अक्सर जावास्क्रिप्ट में पाते हैं। यह वास्तव में बड़ी उत्पादकता लाभों में से एक का स्रोत है जिसे हम कोणीय से प्राप्त करते हैं।

वैश्विक डेटा मिला (उदाहरण के लिए एक उपयोगकर्ता आईडी)? $ rootScope पर इसे स्टोर करें। स्थानीय डेटा मिला (उदाहरण के लिए एक गैलरी में एक मौजूदा छवि जहां कई गैलरी उदाहरण हैं)? इसे उस गैलरी से संबंधित $ स्कोप ऑब्जेक्ट पर स्टोर करें।

टेम्पलेट के सही हिस्से में $ स्कोप स्वचालित रूप से आपके लिए उपलब्ध है।

कोणीय मॉडल पतले हैं

एक रेल पृष्ठभूमि से आ रहा है जहां हम वसा मॉडल और पतला नियंत्रकों पर जोर देते हैं, मुझे एंगुलर के 'मुश्किल से' मॉडल आश्चर्यजनक लगते हैं। वास्तव में, आपके मॉडल में बहुत से व्यवसाय तर्क डालने से अक्सर लाइन के नीचे समस्याएं होती हैं, क्योंकि हम कभी-कभी रेल में उपयोगकर्ता मॉडल के साथ देखते हैं, यदि आप सावधान नहीं हैं, तो यह तब तक बढ़ेगा जब तक यह असंभव न हो जाए।

एक कोणीय मॉडल बस एक जावास्क्रिप्ट वस्तु या आदिम है।

कोई वस्तु एक मॉडल हो सकती है। मॉडल आमतौर पर नियंत्रक में JSON का उपयोग करके परिभाषित किए जाते हैं, या सर्वर से AJAXed। एक मॉडल एक JSON ऑब्जेक्ट हो सकता है, या हो सकता है कि केवल एक स्ट्रिंग, सरणी, या यहां तक ​​कि एक संख्या हो।

बेशक, आपके मॉडल में अतिरिक्त फ़ंक्शंस जोड़ने और जेएसओएन ऑब्जेक्ट में उन्हें संग्रहीत करने के लिए कुछ भी नहीं है, लेकिन यह एक प्रतिमान में पोर्टिंग होगा जो वास्तव में कोणीय के साथ फिट नहीं है।

कोणीय वस्तुएं आम तौर पर डेटा के भंडार होते हैं, न कि कार्य।

सामने के अंत में मॉडल वास्तविक मॉडल नहीं है

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

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

व्यापार तर्क सेवाओं में रह सकते हैं।

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

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

किसी सेवा में उदाहरण के लिए एक विश्वसनीय API से बात करने के लिए विधियां हो सकती हैं, या आपके डेटा को सत्यापित करने के लिए, या किसी अन्य काम को करने के लिए आपको शायद आवश्यकता हो सकती है।

सेवाएं मॉडल नहीं हैं

बेशक आपको सेवाओं के रूप में सेवाओं का उपयोग नहीं करना चाहिए। उन्हें वस्तुओं के रूप में उपयोग करें जो सामान कर सकते हैं। कभी-कभी वे आपके मॉडल में सामान करते हैं। यह सोचने का एक अलग तरीका है, लेकिन एक व्यावहारिक है।





javascript model-view-controller angularjs dci