$ ดูวัตถุ


317

ฉันต้องการดูการเปลี่ยนแปลงในพจนานุกรม แต่ด้วยเหตุผลบางประการการโทรกลับไม่ได้รับการโทร

นี่คือคอนโทรลเลอร์ที่ฉันใช้:

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

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

นี่คือไวโอลิน

ฉันคาดว่าจะได้รับเงิน $ watchbackback ในการเปลี่ยนชื่อหรือนามสกุลทุกครั้ง แต่จะไม่เกิดขึ้น

วิธีที่ถูกต้องในการทำคืออะไร?


1
เป็นไปได้ที่ซ้ำกันของวิธีการดูอาร์เรย์ในเชิงลึก?
lort

คำตอบ:


570

สอบถาม$watchกับtrueเป็นอาร์กิวเมนต์ที่สาม:

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

โดยค่าเริ่มต้นเมื่อเปรียบเทียบสองวัตถุที่ซับซ้อนใน JavaScript พวกเขาจะถูกตรวจสอบเพื่อความเท่าเทียมกัน "อ้างอิง" ซึ่งถามว่าวัตถุทั้งสองอ้างถึงสิ่งเดียวกันมากกว่า "ความคุ้มค่า" ซึ่งจะตรวจสอบว่าค่าของคุณสมบัติทั้งหมดของเหล่านั้น วัตถุมีค่าเท่ากัน

ตามเอกสารเชิงมุมพารามิเตอร์ที่สามมีไว้สำหรับobjectEquality:

เมื่อobjectEquality == trueใดความไม่เสมอภาคของ watchExpression จะถูกกำหนดตามangular.equalsฟังก์ชั่น เพื่อบันทึกค่าของวัตถุสำหรับการเปรียบเทียบในภายหลังangular.copyฟังก์ชั่นจะใช้ ซึ่งหมายความว่าการรับชมวัตถุที่ซับซ้อนจะมีหน่วยความจำและผลกระทบด้านประสิทธิภาพ


2
นี่คือคำตอบที่ดี แต่ผมคิดว่ามีบางครั้งเมื่อคุณไม่ต้องการที่จะดูซ้ำวัตถุ
เจสัน

16
มี โดยค่าเริ่มต้นหากคุณไม่ผ่านtrueมันจะตรวจสอบความเท่าเทียมกันอ้างอิงถ้าค่าดูเป็นวัตถุหรืออาร์เรย์ ในกรณีนี้คุณควรระบุคุณสมบัติอย่างชัดเจนเช่น$scope.$watch('form.name')และ$scope.$watch('form.surname')
rossipedia

3
ขอบคุณ นั่นคือสิ่งที่ฉันพลาด เจสันคุณพูดถูก - คุณไม่ต้องการดูวัตถุแบบเรียกซ้ำ แต่ในกรณีของฉันมันเป็นสิ่งที่ฉันต้องการ
Vladimir Sidorenko

1
ImmutableJS และการเปรียบเทียบการอ้างอิงอาจช่วยเพิ่มประสิทธิภาพ
Dragos Rusu

13

formวัตถุไม่ได้เปลี่ยนแปลงเพียงnameทรัพย์สิน

ซออัพเดทแล้ว

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

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

12

เคล็ดลับประสิทธิภาพเล็กน้อยหากใครบางคนมีบริการดาต้าสโตร์พร้อมคีย์ -> คู่ของค่า:

หากคุณมีบริการที่เรียกว่าdataStoreคุณสามารถอัปเดตการประทับเวลาเมื่อใดก็ตามที่วัตถุข้อมูลขนาดใหญ่ของคุณเปลี่ยนแปลง วิธีนี้แทนที่จะดูวัตถุทั้งหมดอย่างลึกซึ้งคุณเพียง แต่เฝ้าดูการประทับเวลาสำหรับการเปลี่ยนแปลง

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

});

และในคำสั่งคุณเพียง แต่เฝ้าดูการประทับเวลาเพื่อเปลี่ยน

app.directive("myDir", function ($scope, dataStore) {
    $scope.dataStore = dataStore;
    $scope.$watch('dataStore.getChange("myKey")', function(newVal, oldVal){
        if(newVal !== oldVal && newVal){
            // Data changed
        }
    });
});

1
ฉันคิดว่ามันคุ้มค่าที่จะกล่าวถึงว่าในกรณีนี้คุณจะสูญเสียความสามารถในการเข้าถึงข้อมูลเก่าที่คุณเก็บไว้ newVal, oldValในตัวอย่างนี้เป็นค่าการประทับเวลาไม่ได้สังเกตค่าวัตถุ มันไม่ได้ทำให้คำตอบนี้ไม่ถูกต้องฉันแค่คิดว่ามันเป็นข้อเสียเปรียบที่ควรพูดถึง
Michał Schielmann

จริงวิธีนี้ฉันส่วนใหญ่ใช้เมื่อฉันต้องการโหลดโครงสร้างข้อมูลที่ซับซ้อนเป็นแหล่งข้อมูลสำหรับบางสิ่ง (ตัวอย่างเช่นเวอร์ชั่นใหม่ของข้อมูลบางส่วน) ถ้าฉันสนใจเกี่ยวกับค่าใหม่และเก่าฉันจะไม่เก็บไว้ในวัตถุที่ซับซ้อนขนาดใหญ่ แต่ฉันจะมีการแสดงข้อความเล็ก ๆ ของสิ่งที่ฉันสนใจและเฝ้าดูการเปลี่ยนแปลงหรือรับเมื่อการเปลี่ยนแปลงเวลา
Ferenc Takacs

5

สาเหตุที่รหัสของคุณไม่ทำงานเนื่องจาก$watchการตรวจสอบอ้างอิงโดยค่าเริ่มต้น ดังนั้นโดยสังเขปมันทำให้แน่ใจว่าวัตถุที่ถูกส่งผ่านไปนั้นเป็นวัตถุใหม่ แต่ในกรณีของคุณคุณเพียงแค่ปรับเปลี่ยนคุณสมบัติของวัตถุแบบฟอร์มไม่ได้สร้างสิ่งใหม่ เพื่อให้มันทำงานได้คุณสามารถผ่านtrueเป็นพารามิเตอร์ที่สามได้

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

มันจะใช้งานได้ แต่คุณสามารถใช้ $ watchCollection ซึ่งจะมีประสิทธิภาพมากกว่า $ watch เนื่องจาก$watchCollectionจะคอยดูคุณสมบัติที่ตื้นบนวัตถุแบบฟอร์ม เช่น

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

4


$watchCollectionในขณะที่คุณกำลังมองหาการเปลี่ยนแปลงรูปแบบวัตถุวิธีดูดีที่สุดคือการใช้งาน โปรดดูเอกสารประกอบอย่างเป็นทางการสำหรับคุณสมบัติด้านประสิทธิภาพที่แตกต่างกัน


1

ลองสิ่งนี้:

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

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

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

0

สำหรับทุกคนที่ต้องการดูการเปลี่ยนแปลงวัตถุภายในอาร์เรย์ของวัตถุสิ่งนี้ดูเหมือนจะใช้ได้สำหรับฉัน (เนื่องจากวิธีการแก้ปัญหาอื่น ๆ ในหน้านี้ไม่ได้):

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

-3

คุณต้องเปลี่ยนเป็น $ 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>


8
ตอบคำถามอายุสองปีพร้อมคำตอบที่ซ้ำซ้อนกับคำถามเดิม ไม่เท่ห์
Jared Smith
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.