Bagaimana cara saya menggunakan $ scope. $ Watch dan $ scope. $ Berlaku di AngularJS?

1088

Saya tidak mengerti cara menggunakan $scope.$watchdan $scope.$apply. Dokumentasi resmi tidak membantu.

Yang tidak saya mengerti secara spesifik:

  • Apakah mereka terhubung ke DOM?
  • Bagaimana saya bisa memperbarui perubahan DOM ke model?
  • Apa titik koneksi di antara mereka?

Saya sudah mencoba tutorial ini , tetapi butuh pengertian dari $watchdan $applybegitu saja.

Apa yang dilakukan $applydan $watchdilakukan, dan bagaimana cara menggunakannya secara tepat?

ilyo
sumber

Jawaban:

1737

Anda harus sadar tentang cara kerja AngularJS untuk memahaminya.

Siklus pencernaan dan $ lingkup

Pertama dan terpenting, AngularJS mendefinisikan konsep siklus digest . Siklus ini dapat dianggap sebagai loop, di mana AngularJS memeriksa apakah ada perubahan pada semua variabel yang ditonton oleh semua $scopes. Jadi, jika Anda telah $scope.myVarmenetapkan di controller Anda dan variabel ini ditandai untuk diawasi , maka Anda secara implisit memberitahu AngularJS untuk memantau perubahan pada myVarsetiap iterasi dari loop.

Pertanyaan tindak lanjut yang wajar adalah: Apakah segala sesuatu terkait dengan $scopediawasi? Untungnya tidak. Jika Anda akan melihat perubahan pada setiap objek di Anda $scope, maka dengan cepat loop intis akan membutuhkan waktu lama untuk mengevaluasi dan Anda akan dengan cepat mengalami masalah kinerja. Itulah sebabnya tim AngularJS memberi kami dua cara untuk menyatakan beberapa $scopevariabel sedang diawasi (baca di bawah).

$ watch membantu mendengarkan perubahan $ scope

Ada dua cara untuk mendeklarasikan $scopevariabel sebagai yang diawasi.

  1. Dengan menggunakannya di templat Anda melalui ekspresi <span>{{myVar}}</span>
  2. Dengan menambahkannya secara manual melalui $watchlayanan

Iklan 1) Ini adalah skenario yang paling umum dan saya yakin Anda pernah melihatnya sebelumnya, tetapi Anda tidak tahu bahwa ini telah membuat arloji di latar belakang. Ya, benar! Menggunakan arahan AngularJS (seperti ng-repeat) juga dapat membuat jam tangan implisit.

Iklan 2) Ini adalah bagaimana Anda membuat jam tangan Anda sendiri . $watchlayanan membantu Anda untuk menjalankan beberapa kode ketika beberapa nilai yang melekat pada $scopetelah berubah. Ini jarang digunakan, tetapi kadang-kadang bermanfaat. Misalnya, jika Anda ingin menjalankan beberapa kode setiap kali 'myVar' berubah, Anda dapat melakukan hal berikut:

function MyController($scope) {

    $scope.myVar = 1;

    $scope.$watch('myVar', function() {
        alert('hey, myVar has changed!');
    });

    $scope.buttonClicked = function() {
        $scope.myVar = 2; // This will trigger $watch expression to kick in
    };
}

$ apply memungkinkan untuk mengintegrasikan perubahan dengan siklus digest

Anda dapat menganggap $applyfungsinya sebagai mekanisme integrasi . Anda lihat, setiap kali Anda mengubah beberapa variabel yang ditonton yang melekat pada$scope objek secara langsung, AngularJS akan tahu bahwa perubahan telah terjadi. Ini karena AngularJS sudah tahu untuk memantau perubahan itu. Jadi jika itu terjadi dalam kode yang dikelola oleh kerangka kerja, siklus intisari akan melanjutkan.

Namun, terkadang Anda ingin mengubah beberapa nilai di luar dunia AngularJS dan melihat perubahannya menyebar secara normal. Pertimbangkan ini - Anda memiliki $scope.myVarnilai yang akan dimodifikasi dalam $.ajax()penangan jQuery . Ini akan terjadi di beberapa titik di masa depan. AngularJS tidak sabar menunggu hal ini terjadi, karena belum diinstruksikan untuk menunggu di jQuery.

Untuk mengatasinya, $applysudah diperkenalkan. Ini memungkinkan Anda memulai siklus pencernaan secara eksplisit. Namun, Anda hanya boleh menggunakan ini untuk memigrasi beberapa data ke AngularJS (integrasi dengan kerangka kerja lain), tetapi jangan pernah menggunakan metode ini dikombinasikan dengan kode AngularJS biasa, karena AngularJS akan membuang kesalahan saat itu.

Bagaimana semua ini terkait dengan DOM?

Nah, Anda harus benar-benar mengikuti tutorial lagi, sekarang setelah Anda tahu semua ini. Siklus intisari akan memastikan bahwa UI dan kode JavaScript tetap disinkronkan, dengan mengevaluasi setiap pengamat yang terpasang pada semua $scopeselama tidak ada perubahan. Jika tidak ada lagi perubahan yang terjadi di loop digest, maka itu dianggap selesai.

Anda bisa melampirkan objek ke $scopeobjek baik secara eksplisit di Controller, atau dengan mendeklarasikannya dalam {{expression}}bentuk langsung di tampilan.

Saya harap ini membantu menjelaskan beberapa pengetahuan dasar tentang semua ini.

Bacaan lebih lanjut:

ŁukaszBachman
sumber
57
"Pemeriksaan sudut jika ada perubahan pada semua variabel yang melekat pada semua $ lingkup" - Saya pikir itu tidak benar. Saya percaya hanya Angular (kotor) memeriksa properti $ scope yang telah disetel $ jam tangan (perhatikan bahwa menggunakan {{}} dalam suatu tampilan akan membuat $ jam secara otomatis). Lihat juga bagian "Pertimbangan $ menonton Pertimbangan Kinerja" di halaman Lingkup .
Mark Rajcok
5
Mungkin itu masalahnya. Saya akan mencoba mencari waktu untuk membaca lebih lanjut tentang itu dan mengedit jawaban saya.
ŁukaszBachman
15
@MarkRajcok, Anda benar. Saya mengubah jawaban saya dan menunjukkan sebuah artikel yang menunjukkan dengan baik bagaimana ini diterapkan.
ŁukaszBachman
3
bagaimana dengan menggunakan ini? (Metode "Kontrol sebagai")
Leandro
2
Menggunakan "Kontrol sebagai" seharusnya tidak berdampak pada informasi di atas. Menggunakan this.myVar menempatkan myVar pada ruang lingkup.
Marcus Rådell
161

Di AngularJS, kami memperbarui model kami, dan tampilan / templat kami memperbarui DOM "secara otomatis" (melalui petunjuk bawaan atau kustom).

$ apply dan $ watch, keduanya merupakan metode Lingkup, tidak terkait dengan DOM.

The Konsep halaman (bagian "Runtime") memiliki penjelasan yang cukup bagus dari $ mencerna lingkaran, $ berlaku, antrian $ evalAsync dan daftar $ menonton. Inilah gambar yang menyertai teks:

$ digest loop

Kode apa pun yang memiliki akses ke lingkup - biasanya pengontrol dan arahan (fungsi tautan dan / atau pengontrol mereka) - dapat mengatur " watchExpression " yang akan dievaluasi AngularJS terhadap cakupan itu. Evaluasi ini terjadi setiap kali AngularJS memasuki loop $ digest (khususnya, loop "$ watch list"). Anda dapat menonton properti lingkup individual, Anda dapat menentukan fungsi untuk menonton dua properti secara bersamaan, Anda dapat menonton panjang array, dll.

Ketika hal-hal terjadi "di dalam AngularJS" - misalnya, Anda mengetik ke dalam kotak teks yang mengaktifkan penyatuan data dua arah AngularJS (yaitu, menggunakan model-ng), panggilan panggil balik $ http, dll. - $ apply telah dipanggil, jadi kami Di dalam kotak "AngularJS" pada gambar di atas. Semua watchExpressions akan dievaluasi (mungkin lebih dari sekali - sampai tidak ada perubahan lebih lanjut terdeteksi).

Ketika hal-hal terjadi "di luar AngularJS" - misalnya, Anda menggunakan bind () dalam sebuah arahan dan kemudian peristiwa itu terjadi, mengakibatkan panggilan balik Anda dipanggil, atau beberapa panggilan panggil balik terdaftar jQuery terdaftar - kami masih berada dalam kotak "Asli". Jika kode panggilan balik memodifikasi apa pun yang ditonton $ watch apa pun, panggil $ berlaku untuk masuk ke persegi panjang AngularJS, menyebabkan $ digest loop dijalankan, dan karenanya AngularJS akan melihat perubahan dan melakukan keajaibannya.

Mark Rajcok
sumber
5
Saya mengerti idenya, yang tidak saya mengerti adalah bagaimana sebenarnya data ditransfer. Saya memiliki model yang merupakan objek dengan banyak data, saya menggunakannya untuk memanipulasi DOM. kemudian beberapa di antaranya berubah. Bagaimana cara menempatkan data yang diubah di tempat yang tepat dalam model? Dalam contoh yang saya gunakan dia membuat manipulasi dan pada akhirnya hanya menggunakan scope.$apply(scope.model), saya tidak mengerti data apa yang ditransfer dan bagaimana cara ditransfer ke tempat yang tepat dalam model?
ilyo
6
Tidak ada transfer data ajaib yang terjadi. Biasanya dengan aplikasi Angular, Anda harus mengubah model Angular, yang kemudian mendorong pembaruan tampilan / DOM. Jika Anda memperbarui DOM di luar Angular, Anda harus memperbarui model secara manual. scope.$apply(scope.model)hanya akan mengevaluasi scope.modelsebagai ekspresi Angular, dan kemudian memasukkan loop $ digest. Dalam artikel yang Anda referensikan, mungkin scope.$apply()akan cukup, karena modelnya sudah $ diawasi. Fungsi stop () memperbarui model (saya percaya toUpdate adalah referensi ke scope.model), dan kemudian $ apply dipanggil.
Mark Rajcok
Sepertinya dokumen AngularJS telah bergeser dari bawah jawaban ini (tautan pertama tidak memiliki "runtime" atau $watchdi laman, dan tautan kedua rusak - seperti sekarang, bagaimanapun). Sungguh menyakitkan, versi arsip tidak menembolok proses async apa pun yang menciptakan konten.
ruffin
52

AngularJS memperluas loop peristiwa ini , menciptakan sesuatu yang disebut AngularJS context.

$ tonton ()

Setiap kali Anda mengikat sesuatu di UI Anda memasukkan $watchdalam $watchdaftar .

User: <input type="text" ng-model="user" />
Password: <input type="password" ng-model="pass" />

Di sini kita memiliki $scope.user, yang terikat pada input pertama, dan kita miliki $scope.pass, yang terikat pada input kedua. Melakukan ini, kami menambahkan dua $watches ke $watchdaftar .

Ketika templat kita dimuat, AKA dalam fase penautan, kompiler akan mencari setiap arahan dan membuat semua $watches yang diperlukan.

AngularJS menyediakan $watch, $watchcollectiondan $watch(true). Di bawah ini adalah diagram rapi yang menjelaskan ketiganya diambil dari pengamat secara mendalam .

Masukkan deskripsi gambar di sini

angular.module('MY_APP', []).controller('MyCtrl', MyCtrl)
function MyCtrl($scope,$timeout) {
  $scope.users = [{"name": "vinoth"},{"name":"yusuf"},{"name":"rajini"}];

  $scope.$watch("users", function() {
    console.log("**** reference checkers $watch ****")
  });

  $scope.$watchCollection("users", function() {
    console.log("**** Collection  checkers $watchCollection ****")
  });

  $scope.$watch("users", function() {
    console.log("**** equality checkers with $watch(true) ****")
  }, true);

  $timeout(function(){
     console.log("Triggers All ")
     $scope.users = [];
     $scope.$digest();

     console.log("Triggers $watchCollection and $watch(true)")
     $scope.users.push({ name: 'Thalaivar'});
     $scope.$digest();

     console.log("Triggers $watch(true)")
     $scope.users[0].name = 'Superstar';
     $scope.$digest();
  });
}

http://jsfiddle.net/2Lyn0Lkb/

$digest lingkaran

Ketika browser menerima suatu peristiwa yang dapat dikelola oleh konteks AngularJS, $digestloop akan diaktifkan. Loop ini dibuat dari dua loop yang lebih kecil. Satu memproses $evalAsyncantrian, dan yang lainnya memproses $watch list. Surat $digestwasiat akan melalui daftar $watchyang kita miliki

app.controller('MainCtrl', function() {
  $scope.name = "vinoth";

  $scope.changeFoo = function() {
      $scope.name = "Thalaivar";
  }
});

{{ name }}
<button ng-click="changeFoo()">Change the name</button>

Di sini kita hanya punya satu $watchkarena ng-klik tidak membuat jam tangan.

Kami menekan tombol.

  1. Browser menerima acara yang akan memasuki konteks AngularJS
  2. The $digestLoop akan berjalan dan akan meminta setiap $ menonton untuk perubahan.
  3. Karena $watchyang mengawasi perubahan $ scope.name melaporkan perubahan, itu akan memaksa $digestloop lain .
  4. Loop baru tidak melaporkan apa pun.
  5. Browser mendapatkan kontrol kembali dan itu akan memperbarui DOM yang mencerminkan nilai baru $ scope.name
  6. Yang penting di sini adalah bahwa SETIAP peristiwa yang memasuki konteks AngularJS akan menjalankan $digestloop. Itu berarti bahwa setiap kali kita menulis surat di input, loop akan berjalan memeriksa setiap $watchhalaman ini.

$ terapkan ()

Jika Anda menelepon $applyketika suatu peristiwa dipecat, itu akan melalui konteks sudut, tetapi jika Anda tidak menyebutnya, itu akan berjalan di luar itu. Semudah itu. $applyakan memanggil$digest() loop secara internal dan akan mengulangi semua jam tangan untuk memastikan DOM diperbarui dengan nilai yang baru diperbarui.

The $apply()metode akan memicu pengamat pada seluruh $scoperantai sedangkan $digest()metode hanya akan memicu pengamat pada saat $scopedan nya children. Ketika tidak ada objek yang lebih tinggi yang $scopeperlu tahu tentang perubahan lokal, Anda bisa menggunakannya $digest().

Thalaivar
sumber
18

Saya menemukan sangat mendalam video yang meliputi $watch, $apply, $digestdan mencerna siklus di:

Berikut adalah beberapa slide yang digunakan dalam video tersebut untuk menjelaskan konsep-konsep (untuk berjaga-jaga, jika tautan di atas dihapus / tidak berfungsi).

Masukkan deskripsi gambar di sini

Pada gambar di atas, "$ scope.c" tidak ditonton karena tidak digunakan dalam binding data apa pun (dalam markup). Dua lainnya ( $scope.adan $scope.b) akan diawasi.

Masukkan deskripsi gambar di sini

Dari gambar di atas: Berdasarkan pada acara browser masing-masing, AngularJS menangkap acara, melakukan siklus digest (menelusuri semua jam tangan untuk perubahan), menjalankan fungsi jam tangan dan memperbarui DOM. Jika bukan acara browser, siklus intisari dapat dipicu secara manual menggunakan $applyatau $digest.

Lebih lanjut tentang $applydan $digest:

Masukkan deskripsi gambar di sini

pengguna203687
sumber
17

Ada $watchGroupdan $watchCollectionjuga. Secara khusus, $watchGroupsangat membantu jika Anda ingin memanggil fungsi untuk memperbarui objek yang memiliki beberapa properti dalam tampilan yang bukan objek dom, misalnya untuk tampilan lain di kanvas, WebGL atau permintaan server.

Di sini, tautan dokumentasi .

Utkarsh Bhardwaj
sumber
Saya akan berkomentar tentang $watchCollectiontetapi saya melihat Anda sudah melakukannya. Ini dokumentasi tentang hal itu dari situs AngularJS. Mereka memberikan visual yang sangat bagus dari $watchkedalaman. Perhatikan informasinya dekat dengan bagian bawah halaman.
JabberwockyDecompiler
15

Selesai membaca SEMUA hal di atas, membosankan dan mengantuk (maaf tapi itu benar). Sangat teknis, mendalam, terperinci, dan kering. Kenapa saya menulis? Karena AngularJS sangat besar, banyak konsep yang saling terhubung dapat mengubah siapa pun menjadi gila. Saya sering bertanya pada diri sendiri, apakah saya tidak cukup pintar untuk memahaminya? Tidak! Itu karena sangat sedikit yang bisa menjelaskan teknologi dalam bahasa for-dummie tanpa semua terminologi! Oke, izinkan saya mencoba:

1) Mereka semua adalah event-driven. (Saya mendengar tawa, tetapi baca terus)

Jika Anda tidak tahu apa itu event-driven. Lalu anggap Anda meletakkan tombol di halaman tersebut, kaitkan dengan fungsi menggunakan "klik", menunggu pengguna mengkliknya untuk memicu tindakan yang Anda tanam di dalam fungsi. Atau pikirkan "pemicu" dari SQL Server / Oracle.

2) $ jam adalah "on-klik".

Yang spesial tentang ini adalah dibutuhkan 2 fungsi sebagai parameter, yang pertama memberikan nilai dari acara tersebut, yang kedua mempertimbangkan nilai ...

3) $ digest adalah bos yang memeriksa sekitar tanpa lelah , bla-bla-bla tapi bos yang baik.

4) $ apply memberi Anda cara ketika Anda ingin melakukannya secara manual , seperti bukti-gagal (kalau-kalau klik tidak masuk, Anda memaksanya untuk berjalan.)

Sekarang, mari kita membuatnya visual. Bayangkan ini untuk membuatnya lebih mudah untuk mengambil ide:

Di rumah makan,

- WAITER

seharusnya menerima pesanan dari pelanggan, ini

$watch(
  function(){return orders;},
  function(){Kitchen make it;}
);

- MANAGER berlarian untuk memastikan semua pelayan terjaga, responsif terhadap tanda-tanda perubahan dari pelanggan. Ini adalah$digest()

- PEMILIK memiliki kekuatan tertinggi untuk mengarahkan semua orang atas permintaan, ini$apply()

Jeb50
sumber
2
Ini bisa dipahami oleh anak berusia 5 tahun. Saya menghargai jawaban semacam ini. +1
Chris22