$ awasi obyek

317

Saya ingin melihat perubahan dalam kamus, tetapi untuk beberapa alasan menonton panggilan balik tidak dipanggil.

Berikut adalah pengontrol yang saya gunakan:

function MyController($scope) {
    $scope.form = {
        name: 'my name',
        surname: 'surname'
    }

    $scope.$watch('form', function(newVal, oldVal){
        console.log('changed');
    });
}

Ini biola .

Saya berharap $ watch callback dipecat setiap kali nama atau nama keluarga diubah, tetapi itu tidak terjadi.

Apa cara yang benar untuk melakukannya?

Vladimir Sidorenko
sumber
1
duplikat yang mungkin dari Bagaimana cara menonton array di angularjs?
Lort

Jawaban:

570

Panggil $watchdengan truesebagai argumen ketiga:

$scope.$watch('form', function(newVal, oldVal){
    console.log('changed');
}, true);

Secara default ketika membandingkan dua objek kompleks dalam JavaScript, mereka akan diperiksa untuk "referensi" kesetaraan, yang menanyakan apakah kedua objek merujuk pada hal yang sama, bukan "nilai" kesetaraan, yang memeriksa apakah nilai semua properti dari mereka benda sama.

Per dokumentasi Angular , parameter ketiga adalah untuk objectEquality:

Kapan objectEquality == true, ketidaksetaraan watchExpression ditentukan sesuai dengan angular.equalsfungsinya. Untuk menyimpan nilai objek untuk perbandingan nanti, angular.copyfungsi tersebut digunakan. Ini karena itu berarti bahwa menonton objek yang kompleks akan memiliki memori yang buruk dan implikasi kinerja.

rossipedia
sumber
2
Ini adalah jawaban yang baik, tetapi saya membayangkan ada saat-saat ketika Anda tidak ingin menonton objek secara rekursif
Jason
16
Ada. Secara default jika Anda tidak lulus true, maka itu akan memeriksa kesetaraan referensi jika nilai yang ditonton adalah objek atau array. Dalam hal ini, Anda harus menentukan properti secara eksplisit, seperti $scope.$watch('form.name')dan$scope.$watch('form.surname')
rossipedia
3
Terima kasih. Itulah tepatnya yang saya lewatkan. Jason, Anda benar - Anda tidak selalu ingin objek rekursif menonton, tetapi dalam kasus saya persis itulah yang saya butuhkan.
Vladimir Sidorenko
1
ImmutableJS dan perbandingan referensi dapat membantu meningkatkan kinerja.
Dragos Rusu
13

The formobjek tidak berubah, hanya nameproperti

biola diperbarui

function MyController($scope) {
$scope.form = {
    name: 'my name',
}

$scope.changeCount = 0;
$scope.$watch('form.name', function(newVal, oldVal){
    console.log('changed');
    $scope.changeCount++;
});
}
Jason
sumber
12

Sedikit tip kinerja jika seseorang memiliki jenis layanan datastore dengan pasangan kunci -> nilai:

Jika Anda memiliki layanan yang disebut dataStore , Anda dapat memperbarui cap waktu setiap kali objek data besar Anda berubah. Dengan cara ini, alih-alih memperhatikan seluruh objek, Anda hanya melihat stempel waktu untuk perubahan.

app.factory('dataStore', function () {

    var store = { data: [], change: [] };

    // when storing the data, updating the timestamp
    store.setData = function(key, data){
        store.data[key] = data;
        store.setChange(key);
    }

    // get the change to watch
    store.getChange = function(key){
        return store.change[key];
    }

    // set the change
    store.setChange = function(key){
        store.change[key] = new Date().getTime();
    }

});

Dan dalam arahan Anda hanya menonton cap waktu untuk berubah

app.directive("myDir", function ($scope, dataStore) {
    $scope.dataStore = dataStore;
    $scope.$watch('dataStore.getChange("myKey")', function(newVal, oldVal){
        if(newVal !== oldVal && newVal){
            // Data changed
        }
    });
});
Ferenc Takacs
sumber
1
Saya pikir perlu disebutkan bahwa dalam hal ini Anda akan kehilangan fungsionalitas memiliki akses ke nilai lama dari data yang Anda simpan. newVal, oldValdalam contoh ini adalah nilai timestamp bukan nilai objek yang diamati. Itu tidak membuat jawaban ini tidak valid, saya hanya berpikir bahwa itu adalah kelemahan yang layak disebut.
Michał Schielmann
Benar, metode ini kebanyakan saya gunakan ketika saya ingin memuat struktur data yang rumit sebagai sumber untuk sesuatu (versi baru dari beberapa data misalnya). Jika saya peduli dengan nilai-nilai baru dan lama saya tidak akan menyimpannya di objek-objek besar yang rumit, melainkan saya akan memiliki representasi teks kecil dari hal yang saya pedulikan dan melihatnya untuk perubahan atau mendapatkannya ketika timestamp berubah.
Ferenc Takacs
5

Alasan mengapa kode Anda tidak berfungsi adalah karena $watchsecara default memeriksa referensi. Jadi singkatnya itu memastikan bahwa objek yang dilewati adalah objek baru. Tetapi dalam kasus Anda, Anda hanya memodifikasi beberapa properti dari objek form yang tidak membuat yang baru. Untuk membuatnya berfungsi, Anda dapat lulus truesebagai parameter ketiga.

$scope.$watch('form', function(newVal, oldVal){
    console.log('invoked');
}, true);

Ini akan bekerja tetapi Anda dapat menggunakan $ watchCollection yang akan lebih efisien daripada $ watch karena $watchCollectionakan mengawasi properti dangkal pada objek form. Misalnya

$scope.$watchCollection('form', function (newVal, oldVal) {
    console.log(newVal, oldVal);
});
sol4me
sumber
4

Saat Anda mencari perubahan bentuk objek, pendekatan menonton terbaik adalah menggunakan
$watchCollection. Silakan lihat dokumentasi resmi untuk karakteristik kinerja yang berbeda.

Ramesh Papaganti
sumber
1

Coba ini:

function MyController($scope) {
    $scope.form = {
        name: 'my name',
        surname: 'surname'
    }

    function track(newValue, oldValue, scope) {
        console.log('changed');
    };

    $scope.$watch('form.name', track);
}
Tarun
sumber
0

Bagi siapa pun yang ingin melihat perubahan pada objek dalam array objek, ini sepertinya berhasil bagi saya (seperti solusi lain pada halaman ini tidak):

function MyController($scope) {

    $scope.array = [
        data1: {
            name: 'name',
            surname: 'surname'
        },
        data2: {
            name: 'name',
            surname: 'surname'
        },
    ]


    $scope.$watch(function() {
        return $scope.data,
    function(newVal, oldVal){
        console.log(newVal, oldVal);
    }, true);
Maximillion Bartango
sumber
-3

Anda harus mengubah $ watch ....

function MyController($scope) {
    $scope.form = {
        name: 'my name',
    }

    $scope.$watch('form.name', function(newVal, oldVal){
        console.log('changed');
     
    });
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.22/angular.min.js"></script>
<div ng-app>
    <div ng-controller="MyController">
        <label>Name:</label> <input type="text" ng-model="form.name"/>
            
        <pre>
            {{ form }}
        </pre>
    </div>
</div>

Vijay Patel
sumber
8
Menjawab pertanyaan dua tahun dengan jawaban yang merupakan duplikat yang hampir pasti dari yang sudah ada? Tidak keren.
Jared Smith