Di mana menempatkan data model dan perilaku? [tl; dr; Gunakan Layanan]


341

Saya bekerja dengan AngularJS untuk proyek terbaru saya. Dalam dokumentasi dan tutorial semua data model dimasukkan ke dalam lingkup pengontrol. Saya mengerti bahwa harus ada di sana agar tersedia untuk controller dan dengan demikian dalam pandangan yang sesuai.

Namun saya tidak berpikir model ini harus diimplementasikan di sana. Ini mungkin kompleks dan memiliki atribut pribadi misalnya. Lebih jauh lagi, seseorang mungkin ingin menggunakannya kembali dalam konteks / aplikasi lain. Menempatkan semuanya ke dalam pengontrol benar-benar merusak pola MVC.

Hal yang sama berlaku untuk perilaku model apa pun. Jika saya akan menggunakan arsitektur DCI dan memisahkan perilaku dari model data, saya harus memperkenalkan objek tambahan untuk menahan perilaku. Ini akan dilakukan dengan memperkenalkan peran dan konteks.

DCI == D ata C ollaboration I nteraction

Tentu saja data model dan perilaku dapat diimplementasikan dengan objek javascript biasa atau pola "kelas" apa pun. Tapi apa yang akan menjadi cara Angular untuk melakukannya? Menggunakan layanan?

Jadi sampai pada pertanyaan ini:

Bagaimana Anda menerapkan model yang dipisahkan dari pengontrol, mengikuti praktik terbaik AngularJS?


12
Saya akan menjawab pertanyaan ini jika Anda dapat mendefinisikan DCI atau setidaknya memberikan formulir yang dieja. Saya belum pernah melihat akronim ini dalam literatur perangkat lunak apa pun. Terima kasih.
Jim Raden

13
Saya baru saja menambahkan tautan untuk DCI sebagai referensi.
Nils Blum-Oeste

1
@JimRaden DCI adalah Dataq, Konteks, interaksi dan merupakan paradigma yang pertama kali dirumuskan oleh bapak MVC (Trygve Reenskauge). Ada beberapa literatur tentang subjek sekarang. Bacaan yang bagus adalah Coplien dan Bjørnvig "Lean architecture"
Rune FS

3
Terima kasih. Baik atau buruk, kebanyakan orang bahkan belum tahu tentang literatur asli sekarang. Ada 55 juta artikel tentang MVC, menurut Google, tetapi hanya 250.000 yang menyebutkan MCI dan MVC. Dan di Microsoft.com? 7. AngularJS.org bahkan tidak menyebutkan akronim DCI: "Pencarian Anda - situs: angularjs.org dci - tidak cocok dengan dokumen mana pun".
Jim Raden

Objek sumber daya pada dasarnya adalah model di Angular.js .. Saya memperluasnya.
Salman von Abbas

Jawaban:


155

Anda harus menggunakan layanan jika Anda menginginkan sesuatu yang dapat digunakan oleh banyak pengontrol. Ini contoh sederhana yang dibuat:

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
Apa manfaat menggunakan layanan daripada hanya membuat objek Javascript biasa sebagai model dan menugaskan ini ke lingkup pengontrol?
Nils Blum-Oeste

22
Jika Anda membutuhkan logika yang sama yang digunakan bersama antara banyak pengontrol. Juga, dengan cara ini lebih mudah untuk menguji berbagai hal secara mandiri.
Andrew Joslin

1
Contoh terakhir jenis tersedot, yang ini lebih masuk akal. Saya mengeditnya.
Andrew Joslin

9
Ya, dengan objek Javascript biasa, Anda tidak akan dapat menyuntikkan sesuatu yang Angular ke dalam ListService Anda. Seperti dalam contoh ini, jika Anda perlu $ http. Dapatkan untuk mengambil data Daftar di awal, atau jika Anda perlu menyuntikkan $ rootScope sehingga Anda dapat $ menyiarkan acara.
Andrew Joslin

1
Untuk membuat contoh ini lebih seperti DCI, bukankah seharusnya data di luar ListService?
PiTheNumber

81

Saya sedang mencoba pola ini, yang, meskipun bukan DCI, menyediakan layanan decoupling model / klasik (dengan layanan untuk berbicara dengan layanan web (alias model CRUD), dan model mendefinisikan properti objek dan metode).

Perhatikan bahwa saya hanya menggunakan pola ini setiap kali objek model membutuhkan metode yang bekerja pada propertinya sendiri , yang mungkin akan saya gunakan di mana-mana (seperti peningkatan pengambil / setter). Saya tidak menganjurkan melakukan ini untuk setiap layanan secara sistematis.

EDIT: Saya dulu berpikir pola ini akan bertentangan dengan mantra "Model sudut adalah objek javascript tua biasa", tetapi tampaknya bagi saya sekarang bahwa pola ini baik-baik saja.

EDIT (2): Agar lebih jelas, saya menggunakan kelas Model hanya untuk faktor getter / setter sederhana (misalnya: untuk digunakan dalam tampilan templat). Untuk logika bisnis besar, saya sarankan menggunakan layanan terpisah yang "tahu" tentang model, tetapi tetap terpisah dari mereka, dan hanya menyertakan logika bisnis. Sebut saja lapisan layanan "pakar bisnis" jika Anda mau

service / ElementServices.js (perhatikan bagaimana Elemen disuntikkan dalam deklarasi)

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 (menggunakan angularjs Factory, dibuat untuk pembuatan objek)

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
Saya baru saja masuk ke Angular, tetapi saya ingin tahu jika / mengapa para veteran berpikir ini bid'ah. Ini mungkin cara saya awalnya akan mendekatinya juga. Bisakah seseorang memberikan umpan balik?
Aaronius

2
@Aaronius hanya untuk menjadi jelas: saya tidak pernah benar-benar membaca "Anda seharusnya tidak pernah melakukan itu" pada dokumen atau blog angularjs, tetapi saya selalu membaca hal-hal seperti "angularjs tidak memerlukan model, itu hanya menggunakan javascript biasa" , dan saya harus menemukan pola ini sendiri. Karena ini adalah proyek nyata pertama saya di AngularJS, saya menaruh peringatan kuat itu, sehingga orang tidak menyalin / menempel tanpa berpikir terlebih dahulu.
Ben G

Saya telah menetapkan pola yang kira-kira serupa. Sayang sekali bahwa Angular tidak memiliki dukungan nyata (atau tampaknya keinginan untuk mendukung) model dalam arti "klasik".
drt

3
Itu tidak terlihat seperti bid'ah bagi saya, Anda menggunakan pabrik untuk apa mereka diciptakan: membangun objek. Saya percaya "angularjs tidak memerlukan model" frase berarti "Anda tidak perlu mewarisi dari kelas khusus, atau menggunakan metode khusus (seperti ko.observable, dalam sistem gugur) untuk bekerja dengan model dalam sudut, sebuah objek js murni akan cukup ".
Felipe Castro

1
Bukankah memiliki ElementService yang dinamai sesuai untuk setiap koleksi menghasilkan banyak file yang hampir identik?
Collin Allen

29

Dokumentasi Angularjs dengan jelas menyatakan:

Tidak seperti banyak kerangka kerja lainnya, Angular tidak membuat batasan atau persyaratan pada model. Tidak ada kelas untuk diwarisi dari atau metode aksesor khusus untuk mengakses atau mengubah model. Model dapat berupa primitif, objek hash, atau tipe objek penuh. Singkatnya model adalah objek JavaScript biasa.

- Panduan Pengembang AngularJS - Konsep V1.5 - Model

Jadi itu artinya terserah Anda bagaimana mendeklarasikan model. Ini adalah objek Javascript sederhana.

Saya pribadi tidak akan menggunakan Layanan Angular karena mereka dimaksudkan untuk berperilaku seperti objek tunggal yang dapat Anda gunakan, misalnya, untuk menjaga status global di seluruh aplikasi Anda.


Anda harus memberikan tautan ke tempat ini dinyatakan dalam dokumentasi. Saya melakukan pencarian Google untuk "Angular tidak membuat batasan atau persyaratan pada model" , dan itu tidak muncul di mana pun di dokumen resmi, sejauh yang saya tahu.

4
itu ada di dokumen angularjs lama (yang masih hidup saat menjawab): github.com/gitsome/docular/blob/master/lib/angular/ngdocs/guide/…
SC

8

DCI adalah sebuah paradigma dan karena itu tidak ada cara sudut pandang untuk melakukannya, baik bahasa mendukung DCI atau tidak. JS mendukung DCI dengan cukup baik jika Anda bersedia menggunakan transformasi sumber dan dengan beberapa kekurangan jika Anda tidak. Lagi-lagi DCI tidak ada hubungannya dengan injeksi ketergantungan daripada yang dikatakan kelas C # dan jelas bukan layanan juga. Jadi cara terbaik untuk melakukan DCI dengan angulusJS adalah dengan melakukan DCI dengan cara JS, yang cukup dekat dengan bagaimana DCI dirumuskan di tempat pertama. Kecuali Anda melakukan transformasi sumber, Anda tidak akan dapat melakukannya sepenuhnya karena metode peran akan menjadi bagian dari objek bahkan di luar konteks tetapi itu umumnya masalah dengan metode injeksi berbasis DCI. Jika Anda melihat fullOO.infositus otoritatif untuk DCI Anda dapat melihat implementasi ruby ​​mereka juga menggunakan metode injeksi atau Anda bisa melihat di sini untuk informasi lebih lanjut tentang DCI. Sebagian besar dengan contoh RUby tetapi hal-hal DCI agnostik untuk itu. Salah satu kunci DCI adalah apa yang dilakukan sistem dipisahkan dari apa sistem itu. Jadi objek data cukup bodoh tetapi sekali terikat pada peran dalam konteks, metode peran membuat perilaku tertentu tersedia. Peran hanyalah pengidentifikasi, tidak lebih, ketika mengakses objek melalui pengenal itu maka metode peran tersedia. Tidak ada objek peran / kelas. Dengan injeksi metode, pelingkupan metode peran tidak persis seperti yang dijelaskan tetapi ditutup. Contoh konteks dalam JS bisa jadi

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
Terima kasih telah menguraikan hal-hal DCI. Ini adalah bacaan yang bagus. Tapi pertanyaan saya benar-benar bertujuan "di mana harus meletakkan objek model di angularjs". DCI hanya ada di sana untuk referensi, bahwa saya mungkin tidak hanya memiliki model, tetapi membaginya dengan cara DCI. Akan mengedit pertanyaan untuk membuatnya lebih jelas.
Nils Blum-Oeste

7

Artikel tentang model-model di AngularJS ini dapat membantu:

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


7
Perhatikan bahwa jawaban hanya tautan tidak disarankan, jawaban SO harus menjadi titik akhir pencarian solusi (vs. persinggahan referensi lainnya, yang cenderung menjadi basi seiring waktu). Harap pertimbangkan untuk menambahkan sinopsis mandiri di sini, dengan menjaga tautan sebagai referensi.
kleopatra

menambahkan tautan seperti itu dalam komentar pada pertanyaan akan baik-baik saja.
jorrebor

Tautan ini sebenarnya adalah artikel yang sangat bagus, tetapi juga harus dibuat menjadi jawaban yang tepat untuk SO
Jeremy Zerr

5

Seperti yang dinyatakan oleh poster lain, Angular tidak menyediakan kelas dasar out-of-the-box untuk pemodelan, tetapi orang dapat dengan bermanfaat menyediakan beberapa fungsi:

  1. Metode untuk berinteraksi dengan API tenang dan membuat objek baru
  2. Membangun hubungan antar model
  3. Memvalidasi data sebelum bertahan ke backend; juga berguna untuk menampilkan kesalahan waktu nyata
  4. Caching dan lazy-loading agar tidak membuat permintaan HTTP yang boros
  5. Sebutkan kait mesin (sebelum / sesudah menyimpan, memperbarui, membuat, baru, dll)

Salah satu perpustakaan yang melakukan semua hal ini dengan baik adalah ngActiveResource ( https://github.com/FacultyCreative/ngActiveResource ). Pengungkapan penuh - saya menulis perpustakaan ini - dan saya telah menggunakannya dengan sukses dalam membangun beberapa aplikasi skala perusahaan. Ini sudah teruji dengan baik, dan menyediakan API yang seharusnya tidak asing bagi pengembang Rails.

Tim saya dan saya terus mengembangkan perpustakaan ini secara aktif, dan saya ingin melihat lebih banyak pengembang Angular berkontribusi untuk itu dan berjuang mengujinya.


Hei! Ini sangat bagus! Saya akan tancapkan ke aplikasi saya sekarang. Pengujian pertempuran baru saja dimulai.
J. Bruni

1
Saya hanya melihat posting Anda dan bertanya-tanya apa perbedaan antara layanan Anda ngActiveResourcedan Angular $resource. Saya sedikit baru untuk Angular, dan dengan cepat melihat-lihat kedua set dokumen, tetapi tampaknya menawarkan banyak tumpang tindih. Apakah ngActiveResourcedikembangkan sebelum $resourcelayanan tersedia?
Eric B.

5

Sebuah pertanyaan yang lebih tua, tapi saya pikir topiknya lebih relevan dari sebelumnya, diberikan arah baru Angular 2.0. Saya akan mengatakan praktik terbaik adalah menulis kode dengan sesedikit mungkin dependensi pada kerangka kerja tertentu. Hanya gunakan kerangka kerja bagian tertentu di mana itu menambah nilai langsung.

Saat ini sepertinya layanan Angular adalah salah satu dari beberapa konsep yang akan membuatnya ke generasi Angular berikutnya, jadi mungkin pintar untuk mengikuti pedoman umum memindahkan semua logika ke layanan. Namun, saya berpendapat bahwa Anda dapat membuat model yang dipisahkan bahkan tanpa ketergantungan langsung pada layanan Angular. Membuat objek mandiri dengan hanya ketergantungan dan tanggung jawab yang diperlukan mungkin adalah cara untuk pergi. Ini juga membuat hidup jauh lebih mudah ketika melakukan pengujian otomatis. Tanggung jawab tunggal adalah kerja keras hari ini, tetapi itu membuat banyak akal!

Berikut adalah contoh derai yang saya anggap bagus untuk memisahkan model objek dari dom.

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

Tujuan utama adalah untuk menyusun kode Anda sedemikian rupa sehingga membuatnya mudah digunakan dari unit test seperti dari tampilan. Jika Anda mencapai itu, Anda memiliki posisi yang baik untuk menulis tes yang realistis dan berguna.


4

Saya sudah mencoba untuk mengatasi masalah yang tepat di posting blog ini .

Pada dasarnya, rumah terbaik untuk pemodelan data adalah di layanan dan pabrik. Namun, tergantung pada bagaimana Anda mengambil data Anda dan kompleksitas perilaku yang Anda butuhkan, ada banyak cara berbeda untuk menerapkannya. Angular saat ini tidak memiliki cara standar atau praktik terbaik.

Posting ini mencakup tiga pendekatan, menggunakan $ http , $ resource , dan Restangular .

Berikut ini beberapa contoh kode untuk masing-masing, dengan getResult()metode khusus pada model Pekerjaan:

Restangular (peasy mudah):

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 (sedikit lebih berbelit-belit):

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

Posting blog itu sendiri menjadi lebih rinci tentang alasan di balik mengapa Anda mungkin menggunakan setiap pendekatan, serta contoh kode tentang cara menggunakan model dalam pengontrol Anda:

Model Data AngularJS: $ http VS $ resource VS Restangular

Ada kemungkinan Angular 2.0 akan menawarkan solusi yang lebih kuat untuk pemodelan data yang membuat semua orang di halaman yang sama.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.