AngularJS: Cara yang benar untuk mengikat ke properti layanan

162

Saya mencari praktik terbaik tentang cara mengikat ke properti layanan di AngularJS.

Saya telah bekerja melalui beberapa contoh untuk memahami cara mengikat properti di layanan yang dibuat menggunakan AngularJS.

Di bawah ini saya memiliki dua contoh cara mengikat properti di layanan; keduanya bekerja. Contoh pertama menggunakan binding dasar dan contoh kedua menggunakan $ scope. $ Watch untuk mengikat ke properti layanan

Apakah salah satu dari contoh ini lebih disukai ketika mengikat ke properti dalam layanan atau apakah ada opsi lain yang saya tidak tahu yang akan direkomendasikan?

Premis dari contoh-contoh ini adalah bahwa layanan harus memperbarui propertinya "lastUpdated" dan "calls" setiap 5 detik. Setelah properti layanan diperbarui, tampilan harus mencerminkan perubahan ini. Kedua contoh ini berhasil; Saya ingin tahu apakah ada cara yang lebih baik untuk melakukannya.

Binding Dasar

Kode berikut dapat dilihat dan dijalankan di sini: http://plnkr.co/edit/d3c16z

<html>
<body ng-app="ServiceNotification" >

    <div ng-controller="TimerCtrl1" style="border-style:dotted"> 
        TimerCtrl1 <br/>
        Last Updated: {{timerData.lastUpdated}}<br/>
        Last Updated: {{timerData.calls}}<br/>
    </div>

    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.5/angular.js"></script>
    <script type="text/javascript">
        var app = angular.module("ServiceNotification", []);

        function TimerCtrl1($scope, Timer) {
            $scope.timerData = Timer.data;
        };

        app.factory("Timer", function ($timeout) {
            var data = { lastUpdated: new Date(), calls: 0 };

            var updateTimer = function () {
                data.lastUpdated = new Date();
                data.calls += 1;
                console.log("updateTimer: " + data.lastUpdated);

                $timeout(updateTimer, 5000);
            };
            updateTimer();

            return {
                data: data
            };
        });
    </script>
</body>
</html>

Cara lain saya memecahkan ikatan ke properti layanan adalah dengan menggunakan $ scope. $ Watch di controller.

$ scope. $ watch

Kode berikut dapat dilihat dan dijalankan di sini: http://plnkr.co/edit/dSBlC9

<html>
<body ng-app="ServiceNotification">
    <div style="border-style:dotted" ng-controller="TimerCtrl1">
        TimerCtrl1<br/>
        Last Updated: {{lastUpdated}}<br/>
        Last Updated: {{calls}}<br/>
    </div>

    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.5/angular.js"></script>
    <script type="text/javascript">
        var app = angular.module("ServiceNotification", []);

        function TimerCtrl1($scope, Timer) {
            $scope.$watch(function () { return Timer.data.lastUpdated; },
                function (value) {
                    console.log("In $watch - lastUpdated:" + value);
                    $scope.lastUpdated = value;
                }
            );

            $scope.$watch(function () { return Timer.data.calls; },
                function (value) {
                    console.log("In $watch - calls:" + value);
                    $scope.calls = value;
                }
            );
        };

        app.factory("Timer", function ($timeout) {
            var data = { lastUpdated: new Date(), calls: 0 };

            var updateTimer = function () {
                data.lastUpdated = new Date();
                data.calls += 1;
                console.log("updateTimer: " + data.lastUpdated);

                $timeout(updateTimer, 5000);
            };
            updateTimer();

            return {
                data: data
            };
        });
    </script>
</body>
</html>

Saya sadar bahwa saya dapat menggunakan $ rootscope. $ Broadcast di layanan dan $ root. $ Di controller, tetapi dalam contoh lain yang saya buat yang menggunakan $ broadcast / $ pada siaran pertama tidak ditangkap oleh pengontrol, tetapi panggilan tambahan yang disiarkan dipicu di dalam pengontrol. Jika Anda mengetahui cara untuk menyelesaikan $ rootscope. $ Masalah siaran, berikan jawaban.

Tetapi untuk menyatakan kembali apa yang saya sebutkan sebelumnya, saya ingin mengetahui praktik terbaik tentang cara mengikat ke properti layanan.


Memperbarui

Pertanyaan ini awalnya diajukan dan dijawab pada April 2013. Pada Mei 2014, Gil Birman memberikan jawaban baru, yang saya ubah sebagai jawaban yang benar. Karena jawaban Gil Birman memiliki suara yang sangat sedikit, kekhawatiran saya adalah bahwa orang yang membaca pertanyaan ini akan mengabaikan jawaban yang mendukung jawaban lain dengan lebih banyak suara. Sebelum Anda membuat keputusan tentang apa jawaban terbaik, saya sangat merekomendasikan jawaban Gil Birman.

Mike Barlow - BarDev
sumber
Saya pikir jawaban Josh David Miller lebih baik daripada Gil Birman. Dan menggunakan $ watch, $ watchGroup dan $ watchCollection bahkan dapat membuatnya lebih baik. Pemisahan masalah sangat penting pada aplikasi ukuran sedang hingga besar.
Jonathan
@ Bardard Saya pikir kedua jawaban itu tidak berguna, dan pengembang baru akan memahami mereka sepenuhnya salah.
Zalaboza
Masalah yang Anda tanyakan adalah mengenai perilaku variabel JavaScript asli dari objek referensi, saya menambahkan sedikit penjelasan di bawah ini
Zalaboza

Jawaban:

100

Pertimbangkan beberapa pro dan kontra dari pendekatan kedua :

  • 0 {{lastUpdated}} bukannya {{timerData.lastUpdated}}, yang bisa saja dengan mudah {{timer.lastUpdated}}, yang menurut saya lebih mudah dibaca (tapi jangan berdebat ... Saya memberikan nilai netral pada poin ini sehingga Anda memutuskan sendiri)

  • +1 Mungkin lebih nyaman bahwa controller bertindak sebagai semacam API untuk markup sedemikian rupa sehingga jika entah bagaimana struktur model data berubah, Anda dapat (secara teori) memperbarui pemetaan API pengontrol tanpa menyentuh sebagian html.

  • -1 Namun, teori tidak selalu berlatih dan saya biasanya menemukan diri saya harus memodifikasi markup dan logic controller ketika perubahan dipanggil untuk, pula . Jadi upaya ekstra untuk menulis API meniadakan kelebihannya.

  • -1 Selanjutnya, pendekatan ini tidak terlalu KERING.

  • -1 Jika Anda ingin mengikat data ke ng-modelkode Anda menjadi lebih kering karena Anda harus mengemas $scope.scalar_valuesulang pengontrol untuk membuat panggilan REST baru.

  • -0.1 Ada kinerja kecil yang membuat pengamat ekstra. Juga, jika properti data melekat pada model yang tidak perlu diawasi dalam pengontrol tertentu mereka akan membuat overhead tambahan untuk pengamat dalam.

  • -1 Bagaimana jika beberapa pengontrol memerlukan model data yang sama? Itu berarti Anda memiliki beberapa API untuk diperbarui dengan setiap perubahan model.

$scope.timerData = Timer.data;mulai terdengar sangat menggoda saat ini ... Mari selami titik terakhir ... Perubahan model seperti apa yang sedang kita bicarakan? Model di back-end (server)? Atau model yang dibuat dan hanya hidup di front-end? Dalam kedua kasus tersebut, apa yang pada dasarnya API pemetaan data termasuk dalam lapisan layanan front-end , (pabrik atau layanan sudut). (Perhatikan bahwa contoh pertama Anda - preferensi saya - tidak memiliki API di lapisan layanan , yang baik-baik saja karena cukup sederhana tidak memerlukannya.)

Kesimpulannya , semuanya tidak harus dipisahkan. Dan sejauh memisahkan markup sepenuhnya dari model data, kelemahannya lebih besar dari keuntungannya.


Pengendali, secara umum tidak boleh dikotori dengan $scope = injectable.data.scalar. Sebaliknya, mereka harus ditaburi dengan $scope = injectable.data's, promise.then(..)' s, dan$scope.complexClickAction = function() {..} 's

Sebagai pendekatan alternatif untuk mencapai data-decoupling dan dengan demikian view-encapsulation, satu-satunya tempat yang benar-benar masuk akal untuk memisahkan pandangan dari model adalah dengan arahan . Tetapi bahkan di sana, jangan $watchnilai skalar dalam controlleratau linkfungsi. Itu tidak akan menghemat waktu atau membuat kode lebih mudah dikelola atau dibaca. Bahkan tidak akan membuat pengujian lebih mudah karena tes yang kuat di sudut biasanya menguji DOM yang dihasilkan pula . Sebaliknya, dalam permintaan direktif API data Anda dalam bentuk objek, dan nikmatilah hanya dengan yang $watchdibuat oleh ng-bind.


Contoh http://plnkr.co/edit/MVeU1GKRTN4bqA3h9Yio

<body ng-app="ServiceNotification">
    <div style="border-style:dotted" ng-controller="TimerCtrl1">
        TimerCtrl1<br/>
        Bad:<br/>
        Last Updated: {{lastUpdated}}<br/>
        Last Updated: {{calls}}<br/>
        Good:<br/>
        Last Updated: {{data.lastUpdated}}<br/>
        Last Updated: {{data.calls}}<br/>
    </div>

    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.5/angular.js"></script>
    <script type="text/javascript">
        var app = angular.module("ServiceNotification", []);

        function TimerCtrl1($scope, Timer) {
            $scope.data = Timer.data;
            $scope.lastUpdated = Timer.data.lastUpdated;
            $scope.calls = Timer.data.calls;
        };

        app.factory("Timer", function ($timeout) {
            var data = { lastUpdated: new Date(), calls: 0 };

            var updateTimer = function () {
                data.lastUpdated = new Date();
                data.calls += 1;
                console.log("updateTimer: " + data.lastUpdated);

                $timeout(updateTimer, 500);
            };
            updateTimer();

            return {
                data: data
            };
        });
    </script>
</body>

MEMPERBARUI : Saya akhirnya kembali ke pertanyaan ini untuk menambahkan bahwa saya tidak berpikir bahwa kedua pendekatan itu "salah". Awalnya saya telah menulis bahwa jawaban Josh David Miller tidak benar, tetapi dalam retrospeksi poinnya sepenuhnya valid, terutama pendapatnya tentang pemisahan kekhawatiran.

Mengesampingkan kekhawatiran (tetapi terkait secara tangensial), ada alasan lain untuk menyalin defensif yang gagal saya pertimbangkan. Pertanyaan ini sebagian besar berkaitan dengan membaca data langsung dari suatu layanan. Tetapi bagaimana jika pengembang di tim Anda memutuskan bahwa pengontrol perlu mengubah data dengan cara yang sepele sebelum tampilan menampilkannya? (Apakah pengontrol harus mengubah data sama sekali adalah diskusi lain.) Jika dia tidak membuat salinan objek terlebih dahulu, dia mungkin tanpa disadari menyebabkan regresi dalam komponen tampilan lain yang menggunakan data yang sama.

Apa yang benar-benar disoroti oleh pertanyaan ini adalah kekurangan arsitektural dari aplikasi sudut tipikal (dan benar-benar aplikasi JavaScript apa pun): penggabungan perhatian yang ketat, dan mutabilitas objek. Saya baru-baru ini menjadi terpikat dengan aplikasi arsitek dengan Bereaksi dan struktur data yang tidak berubah. Melakukan hal itu akan menyelesaikan dua masalah berikut dengan luar biasa:

  1. Pemisahan masalah : Komponen mengkonsumsi semua data itu melalui alat peraga dan memiliki sedikit ketergantungan pada singleton global (seperti layanan Angular), dan tidak tahu apa-apa tentang apa yang terjadi di atasnya dalam hierarki tampilan.

  2. Mutabilitas : Semua properti tidak dapat diubah yang menghilangkan risiko mutasi data tanpa disadari.

Angular 2.0 sekarang di jalur untuk meminjam banyak dari Bereaksi untuk mencapai dua poin di atas.

Gil Birman
sumber
1
Semakin banyak saya bekerja dengan Angular, semakin saya mengerti. Saya percaya bahwa pengontrol AngularJS harus sesederhana dan setipis mungkin. Dengan menambahkan $ watches pada pengontrol, hal itu menyulitkan logika. Dengan hanya merujuk nilai dalam layanan itu jauh lebih sederhana dan tampaknya lebih dari cara AngularJS. -
Mike Barlow - BarDev
3
"Sepuluh perintah" dari AngularJS. Itu deklaratif karena suatu alasan.
pilau
Jawaban Josh David Miller bukan "TIDAK BENAR". Ada waktu dan tempat untuk semuanya.
JoshuaDavid
Saya pikir Anda benar, @FireCoding. Saya berencana memperbarui jawabannya.
Gil Birman
@GilBirman jawaban yang sangat baik. Maukah Anda menulis dan memberi contoh untuk arahan? Untuk mengilustrasikan apa yang Anda katakan "satu-satunya tempat yang benar-benar masuk akal untuk memisahkan pandangan dari model adalah dengan arahan. [...] Sebaliknya, dalam permintaan direktif API data Anda dalam bentuk objek, dan nikmatilah hanya dengan $ pengamat dibuat oleh ng-bind. "
klode
78

Dari sudut pandang saya, $watchakan menjadi cara praktik terbaik.

Anda sebenarnya dapat menyederhanakan contoh Anda sedikit:

function TimerCtrl1($scope, Timer) {
  $scope.$watch( function () { return Timer.data; }, function (data) {
    $scope.lastUpdated = data.lastUpdated;
    $scope.calls = data.calls;
  }, true);
}

Itu yang kamu butuhkan.

Karena properti diperbarui secara bersamaan, Anda hanya perlu satu arloji. Juga, karena mereka berasal dari objek tunggal, agak kecil, saya mengubahnya menjadi hanya menonton Timer.dataproperti. Parameter terakhir yang diteruskan untuk $watchmemberitahukannya adalah untuk memeriksa kesetaraan yang dalam, bukan hanya memastikan bahwa rujukannya sama.


Untuk memberikan sedikit konteks, alasan saya lebih suka metode ini untuk menempatkan nilai layanan langsung pada ruang lingkup adalah untuk memastikan pemisahan masalah yang tepat. Pandangan Anda seharusnya tidak perlu tahu apa-apa tentang layanan Anda untuk beroperasi. Tugas pengendali adalah merekatkan semuanya menjadi satu; tugasnya adalah untuk mendapatkan data dari layanan Anda dan memprosesnya dengan cara apa pun yang diperlukan dan kemudian memberikan pandangan Anda dengan spesifik apa pun yang dibutuhkan. Tapi saya tidak berpikir tugasnya adalah hanya melewati layanan tepat ke tampilan. Kalau tidak, apa yang dilakukan bahkan oleh pengontrol di sana? Pengembang AngularJS mengikuti alasan yang sama ketika mereka memilih untuk tidak memasukkan "logika" dalam template (misalnya ifpernyataan).

Agar adil, mungkin ada beberapa perspektif di sini dan saya menantikan jawaban lain.

Josh David Miller
sumber
3
Bisakah Anda sedikit menjelaskan? Apakah Anda lebih suka $ watches karena tampilan kurang digabungkan ke layanan? Yaitu, {{lastUpdated}}vs.{{timerData.lastUpdated}}
Mark Rajcok
2
@BarDev, untuk digunakan Timer.datadalam $ watch, Timerharus didefinisikan pada $ scope, karena ekspresi string yang Anda berikan ke $ watch dievaluasi terhadap scope. Berikut adalah penyedot yang menunjukkan cara membuatnya. Parameter objectEquality didokumentasikan di sini - parameter ke-3 - tetapi tidak terlalu dijelaskan dengan baik.
Mark Rajcok
2
Kinerja bijaksana, $watchsangat tidak efisien. Lihat jawaban di stackoverflow.com/a/17558885/932632 dan stackoverflow.com/questions/12576798/…
Krym
11
@Kyrm Itu mungkin benar dalam beberapa keadaan, tetapi ketika berhadapan dengan kinerja kita perlu mencari peningkatan kinerja yang "signifikan secara klinis", daripada hanya yang signifikan secara statistik, yang digeneralisasi. Jika ada masalah kinerja dalam aplikasi yang ada, maka itu harus diatasi. Kalau tidak, itu hanya kasus optimasi prematur, yang mengarah ke kode yang lebih mudah dibaca, rawan bug yang tidak mengikuti praktik terbaik dan yang tidak menunjukkan manfaat.
Josh David Miller
1
Dengan asumsi pengamat menggunakan fungsi untuk memanggil pengambil, maka ya. Ini bekerja dengan baik. Saya juga pendukung layanan yang mengembalikan instance dalam koleksi, di mana fitur pengambil dan penyetel es5 dengan cukup baik sebagai gantinya.
Josh David Miller
19

Terlambat ke pesta, tetapi untuk Googler masa depan - jangan gunakan jawaban yang disediakan.

JavaScript memiliki mekanisme melewati objek dengan referensi, sementara itu hanya melewati salinan dangkal untuk nilai "angka, string dll".

Dalam contoh di atas, alih-alih mengikat atribut layanan, mengapa kita tidak mengekspos layanan ke ruang lingkup?

$scope.hello = HelloService;

Pendekatan sederhana ini akan membuat sudut mampu melakukan ikatan dua arah dan semua hal ajaib yang Anda butuhkan. Jangan meretas pengontrol Anda dengan pengamat atau markup yang tidak dibutuhkan.

Dan jika Anda khawatir tampilan Anda secara tidak sengaja menimpa atribut layanan Anda, gunakan definePropertyuntuk membuatnya dapat dibaca, dihitung, dapat dikonfigurasi, atau mendefinisikan getter dan setter. Anda bisa mendapatkan banyak kendali dengan membuat layanan Anda lebih solid.

Kiat terakhir: jika Anda menghabiskan waktu bekerja pada pengontrol Anda lebih dari layanan Anda maka Anda salah melakukannya :(.

Dalam kode demo tertentu yang Anda berikan, saya sarankan Anda lakukan:

 function TimerCtrl1($scope, Timer) {
   $scope.timer = Timer;
 }
///Inside view
{{ timer.time_updated }}
{{ timer.other_property }}
etc...

Edit:

Seperti yang saya sebutkan di atas, Anda dapat mengontrol perilaku atribut layanan Anda menggunakan defineProperty

Contoh:

// Lets expose a property named "propertyWithSetter" on our service
// and hook a setter function that automatically saves new value to db !
Object.defineProperty(self, 'propertyWithSetter', {
  get: function() { return self.data.variable; },
  set: function(newValue) { 
         self.data.variable = newValue; 
         // let's update the database too to reflect changes in data-model !
         self.updateDatabaseWithNewData(data);
       },
  enumerable: true,
  configurable: true
});

Sekarang di pengontrol kami jika kami melakukannya

$scope.hello = HelloService;
$scope.hello.propertyWithSetter = 'NEW VALUE';

layanan kami akan mengubah nilai propertyWithSetterdan juga mengirim nilai baru ke basis data entah bagaimana!

Atau kita dapat mengambil pendekatan apa pun yang kita inginkan.

Lihat dokumentasi MDN untuk defineProperty.

Zalaboza
sumber
Cukup yakin itulah yang saya jelaskan di atas, dengan $scope.model = {timerData: Timer.data};, hanya melampirkannya pada model alih-alih langsung pada Lingkup.
Scott Silvi
1
AFAIK, referensi objek js bekerja untuk semua yang ada di Layanan. Pengkodean seperti ini: $ scope.controllerVar = ServiceVar, semua yang Anda lakukan di $ scope.controllerVar juga dilakukan di ServiceVar.
Kai Wang
@ KaiWang setuju, kecuali jika Anda memutuskan untuk menggunakan DefineAttribute maka Anda dapat membuat layanan Anda memiliki fungsi setter untuk mencegah pengontrol dari kecelakaan memutasi data layanan Anda.
Zalaboza
12

Saya pikir pertanyaan ini memiliki komponen kontekstual.

Jika Anda hanya menarik data dari layanan & memancarkan informasi itu ke tampilan itu, saya pikir mengikat langsung ke properti layanan baik-baik saja. Saya tidak ingin menulis banyak kode boilerplate untuk hanya memetakan properti layanan ke model properti untuk dikonsumsi dalam pandangan saya.

Selanjutnya, kinerja dalam sudut didasarkan pada dua hal. Yang pertama adalah berapa banyak binding yang ada di halaman. Yang kedua adalah seberapa mahal fungsi pengambil. Misko berbicara tentang ini di sini

Jika Anda perlu melakukan contoh logika spesifik pada data layanan (sebagai lawan dari pemijatan data yang diterapkan dalam layanan itu sendiri), dan hasil ini berdampak pada model data yang terpapar pada tampilan, maka saya akan mengatakan $ watcher sesuai, karena selama fungsinya tidak terlalu mahal. Dalam hal fungsi yang mahal, saya akan menyarankan caching hasil dalam variabel lokal (ke controller), melakukan operasi kompleks Anda di luar fungsi $ watcher, dan kemudian mengikat cakupan Anda ke hasil itu.

Sebagai peringatan, Anda tidak boleh menggantung properti apa pun langsung dari ruang lingkup $ Anda. The $scopevariabel TIDAK Anda Model. Ini memiliki referensi ke model Anda.

Dalam pikiran saya, "praktik terbaik" untuk sekadar memancarkan informasi dari layanan ke bawah untuk melihat:

function TimerCtrl1($scope, Timer) {
  $scope.model = {timerData: Timer.data};
};

Dan kemudian tampilan Anda akan berisi {{model.timerData.lastupdated}}.

Scott Silvi
sumber
Hati-hati dengan saran itu, seseorang yang tidak ahli dalam javascript bisa mencoba melakukan itu dengan properti yang merupakan string. Dalam hal ini, javascript tidak membuat referensi ke objek, tetapi mentah menyalin string. (dan itu penyebab buruknya tidak diperbarui, jika itu berubah)
Valerio
7
Apakah saya tidak menutupinya dengan 'peringatan' saya bahwa Anda harus selalu menggunakan titik (artinya jangan menggantungnya $ lingkup, tetapi dari $ lingkup. Model). Jika Anda memiliki $ scope.model.someStringProperty, dan Anda mereferensikan model.someStringProperty dalam pandangan Anda, itu AKAN diperbarui, karena pengamat internal akan berada di objek, bukan prop.
Scott Silvi
6

Membangun contoh-contoh di atas saya pikir saya akan melemparkan cara pengikatan variabel pengontrol secara transparan ke variabel layanan.

Dalam contoh di bawah ini, perubahan ke $scope.countvariabel Kontroler akan secara otomatis tercermin dalam countvariabel Layanan .

Dalam produksi, kami benar-benar menggunakan pengikatan ini untuk memperbarui id pada layanan yang kemudian secara asinkron mengambil data dan memperbarui vars layanannya. Pengikatan lebih lanjut itu berarti bahwa pengontrol secara otomatis diperbarui ketika layanan memperbarui itu sendiri.

Kode di bawah ini dapat dilihat berfungsi di http://jsfiddle.net/xuUHS/163/

Melihat:

<div ng-controller="ServiceCtrl">
    <p> This is my countService variable : {{count}}</p>
    <input type="number" ng-model="count">
    <p> This is my updated after click variable : {{countS}}</p>

    <button ng-click="clickC()" >Controller ++ </button>
    <button ng-click="chkC()" >Check Controller Count</button>
    </br>

    <button ng-click="clickS()" >Service ++ </button>
    <button ng-click="chkS()" >Check Service Count</button>
</div>

Layanan / Pengendali:

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

app.service('testService', function(){
    var count = 10;

    function incrementCount() {
      count++;
      return count;
    };

    function getCount() { return count; }

    return {
        get count() { return count },
        set count(val) {
            count = val;
        },
        getCount: getCount,
        incrementCount: incrementCount
    }

});

function ServiceCtrl($scope, testService)
{

    Object.defineProperty($scope, 'count', {
        get: function() { return testService.count; },
        set: function(val) { testService.count = val; },
    });

    $scope.clickC = function () {
       $scope.count++;
    };
    $scope.chkC = function () {
        alert($scope.count);
    };

    $scope.clickS = function () {
       ++testService.count;
    };
    $scope.chkS = function () {
        alert(testService.count);
    };

}
TomDotTom
sumber
Ini adalah solusi yang luar biasa, terima kasih, ini banyak membantu saya, cara yang sangat cerdas untuk melakukannya :)
Javis Perez
2

Saya pikir ini adalah cara yang lebih baik untuk mengikat layanan itu sendiri daripada atribut di dalamnya.

Inilah alasannya:

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.7/angular.min.js"></script>
<body ng-app="BindToService">

  <div ng-controller="BindToServiceCtrl as ctrl">
    ArrService.arrOne: <span ng-repeat="v in ArrService.arrOne">{{v}}</span>
    <br />
    ArrService.arrTwo: <span ng-repeat="v in ArrService.arrTwo">{{v}}</span>
    <br />
    <br />
    <!-- This is empty since $scope.arrOne never changes -->
    arrOne: <span ng-repeat="v in arrOne">{{v}}</span>
    <br />
    <!-- This is not empty since $scope.arrTwo === ArrService.arrTwo -->
    <!-- Both of them point the memory space modified by the `push` function below -->
    arrTwo: <span ng-repeat="v in arrTwo">{{v}}</span>
  </div>

  <script type="text/javascript">
    var app = angular.module("BindToService", []);

    app.controller("BindToServiceCtrl", function ($scope, ArrService) {
      $scope.ArrService = ArrService;
      $scope.arrOne = ArrService.arrOne;
      $scope.arrTwo = ArrService.arrTwo;
    });

    app.service("ArrService", function ($interval) {
      var that = this,
          i = 0;
      this.arrOne = [];
      that.arrTwo = [];

      $interval(function () {
        // This will change arrOne (the pointer).
        // However, $scope.arrOne is still same as the original arrOne.
        that.arrOne = that.arrOne.concat([i]);

        // This line changes the memory block pointed by arrTwo.
        // And arrTwo (the pointer) itself never changes.
        that.arrTwo.push(i);
        i += 1;
      }, 1000);

    });
  </script>
</body> 

Anda dapat memainkannya di plunker ini .

Trantor Liu
sumber
1

Saya lebih suka menjaga pengamat saya sesedikit mungkin. Alasan saya didasarkan pada pengalaman saya dan orang mungkin membantahnya secara teoritis.
Masalah dengan menggunakan pengamat adalah Anda dapat menggunakan properti apa pun pada ruang lingkup untuk memanggil metode apa pun dalam komponen atau layanan apa pun yang Anda suka.
Dalam proyek dunia nyata, segera Anda akan berakhir dengan non-tracable (lebih baik mengatakan sulit untuk jejak) rantai metode yang disebut dan nilai-nilai yang berubah yang khusus membuat proses on-boarding tragis.

Reyraa
sumber
0

Untuk mengikat data apa pun, yang mengirimkan layanan bukan ide yang baik (arsitektur), tetapi jika Anda membutuhkannya lagi saya sarankan Anda 2 cara untuk melakukan itu

1) Anda bisa mendapatkan data tidak di dalam layanan Anda. Anda bisa mendapatkan data di dalam pengontrol / arahan Anda dan Anda tidak akan memiliki masalah untuk mengikatnya di mana saja

2) Anda dapat menggunakan peristiwa angular. Kapan pun Anda mau, Anda dapat mengirim sinyal (dari $ rootScope) dan menangkapnya di mana pun Anda inginkan. Anda bahkan dapat mengirim data pada eventName itu.

Mungkin ini bisa membantu Anda. Jika Anda membutuhkan lebih banyak dengan contoh, ini tautannya

http://www.w3docs.com/snippets/angularjs/bind-value-between-service-and-controller-directive.html

Hazarapet Tunanyan
sumber
-1

Bagaimana dengan

scope = _.extend(scope, ParentScope);

Di mana ParentScope adalah layanan yang disuntikkan?

tomascharad
sumber
-2

Solusi Paling Elegan ...

app.service('svc', function(){ this.attr = []; return this; });
app.controller('ctrl', function($scope, svc){
    $scope.attr = svc.attr || [];
    $scope.$watch('attr', function(neo, old){ /* if necessary */ });
});
app.run(function($rootScope, svc){
    $rootScope.svc = svc;
    $rootScope.$watch('svc', function(neo, old){ /* change the world */ });
});

Juga, saya menulis EDA (Event-Driven Architecture) jadi saya cenderung melakukan sesuatu seperti [versi yang disederhanakan] berikut:

var Service = function Service($rootScope) {
    var $scope = $rootScope.$new(this);
    $scope.that = [];
    $scope.$watch('that', thatObserver, true);
    function thatObserver(what) {
        $scope.$broadcast('that:changed', what);
    }
};

Kemudian, saya menempatkan pendengar di controller saya pada saluran yang diinginkan dan tetap perbarui lingkup lokal saya dengan cara ini.

Kesimpulannya, tidak ada banyak "Praktik Terbaik" - melainkan, sebagian besar preferensi - selama Anda menjaga hal-hal SOLID dan menggunakan kopling yang lemah. Alasan saya mendukung kode yang terakhir adalah karena EDA memiliki kopling terendah yang layak secara alami. Dan jika Anda tidak terlalu khawatir tentang fakta ini, mari kita hindari bekerja sama dalam proyek yang sama.

Semoga ini membantu...

Cody
sumber