Saya punya layanan, katakan:
factory('aService', ['$rootScope', '$resource', function ($rootScope, $resource) {
var service = {
foo: []
};
return service;
}]);
Dan saya ingin menggunakan foo
untuk mengontrol daftar yang dirender dalam HTML:
<div ng-controller="FooCtrl">
<div ng-repeat="item in foo">{{ item }}</div>
</div>
Agar controller dapat mendeteksi kapan aService.foo
diperbarui, saya telah menggabungkan pola ini di mana saya menambahkan aService ke controller $scope
dan kemudian gunakan $scope.$watch()
:
function FooCtrl($scope, aService) {
$scope.aService = aService;
$scope.foo = aService.foo;
$scope.$watch('aService.foo', function (newVal, oldVal, scope) {
if(newVal) {
scope.foo = newVal;
}
});
}
Ini terasa sulit, dan saya telah mengulanginya di setiap pengontrol yang menggunakan variabel layanan. Apakah ada cara yang lebih baik untuk menyelesaikan menonton variabel yang dibagikan?
angularjs
watch
angular-services
berto
sumber
sumber
aService.foo
di markup html? (seperti ini: plnkr.co/edit/aNrw5Wo4Q0IxR2loipl5?p=preview )Jawaban:
Anda selalu dapat menggunakan pola pengamat lama yang baik jika Anda ingin menghindari tirani dan overhead
$watch
.Dalam layanan:
Dan di controller:
sumber
$destory
acara pada lingkup dan tambahkan metode membatalkan pendaftaran keaService
$watch
vs pengamat pola hanya memilih apakah akan polling atau mendorong, dan pada dasarnya adalah masalah kinerja, jadi gunakan itu ketika kinerja penting. Saya menggunakan pola pengamat ketika kalau tidak saya harus "mendalam" menonton benda-benda kompleks. Saya melampirkan seluruh layanan ke $ scope daripada menonton nilai layanan tunggal. Saya menghindari $ arloji sudut seperti iblis, ada cukup banyak yang terjadi dalam arahan dan dalam pengikatan data sudut asli.Dalam skenario seperti ini, di mana beberapa objek / tidak dikenal mungkin tertarik pada perubahan, gunakan
$rootScope.$broadcast
dari item yang diubah.Daripada membuat registri pendengar Anda sendiri (yang harus dibersihkan pada berbagai $ Destroys), Anda harus dapat
$broadcast
dari layanan yang bersangkutan.Anda masih harus memberi kode pada
$on
penangan di setiap pendengar tetapi polanya dipisahkan dari beberapa panggilan ke$digest
dan dengan demikian menghindari risiko pengamat berjalan lama.Dengan cara ini, pendengar dapat datang dan pergi dari DOM dan / atau lingkup anak yang berbeda tanpa layanan mengubah perilakunya.
** pembaruan: contoh **
Siaran akan paling masuk akal dalam layanan "global" yang dapat memengaruhi banyak hal lain di aplikasi Anda. Contoh yang baik adalah layanan Pengguna di mana ada sejumlah peristiwa yang dapat terjadi seperti masuk, keluar, diperbarui, menganggur, dll. Saya percaya ini adalah tempat siaran paling masuk akal karena ruang lingkup apa pun dapat mendengarkan suatu acara, tanpa bahkan menyuntikkan layanan, dan tidak perlu mengevaluasi ekspresi atau hasil cache apa pun untuk memeriksa perubahan. Itu hanya api dan lupa (jadi pastikan itu pemberitahuan api-dan-lupakan, bukan sesuatu yang membutuhkan tindakan)
Layanan di atas akan menyiarkan pesan ke setiap lingkup ketika fungsi save () selesai dan data valid. Atau, jika itu adalah $ resource atau submisi ajax, pindahkan panggilan broadcast ke dalam callback sehingga menyala ketika server merespons. Siaran sesuai dengan pola itu terutama karena setiap pendengar hanya menunggu acara tanpa perlu memeriksa ruang lingkup pada setiap $ digest tunggal. Pendengar akan terlihat seperti:
Salah satu manfaat dari ini adalah penghapusan beberapa jam tangan. Jika Anda menggabungkan bidang atau memperoleh nilai seperti contoh di atas, Anda harus menonton properti firstname dan lastname. Menonton fungsi getUser () hanya akan berfungsi jika objek pengguna diganti pada pembaruan, itu tidak akan menyala jika objek pengguna hanya memiliki sifat-sifatnya diperbarui. Dalam hal ini Anda harus melakukan pengamatan mendalam dan itu lebih intensif.
$ broadcast mengirimkan pesan dari lingkup yang dipanggil ke lingkup anak apa pun. Jadi memanggilnya dari $ rootScope akan menyala di setiap ruang lingkup. Jika Anda $ broadcast dari ruang lingkup controller Anda, misalnya, itu akan menembak hanya di lingkup yang mewarisi dari lingkup controller Anda. $ emit berjalan ke arah yang berlawanan dan berperilaku serupa dengan peristiwa DOM dalam hal itu menggembungkan rantai lingkup.
Perlu diingat bahwa ada skenario di mana $ broadcast masuk akal, dan ada skenario di mana $ watch adalah pilihan yang lebih baik - terutama jika dalam ruang lingkup yang terisolasi dengan ekspresi menonton yang sangat spesifik.
sumber
Saya menggunakan pendekatan yang sama seperti @dtheodot tetapi menggunakan janji sudut alih-alih melewati panggilan balik
Lalu di mana saja gunakan
myService.setFoo(foo)
metode untuk memperbaruifoo
layanan. Di controller Anda, Anda dapat menggunakannya sebagai:Dua argumen pertama
then
adalah keberhasilan dan kesalahan panggilan balik, yang ketiga adalah memberitahukan panggilan balik.Referensi untuk $ q.
sumber
$scope.$watch
pada variabel layanan sepertinya tidak berfungsi (ruang lingkup yang saya tonton adalah modal yang diwarisi dari$rootScope
) - ini berhasil. Trik keren, terima kasih sudah berbagi!Tanpa arloji atau panggilan balik pengamat ( http://jsfiddle.net/zymotik/853wvv7s/ ):
JavaScript:
HTML
JavaScript ini berfungsi saat kami melewati objek kembali dari layanan daripada nilai. Saat objek JavaScript dikembalikan dari layanan, Angular menambahkan jam tangan ke semua propertinya.
Perhatikan juga bahwa saya menggunakan 'var self = this' karena saya perlu menyimpan referensi ke objek asli ketika $ timeout dijalankan, jika tidak 'ini' akan merujuk ke objek jendela.
sumber
$scope.count = service.count
tidak akan berhasil.$scope.data = service.data
<p>Count: {{ data.count }}</p>
Saya menemukan pertanyaan ini mencari sesuatu yang serupa, tetapi saya pikir itu layak mendapatkan penjelasan menyeluruh tentang apa yang terjadi, serta beberapa solusi tambahan.
Ketika ekspresi sudut seperti yang Anda gunakan ada dalam HTML, Angular secara otomatis menyiapkan a
$watch
untuk$scope.foo
, dan akan memperbarui HTML setiap kali ada$scope.foo
perubahan.Masalah yang tidak terungkap di sini adalah bahwa satu dari dua hal mempengaruhi
aService.foo
sehingga perubahan tidak terdeteksi. Dua kemungkinan ini adalah:aService.foo
semakin diatur ke array baru setiap kali, menyebabkan referensi untuk itu menjadi usang.aService.foo
sedang diperbarui sedemikian rupa sehingga$digest
siklus tidak dipicu pada pembaruan.Masalah 1: Referensi yang sudah ketinggalan zaman
Mempertimbangkan kemungkinan pertama, dengan asumsi a
$digest
sedang diterapkan, jikaaService.foo
selalu array yang sama, set otomatis$watch
akan mendeteksi perubahan, seperti yang ditunjukkan dalam cuplikan kode di bawah ini.Solusi 1-a: Pastikan array atau objek adalah objek yang sama pada setiap pembaruan
Tampilkan cuplikan kode
Seperti yang Anda lihat, ng-repeat yang seharusnya dilampirkan
aService.foo
tidak memperbarui ketikaaService.foo
perubahan, tetapi ng-repeat yang dilampirkanaService2.foo
tidak . Ini karena referensi kami kepadaaService.foo
sudah usang, tetapi referensi kami untukaService2.foo
tidak. Kami membuat referensi ke array awal dengan$scope.foo = aService.foo;
, yang kemudian dibuang oleh layanan pada pembaruan berikutnya, yang berarti$scope.foo
tidak lagi mereferensikan array yang kami inginkan lagi.Namun, sementara ada beberapa cara untuk memastikan referensi awal disimpan dalam kebijaksanaan, kadang-kadang mungkin perlu untuk mengubah objek atau array. Atau bagaimana jika properti layanan merujuk ke primitif seperti
String
atauNumber
? Dalam kasus itu, kita tidak bisa hanya mengandalkan referensi. Jadi apa yang bisa kita lakukan?Beberapa jawaban yang diberikan sebelumnya sudah memberikan beberapa solusi untuk masalah itu. Namun, secara pribadi saya mendukung penggunaan metode sederhana yang disarankan oleh Jin dan semuanya dalam komentar:
Solusi 1-b: Pasang layanan ke lingkup, dan referensi
{service}.{property}
dalam HTML.Artinya, lakukan saja ini:
HTML:
JS:
Tampilkan cuplikan kode
Dengan begitu,
$watch
akan menyelesaikanaService.foo
masing-masing$digest
, yang akan mendapatkan nilai yang diperbarui dengan benar.Ini adalah jenis apa yang Anda coba lakukan dengan solusi Anda, tetapi dalam cara yang jauh lebih sedikit. Anda menambahkan yang tidak perlu
$watch
di controller yang secara eksplisit menempatkanfoo
pada$scope
setiap kali perubahan. Anda tidak perlu ekstra$watch
saat melampirkanaService
daripadaaService.foo
ke$scope
, dan mengikat secara eksplisit keaService.foo
dalam markup.Nah, itu semua baik dan bagus dengan asumsi
$digest
siklus sedang diterapkan. Dalam contoh saya di atas, saya menggunakan layanan Angular$interval
untuk memperbarui array, yang secara otomatis memulai$digest
loop setelah setiap pembaruan. Tetapi bagaimana jika variabel layanan (untuk alasan apa pun) tidak diperbarui di "dunia sudut". Dengan kata lain, kami tidak memiliki$digest
siklus yang diaktifkan secara otomatis setiap kali properti layanan berubah?Masalah 2: Hilang
$digest
Banyak solusi di sini akan menyelesaikan masalah ini, tetapi saya setuju dengan Code Whisperer :
Oleh karena itu, saya lebih suka untuk terus menggunakan
aService.foo
referensi dalam markup HTML seperti yang ditunjukkan pada contoh kedua di atas, dan tidak harus mendaftarkan panggilan balik tambahan dalam Controller.Solusi 2: Gunakan setter dan pengambil
$rootScope.$apply()
Saya terkejut belum ada yang menyarankan penggunaan setter dan pengambil . Kemampuan ini diperkenalkan di ECMAScript5, dan telah ada selama bertahun-tahun sekarang. Tentu saja, itu berarti jika, untuk alasan apa pun, Anda perlu mendukung browser yang sangat lama, maka metode ini tidak akan berfungsi, tetapi saya merasa seperti getter dan setter sangat kurang digunakan dalam JavaScript. Dalam kasus khusus ini, mereka bisa sangat berguna:
Tampilkan cuplikan kode
Di sini saya menambahkan variabel 'pribadi' dalam fungsi pelayanan:
realFoo
. Get ini diperbarui dan diambil menggunakanget foo()
danset foo()
fungsi masing-masing padaservice
objek.Perhatikan penggunaan
$rootScope.$apply()
dalam fungsi yang ditetapkan. Ini memastikan bahwa Angular akan mengetahui adanya perubahanservice.foo
. Jika Anda mendapatkan kesalahan 'inprog' lihat halaman referensi yang berguna ini , atau jika Anda menggunakan Angular> = 1.3 Anda bisa menggunakan$rootScope.$applyAsync()
.Berhati-hatilah dengan hal ini jika
aService.foo
diperbarui dengan sangat sering, karena hal itu dapat berdampak signifikan terhadap kinerja. Jika kinerja akan menjadi masalah, Anda bisa mengatur pola pengamat yang mirip dengan jawaban lain di sini menggunakan setter.sumber
services
bukan untuk properti milik layanan itu sendiri.Sejauh yang saya tahu, Anda tidak perlu melakukan sesuatu yang rumit seperti itu. Anda telah menetapkan foo dari layanan ke ruang lingkup Anda dan karena foo adalah array (dan pada gilirannya objek itu ditetapkan oleh referensi!). Jadi, yang perlu Anda lakukan adalah sesuatu seperti ini:
Jika beberapa, variabel lain dalam Ctrl yang sama ini bergantung pada perubahan foo maka ya, Anda akan memerlukan arloji untuk mengamati foo dan membuat perubahan pada variabel itu. Tetapi selama itu hanya menonton referensi sederhana tidak perlu. Semoga ini membantu.
sumber
$watch
bekerja dengan primitif. Sebaliknya, saya mendefinisikan sebuah metode pada layanan yang akan mengembalikan nilai primitif:somePrimitive() = function() { return somePrimitive }
Dan saya ditugaskan properti $ lingkup untuk metode yang:$scope.somePrimitive = aService.somePrimitive;
. Lalu saya menggunakan metode lingkup dalam HTML:<span>{{somePrimitive()}}</span>
$scope.foo = aService.foo
tidak memperbarui variabel ruang lingkup secara otomatis.Anda dapat memasukkan layanan dalam $ rootScope dan menonton:
Di controller Anda:
Pilihan lain adalah menggunakan perpustakaan eksternal seperti: https://github.com/melanke/Watch.JS
Bekerja dengan: IE 9+, FF 4+, SF 5+, WebKit, CH 7+, OP 12+, BESEN, Node.JS, Badak 1.7+
Anda dapat mengamati perubahan satu, banyak atau semua atribut objek.
Contoh:
sumber
Anda dapat menonton perubahan di dalam pabrik itu sendiri dan kemudian menyiarkan perubahan
Kemudian di controller Anda:
Dengan cara ini Anda memasukkan semua kode pabrik terkait ke dalam uraiannya maka Anda hanya dapat mengandalkan siaran dari luar
sumber
== DIPERBARUI ==
Sangat sederhana sekarang di $ watch.
Pena di sini .
HTML:
JavaScript:
sumber
Membangun berdasarkan jawaban dtheodor Anda dapat menggunakan sesuatu yang mirip dengan di bawah ini untuk memastikan bahwa Anda tidak lupa membatalkan pendaftaran callback ... Namun, beberapa orang mungkin keberatan untuk meneruskannya
$scope
ke layanan.Array.remove adalah metode ekstensi yang terlihat seperti ini:
sumber
Inilah pendekatan generik saya.
Di Kontroler Anda
sumber
Bagi mereka seperti saya hanya mencari solusi sederhana, ini tidak persis seperti yang Anda harapkan dari menggunakan $ menonton normal di pengontrol. Satu-satunya perbedaan adalah, ia mengevaluasi string dalam konteks javascriptnya dan bukan pada lingkup tertentu. Anda harus menyuntikkan $ rootScope ke layanan Anda, meskipun itu hanya digunakan untuk menghubungkan ke siklus digest dengan benar.
sumber
saat menghadapi masalah yang sangat mirip saya melihat fungsi dalam lingkup dan fungsi mengembalikan variabel layanan. Saya telah membuat biola . Anda dapat menemukan kode di bawah ini.
sumber
Saya sampai pada pertanyaan ini tetapi ternyata masalah saya adalah bahwa saya menggunakan setInterval ketika seharusnya saya menggunakan penyedia $ interval angular. Ini juga merupakan kasus untuk setTimeout (gunakan $ timeout). Saya tahu itu bukan jawaban untuk pertanyaan OP, tetapi mungkin membantu beberapa, karena membantu saya.
sumber
setTimeout
, atau fungsi non-sudut lainnya, tetapi jangan lupa untuk membungkus kode dengan panggilan balik$scope.$apply()
.Saya telah menemukan solusi yang sangat bagus di utas lainnya dengan masalah yang sama tetapi pendekatan yang sama sekali berbeda. Sumber: AngularJS: $ watch dalam direktif tidak berfungsi ketika nilai $ rootScope diubah
Pada dasarnya solusi yang ada TIDAK memberitahu untuk digunakan
$watch
karena ini adalah solusi yang sangat berat. Sebaliknya mereka mengusulkan untuk menggunakan$emit
dan$on
.Masalah saya adalah untuk menonton variabel dalam layanan saya dan bereaksi dalam arahan . Dan dengan metode di atas sangat mudah!
Modul / layanan saya contoh:
Jadi pada dasarnya saya menonton saya
user
- setiap kali diatur untuk nilai baru saya$emit
sebuahuser:change
status.Sekarang dalam kasus saya, dalam arahan yang saya gunakan:
Sekarang di direktif saya mendengarkan pada
$rootScope
dan di perubahan yang diberikan - saya bereaksi masing-masing. Sangat mudah dan elegan!sumber
// layanan: (tidak ada yang istimewa di sini)
// ctrl:
sumber
Agak jelek, tapi saya sudah menambahkan pendaftaran variabel lingkup ke layanan saya untuk beralih:
Dan kemudian di controller:
sumber
this
dari layanan itu alih-alihself
?return this;
keluar dari konstruktor Anda ;-)Lihatlah plunker ini :: ini adalah contoh paling sederhana yang bisa saya pikirkan
http://jsfiddle.net/HEdJF/
sumber
Saya telah melihat beberapa pola pengamat yang mengerikan di sini yang menyebabkan kebocoran memori pada aplikasi besar.
Saya mungkin sedikit terlambat tetapi sesederhana ini.
Fungsi arloji mengawasi perubahan referensi (tipe primitif) jika Anda ingin menonton sesuatu seperti push array cukup gunakan:
someArray.push(someObj); someArray = someArray.splice(0);
Ini akan memperbarui referensi dan memperbarui arloji dari mana saja. Termasuk metode pengambil layanan. Apa pun yang primitif akan diperbarui secara otomatis.
sumber
Saya terlambat ke bagian itu tetapi saya menemukan cara yang lebih baik untuk melakukan ini daripada jawaban yang diposting di atas. Alih-alih menugaskan variabel untuk menyimpan nilai variabel layanan, saya membuat fungsi yang dilampirkan ke lingkup, yang mengembalikan variabel layanan.
pengontrol
Saya pikir ini akan melakukan apa yang Anda inginkan. Pengontrol saya terus memeriksa nilai layanan saya dengan implementasi ini. Jujur, ini jauh lebih sederhana daripada jawaban yang dipilih.
sumber
Saya telah menulis dua layanan utilitas sederhana yang membantu saya melacak perubahan properti layanan.
Jika Anda ingin melewatkan penjelasan panjang, Anda bisa pergi ke jsfiddle
Layanan ini digunakan dalam controller dengan cara berikut: Misalkan Anda memiliki layanan "testService" dengan properti 'prop1', 'prop2', 'prop3'. Anda ingin menonton dan menetapkan ruang lingkup 'prop1' dan 'prop2'. Dengan layanan jam tangan akan terlihat seperti itu:
Saya akan memicunya di akhir kode async saya untuk memicu loop $ digest. Seperti itu:
Jadi, semua itu bersama akan terlihat seperti itu (Anda dapat menjalankannya atau membuka biola ):
sumber