Model verileri ve davranışları nereye yerleştirilir? [Tl; dr; Hizmetleri Kullan]


341

En son projem için AngularJS ile çalışıyorum. Dokümantasyon ve eğitimlerde tüm model verileri kontrolör kapsamına alınır. Bunun kontrolör için mevcut olması ve dolayısıyla ilgili görüşlerde olması gerektiğini anlıyorum.

Ancak modelin aslında orada uygulanması gerektiğini düşünmüyorum. Karmaşık olabilir ve örneğin özel nitelikleri olabilir. Ayrıca, kişi başka bir bağlamda / uygulamada yeniden kullanılabilir. Her şeyi denetleyiciye koymak MVC modelini tamamen bozar.

Aynı şey herhangi bir modelin davranışı için de geçerlidir. DCI mimarisini ve veri modelinden ayrı davranışı kullanırsam , davranışı korumak için ek nesneler tanıtmam gerekirdi. Bu, roller ve bağlamlar getirilerek yapılacaktı.

DCI == D ata ollaboration I nteraction

Elbette model verileri ve davranışları düz javascript nesneleri veya herhangi bir "sınıf" modeli ile uygulanabilir. Ama AngularJS'nin bunu yapmasının yolu ne olurdu? Hizmetleri mi kullanıyorsunuz?

Bu şu soruya geliyor:

AngularJS en iyi uygulamalarını izleyerek denetleyiciden ayrılmış modelleri nasıl uygularsınız?


12
DCI'yi tanımlayabilir veya en azından hecelenmiş formu sağlayabiliyorsanız bu soruyu daha iyi değerlendiririm. Bu kısaltmayı hiçbir yazılım literatüründe hiç görmedim. Teşekkürler.
Jim Raden

13
Referans olarak DCI için bir bağlantı ekledim.
Nils Blum-Oeste

1
@JimRaden DCI, Dataq, Bağlam, etkileşimdir ve öncelikle MVC'nin babası (Trygve Reenskauge) tarafından formüle edilen bir paradigmadır. Şu ana kadar bu konuda bir miktar edebi eser var. İyi bir okuma Coplien ve Bjørnvig "Yalın mimari" dir
Rune FS

3
Teşekkürler. Daha iyi ya da kötü için, çoğu insan şimdiye kadar orijinal literatürü bile bilmiyor. Google'a göre, MVC hakkında 55 milyon makale var, ancak MCI ve MVC'den sadece 250.000 tanesi var. Microsoft.com'da mı? 7. AngularJS.org, DCI kısaltmasından bile bahsetmiyor: "Aramanız - site: angularjs.org dci - hiçbir belgeyle eşleşmedi".
Jim Raden

Kaynak nesneler temelde Angular.js'deki modellerdir.
Salman von Abbas

Yanıtlar:


155

Birden fazla denetleyicinin kullanabileceği bir şey istiyorsanız hizmetleri kullanmalısınız. İşte basit bir örnek:

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
}

23
Bir hizmeti model olarak düz bir Javascript nesnesi oluşturmak ve bunu denetleyici kapsamına atamaktan faydalanmanın faydası nedir?
Nils Blum-Oeste

22
Birden fazla denetleyici arasında paylaşılan aynı mantığa ihtiyacınız varsa. Ayrıca, bu şekilde işleri bağımsız olarak test etmek daha kolaydır.
Andrew Joslin

1
Son örnek berbattı, bu daha mantıklı. Ben düzenledim.
Andrew Joslin

9
Evet, sade eski bir Javascript nesnesiyle ListService'inize Açısal bir şey enjekte edemezdiniz. Bu örnekte olduğu gibi, başlangıçta Liste verilerini almak için $ http.get adresine ihtiyacınız varsa veya $ broadScope'u enjekte etmeniz gerekiyorsa, böylece olayları $ yayınlayabilirsiniz.
Andrew Joslin

1
Bu örneği daha fazla DCI yapmak için veriler ListService dışında olmamalıdır?
PiTheNumber

81

Şu anda, DCI olmasa da, klasik bir hizmet / model ayrıştırması (web hizmetleri (aka model CRUD olarak) ve nesne özelliklerini ve yöntemlerini tanımlayan model ile) hizmetleri sağlayan bu modeli deniyorum.

Ben sadece model nesnesi kendi özellikleri üzerinde çalışan yöntemlere ihtiyaç duyduğunda , muhtemelen her yerde kullanacağım (örneğin gelişmiş getter / setters gibi) bu kalıbı kullandığımı unutmayın . Ben değilim değil savunan sistematik her hizmet için yapıyorum.

EDIT: Bu desen "açısal model düz eski javascript nesnesi" mantra karşı gideceğini düşünüyorum, ama bana öyle geliyor ki şimdi bu desen mükemmel para cezası.

DÜZENLEME (2): Daha da net olmak gerekirse, Model sınıfını yalnızca basit alıcıları / ayarlayıcıları hesaba katmak için kullanıyorum (örneğin: görünüm şablonlarında kullanılacak). Büyük iş mantığı için, model hakkında "bilen" ancak onlardan ayrı tutulan ve sadece iş mantığını içeren ayrı hizmet (ler) kullanmanızı öneririm. İsterseniz "işletme uzmanı" hizmet katmanı olarak adlandırın

service / ElementServices.js (bildirime Element'in nasıl enjekte edildiğine dikkat edin)

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
}

model / Element.js (nesne oluşturmak için yapılmış angularjs Factory kullanılarak)

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;
});

4
Sadece Angular'a giriyorum, ama gazilerin bunun sapkınlık olduğunu düşünüp düşünmediklerini merak ediyorum. Muhtemelen başlangıçta da bu şekilde yaklaşırım. Birisi geri bildirim verebilir mi?
Aaronius

2
@Aaronius sadece açık söylemek gerekirse: Ben herhangi bir angularjs doc veya blog üzerinde "asla bunu yapmamalı" hiç okumadım, ama her zaman "angularjs bir modele ihtiyaç duymaz, sadece düz eski javascript kullanıyor" gibi şeyler okudum ve bu modeli kendi başıma keşfetmek zorunda kaldım. Bu, AngularJS'deki ilk gerçek projem olduğundan, bu güçlü uyarıları koyuyorum, böylece insanlar önce düşünmeden kopyalamıyor / yapıştırmıyor.
Ben G

Kabaca benzer bir düzen benimsemiştim. Angular'ın "klasik" anlamda bir modelin gerçek bir desteği (veya görünüşte destekleme arzusu) olmaması utanç vericidir.
drt

3
Bu benim için bir sapkınlık gibi görünmüyor. "Angularjs'ın bir modele ihtiyacı yok" ifadesinin, özel bir sınıftan miras almanız veya açısal, msgstr "salt js nesnesi yeterli olacak" + + msgid ".
Felipe Castro

1
Her koleksiyon için uygun şekilde adlandırılmış bir ElementService'e sahip olmak, neredeyse aynı dosyalara neden olmaz mı?
Collin Allen

29

Angularjs belgeleri açıkça şunları belirtmektedir:

Diğer birçok çerçevenin aksine Angular, model üzerinde herhangi bir kısıtlama veya gereklilik yapmaz. Modelden devralınacak sınıflar veya modele erişmek veya model değiştirmek için özel erişimci yöntemleri yoktur. Model ilkel, nesne karması veya tam nesne türü olabilir. Kısacası, model sade bir JavaScript nesnesidir.

- AngularJS Geliştirici Kılavuzu - V1.5 Kavramları - Model

Yani bir modeli nasıl ilan edeceğiniz size bağlı. Basit bir Javascript nesnesidir.

Ben şahsen Açısal Hizmetleri kullanmayacağım, çünkü küresel durumları uygulamanızda tutmak için kullanabileceğiniz tekil nesneler gibi davranmaları gerekiyordu.


Bunun belgelerde belirtildiği yere bir bağlantı sağlamalısınız. "Açısal model üzerinde herhangi bir kısıtlama veya gereklilik oluşturmuyor " için bir Google araması yaptım ve resmi belgelerin hiçbir yerinde, anlayabildiğim kadarıyla ortaya çıkmıyor.

4
eski angularjs belgelerinde (cevap verirken hayatta kalan): github.com/gitsome/docular/blob/master/lib/angular/ngdocs/guide/…
SC

8

DCI bir paradigmadır ve bu nedenle bunu yapmanın herhangi bir açısalJS yolu yoktur, ya dil DCI'yi destekler ya da desteklemez. Kaynak dönüştürmeyi kullanmak istiyorsanız JSI DCI'yi iyi destekliyor ve eğer değilseniz bazı dezavantajları var. Yine DCI, bağımlılık enjeksiyonuyla bir C # sınıfının sahip olduğunu ve kesinlikle bir hizmet olmadığını söylemeye gerek yoktur. Bu nedenle angulusJS ile DCI yapmanın en iyi yolu, DCI'yi DCI yapmaktır, bu da DCI'nin ilk etapta nasıl formüle edildiğine oldukça yakındır. Kaynak dönüşümü yapmadığınız sürece, rol yöntemleri bağlamın dışında bile nesnenin bir parçası olacağından tam olarak yapamazsınız, ancak genellikle yöntem enjeksiyon tabanlı DCI ile ilgili sorun budur. Eğer bakarsanFullOO.info'yaDCI için yetkili site, yöntem enjeksiyonu kullandıkları yakut uygulamalara bir göz atabilir veya DCI hakkında daha fazla bilgi için buraya bakabilirsiniz . Çoğunlukla RUby örnekleriyle ilgilidir, ancak DCI şeyleri buna karşı agnostiktir. DCI'nin anahtarlarından biri, sistemin yaptığı şeyin sistemden ayrılmasıdır. Bu nedenle veri nesnesi oldukça aptaldır, ancak bir kez bağlamsal roldeki bir role bağlanır, belirli davranışları kullanılabilir hale getirir. Bir rol basitçe bir tanımlayıcıdır, başka bir şey değildir, bir tanımlayıcı üzerinden bir nesneye erişirken rol yöntemleri kullanılabilir. Rol nesnesi / sınıfı yok. Yöntem enjeksiyonu ile rol yöntemlerinin kapsamı tam olarak tarif edildiği gibi değil, yakındır. JS'deki bir bağlam örneği

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
DCI öğelerini ayrıntılı olarak açıkladığınız için teşekkür ederiz. Harika bir okuma. Ama sorularım gerçekten "model nesneleri açısallara nereye koyacağımızı" hedefliyor. DCI sadece referans için orada, sadece bir modelim olmayabilir, aynı zamanda DCI tarzında bölebilirim. Soruyu daha açık hale getirmek için düzenleyecektir.
Nils Blum-Oeste

7

AngularJS'deki modeller hakkında bu makale yardımcı olabilir:

http://joelhooks.com/blog/2013/04/24/modeling-data-and-state-in-your-angularjs-application/


7
Not o bağlantı sadece cevaplar tavsiye edilmez, SO cevaplar çözüm (vs. henüz zamanla bayat almak eğilimindedir referanslar, başka mola) için bir aramanın son nokta olmalıdır. Lütfen bağlantıyı referans olarak tutarak bağımsız bir özet eklemeyi düşünün.
Kleopatra

soru üzerine bir yorum böyle bir bağlantı eklemek olsa iyi olurdu.
jorrebor

Bu bağlantı aslında çok iyi bir makale, ancak SO için uygun olmak için bir cevap haline getirilmesi gerekecek
Jeremy Zerr

5

Diğer posterlerde belirtildiği gibi, Angular modelleme için temel sınıf sınıfını sağlamaz, ancak birkaç işlev yararlı olabilir:

  1. RESTful API ile etkileşim ve yeni nesneler oluşturma yöntemleri
  2. Modeller arasında ilişki kurma
  3. Arka uca devam etmeden önce verilerin doğrulanması; gerçek zamanlı hataları görüntülemek için de yararlıdır
  4. Önemsiz HTTP istekleri yapmaktan kaçınmak için önbellekleme ve tembel yükleme
  5. Durum makine kancaları (kaydetmeden önce / sonra güncelleme, oluşturma, yeni, vb.)

Tüm bunları iyi yapan bir kütüphane ngActiveResource'dur ( https://github.com/FacultyCreative/ngActiveResource ). Tam açıklama - Bu kütüphaneyi yazdım - ve bunu kurumsal ölçekli birçok uygulama geliştirmede başarıyla kullandım. İyi test edilmiştir ve Rails geliştiricilerine aşina olması gereken bir API sağlar.

Ekibim ve ben bu kütüphaneyi aktif olarak geliştirmeye devam ediyoruz ve daha fazla Açısal geliştiricinin ona katkıda bulunmasını ve savaş testini yapmasını isterim.


Hey! Bu gerçekten harika! Şu anda uygulamama takacağım. Savaş testi yeni başladı.
J. Bruni

1
Ben de sadece gönderiye bakıyorum ve senin ngActiveResourceve Angular'ın $resourceservisi arasındaki farkların ne olduğunu merak ediyordum . Angular için biraz yeniyim ve her iki dokümana hızlı bir şekilde göz attım, ancak çok fazla örtüşme sunuyorlar. Oldu ngActiveResourceöncesinde geliştirilen $resourcemevcut olan hizmet?
Eric

5

Daha eski bir soru, ama bence konu Angular 2.0'ın yeni yönü göz önüne alındığında her zamankinden daha alakalı. En iyi uygulama, belirli bir çerçeveye mümkün olduğunca az bağımlılık içeren kod yazmaktır. Yalnızca doğrudan değer kattığı çerçeveye özgü parçaları kullanın.

Şu anda Angular hizmeti, onu yeni nesil Angular nesline dönüştürecek birkaç kavramdan biri gibi görünüyor, bu nedenle tüm mantığı hizmetlere taşıma genel yönergelerini takip etmek muhtemelen akıllıdır. Ancak, açısal hizmetlere doğrudan bağımlı olmadan bile ayrıştırılmış modeller yapabileceğinizi iddia ediyorum. Sadece gerekli bağımlılıklar ve sorumluluklarla kendi kendine yeten nesneler yaratmak muhtemelen yoludur. Otomatik test yaparken hayatı çok daha kolay hale getirir. Tek sorumluluk bugünlerde bir vızıltı iş, ama çok mantıklı!

İşte nesne modelini dom'dan ayırmak için iyi olduğunu düşündüğüm bir pıtırtı örneği.

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

Önemli bir amaç, kodunuzu bir birim testlerden bir görünüm kadar kullanımı kolaylaştıracak şekilde yapılandırmaktır. Bunu başarırsanız, gerçekçi ve kullanışlı testler yazmak için iyi bir konumdasınız demektir.


4

Bu blog yazısında bu sorunun üstesinden gelmeye çalıştım .

Temel olarak, veri modelleme için en iyi yer hizmetler ve fabrikalardadır. Ancak, verilerinizi nasıl alacağınıza ve ihtiyacınız olan davranışların karmaşıklığına bağlı olarak, uygulama hakkında gitmenin birçok farklı yolu vardır. Açısal şu ​​anda standart bir yol veya en iyi uygulama yoktur.

Gönderi , $ http , $ resource ve Restangular kullanarak üç yaklaşımı kapsar .

İşte getResult()Job modelinde özel bir yöntemle her biri için bazı örnek kodlar :

Dikdörtgen (kolay bezelye):

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;
  }]);

$ resource (biraz daha kıvrımlı):

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 (hardcore):

angular.module('job.models', [])
    .service('JobManager', ['$http', 'Job', function($http, Job) {
        return {
            getAll: function(limit) {
                var params = {"limit": limit, "full": 'true'};
                return $http.get('/api/jobs', {params: params})
                  .then(function(response) {
                    var data = response.data;
                    var jobs = [];
                    for (var i = 0; i < data.objects.length; i ++) {
                        jobs.push(new Job(data.objects[i]));
                    }
                    return jobs;
                });
            }
        };
    }])
    .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;
    });

Blog yazısının kendisi, her bir yaklaşımı neden kullanabileceğinizin ardındaki mantık ve modellerin denetleyicilerinizde nasıl kullanılacağına ilişkin kod örnekleriyle ilgili daha ayrıntılı bilgi verir:

AngularJS Veri Modelleri: $ http VS $ resource VS Restangular

Angular 2.0'ın, herkese aynı sayfaya ulaşmasını sağlayan veri modelleme için daha sağlam bir çözüm sunma olasılığı vardır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.