ฉันจะละเว้นการโหลดเริ่มต้นเมื่อดูการเปลี่ยนแปลงโมเดลใน AngularJS ได้อย่างไร


184

ฉันมีหน้าเว็บที่ทำหน้าที่เป็นตัวแก้ไขสำหรับเอนทิตีเดียวซึ่งอยู่ในรูปกราฟลึกในคุณสมบัติ $ scope.fieldcontainer หลังจากที่ฉันได้รับการตอบกลับจาก REST API ของฉัน (ผ่าน $ resource) ฉันเพิ่มนาฬิกาใน 'fieldcontainer' ฉันใช้นาฬิกาเรือนนี้เพื่อตรวจสอบว่าหน้า / เอนทิตีเป็น "สกปรก" ตอนนี้ฉันกำลังทำให้ปุ่มบันทึกเด้ง แต่จริง ๆ แล้วฉันต้องการทำให้ปุ่มบันทึกไม่ปรากฏจนกว่าผู้ใช้จะทำให้รูปแบบสกปรก

สิ่งที่ฉันได้รับคือสิ่งกระตุ้นหนึ่งของนาฬิกาซึ่งฉันคิดว่าจะเกิดขึ้นเพราะ. fieldcontainer = ... การมอบหมายจะเกิดขึ้นทันทีหลังจากที่ฉันสร้างนาฬิกาขึ้นมา ฉันกำลังคิดว่าจะใช้คุณสมบัติ "dirtyCount" เพื่อรับสัญญาณเตือนที่ผิดพลาดครั้งแรก แต่รู้สึกว่าแฮ็คมาก ... และฉันคิดว่าต้องมีวิธี "เชิงมุม" เพื่อจัดการกับสิ่งนี้ - ฉันไม่ใช่คนเดียว ใช้นาฬิกาเพื่อตรวจจับแบบจำลองที่สกปรก

นี่คือรหัสที่ฉันตั้งนาฬิกาของฉัน:

 $scope.fieldcontainer = Message.get({id: $scope.entityId },
            function(message,headers) {
                $scope.$watch('fieldcontainer',
                    function() {
                        console.log("model is dirty.");
                        if ($scope.visibility.saveButton) {
                            $('#saveMessageButtonRow').effect("bounce", { times:5, direction: 'right' }, 300);
                        }
                    }, true);
            });

ฉันแค่คิดว่าจะต้องมีวิธีที่สะอาดกว่าการปกป้องรหัส "UI ที่สกปรก" ของฉันด้วย "if (dirtyCount> 0)" ...


ฉันยังต้องสามารถโหลด $ scope.fieldcontainer อีกครั้งโดยใช้ปุ่มบางปุ่ม (เช่นการโหลดเอนทิตีเวอร์ชันก่อนหน้า) สำหรับสิ่งนี้ฉันจะต้องระงับนาฬิกาโหลดซ้ำจากนั้นกลับมาทำงานนาฬิกาต่อ
Kevin Hoffman

คุณจะพิจารณาเปลี่ยนคำตอบของฉันให้เป็นคำตอบที่ยอมรับหรือไม่ ผู้ที่ใช้เวลาในการอ่านตลอดทางจนถึงมีแนวโน้มที่จะยอมรับว่ามันเป็นทางออกที่ถูกต้อง
trixtur

@trixtur ฉันมีปัญหา OP เดียวกัน undefinedแต่ในกรณีของฉันคำตอบของคุณไม่ทำงานเนื่องจากค่าเก่าของฉันไม่ได้ มีค่าเริ่มต้นซึ่งจำเป็นในกรณีที่การอัปเดตโมเดลของฉันไม่ได้เกิดขึ้นกับข้อมูลทั้งหมด ดังนั้นค่าบางอย่างไม่เปลี่ยนแปลง แต่ต้องกระตุ้น
oliholz

@oliholz ​​ดังนั้นแทนที่จะตรวจสอบกับ undefined ให้นำ "typeof" ออกและตรวจสอบ old_fieldcontainer กับค่าเริ่มต้นของคุณ
trixtur

@KevinHoffman คุณควรทำเครื่องหมายคำตอบของ MW ที่คำตอบที่ถูกต้องสำหรับคนอื่นที่ค้นหาคำถามนี้ มันเป็นทางออกที่ดีกว่ามาก ขอบคุณ!
Dev01

คำตอบ:


118

ตั้งค่าสถานะก่อนโหลดครั้งแรก

var initializing = true

และจากนั้นเมื่อ $ watch แรกทำการเริ่มให้ทำ

$scope.$watch('fieldcontainer', function() {
  if (initializing) {
    $timeout(function() { initializing = false; });
  } else {
    // do whatever you were going to do
  }
});

การตั้งค่าสถานะจะพังลงเมื่อสิ้นสุดรอบการสรุปปัจจุบันดังนั้นการเปลี่ยนแปลงครั้งต่อไปจะไม่ถูกบล็อก


17
ใช่นั่นจะใช้งานได้ แต่เป็นการเปรียบเทียบวิธีมูลค่าเก่าและใหม่ที่แนะนำโดย @MW มีทั้งความเรียบง่ายและสำนวนเชิงมุมมากกว่า คุณสามารถอัพเดตคำตอบที่ยอมรับได้ไหม?
gerryster

2
มักจะตั้งค่า OLDVALUE เท่ากับ newValue บนไฟแรกที่ถูกเพิ่มเข้ามาโดยเฉพาะเพื่อหลีกเลี่ยงการทำธงนี้สับ
ชาร์ลีมาร์ติน

2
จริง ๆ แล้วคำตอบของ MW นั้นไม่ถูกต้องเนื่องจากค่าใหม่ของค่าเก่าจะไม่จัดการกับกรณีที่ค่าเก่าไม่ถูกกำหนด
trixtur

1
สำหรับผู้แสดงความคิดเห็น: นี่ไม่ใช่โมเดลของพระเจ้าไม่มีอะไรป้องกันคุณให้ใส่ตัวแปร 'เริ่มต้น' ภายในขอบเขตของมัณฑนากรแล้วตกแต่งนาฬิกา "ปกติ" ของคุณด้วย ไม่ว่าในกรณีใดก็ตามที่อยู่นอกขอบเขตของคำถามนี้อย่างสมบูรณ์
เขียนใหม่

1
ดูplnkr.co/edit/VrWrHHWwukqnxaEM3J5iสำหรับการเปรียบเทียบวิธีการที่เสนอทั้งการตั้งค่าตัวเฝ้าดูในการเรียกกลับ. get () และการเริ่มต้นขอบเขต
เขียนใหม่

483

ครั้งแรกที่ผู้ฟังเรียกค่าเก่าและค่าใหม่จะเหมือนกัน ดังนั้นเพียงแค่ทำสิ่งนี้:

$scope.$watch('fieldcontainer', function(newValue, oldValue) {
  if (newValue !== oldValue) {
    // do whatever you were going to do
  }
});

นี่คือวิธีที่ Angular docs แนะนำให้จัดการ :

หลังจากผู้สังเกตการณ์ลงทะเบียนกับขอบเขตผู้ฟัง fn จะถูกเรียกว่าแบบอะซิงโครนัส (ผ่าน $ evalAsync) เพื่อเริ่มต้นผู้เฝ้าดู ในกรณีที่ไม่ค่อยเกิดขึ้นสิ่งนี้ไม่พึงประสงค์เนื่องจากผู้ฟังถูกเรียกเมื่อผลลัพธ์ของ watchExpression ไม่เปลี่ยนแปลง ในการตรวจสอบสถานการณ์นี้ภายในตัวฟัง fn คุณสามารถเปรียบเทียบ newVal และ oldVal หากค่าทั้งสองนี้เหมือนกัน (===) ดังนั้นผู้ฟังจึงถูกเรียกเนื่องจากการเริ่มต้น


3
วิธีนี้เป็นสำนวนที่มากกว่าคำตอบของ @ rewritten
JiříVypědřík

25
สิ่งนี้ไม่ถูกต้อง จุดนี้จะเพิกเฉยต่อการเปลี่ยนแปลงจากnullค่าเริ่มต้นที่จะไม่เพิกเฉยต่อการเปลี่ยนแปลงเมื่อไม่มีการเปลี่ยนแปลง
เขียนใหม่

1
ฟังก์ชั่นการฟังจะทำงานเมื่อมีการสร้างนาฬิกา นี่เป็นการละเว้นการโทรครั้งแรกซึ่งเป็นสิ่งที่ฉันเชื่อว่าผู้ถามต้องการ หากเขาต้องการเพิกเฉยต่อการเปลี่ยนแปลงเมื่อค่าเก่าเป็นโมฆะแน่นอนว่าเขาสามารถเปรียบเทียบ oldValue เป็นโมฆะได้ ... แต่ฉันไม่คิดว่านั่นเป็นสิ่งที่เขาต้องการจะทำ
เมกะวัตต์

OP กำลังถามเกี่ยวกับผู้เฝ้าดูทรัพยากรโดยเฉพาะ (จากบริการ REST) ทรัพยากรถูกสร้างขึ้นครั้งแรกจากนั้นผู้เฝ้าดูจะนำไปใช้แล้วจากนั้นจะเขียนแอตทริบิวต์จากการตอบกลับและจากนั้น Angular จะทำการวนรอบ กระโดดข้ามรอบแรกด้วย "การเริ่มต้น" ธงมีวิธีการใช้เฝ้าดูเท่านั้นเกี่ยวกับทรัพยากรที่เริ่ม nullแต่ยังคงเฝ้าดูการเปลี่ยนแปลงจาก
เขียนใหม่

7
สิ่งนี้ผิดoldValueจะเป็นโมฆะในการไปรอบแรก
Kevin C.

42

ฉันรู้ว่าคำถามนี้ได้รับคำตอบแล้ว แต่ฉันมีข้อเสนอแนะ:

$scope.$watch('fieldcontainer', function (new_fieldcontainer, old_fieldcontainer) {
    if (typeof old_fieldcontainer === 'undefined') return;

    // Other code for handling changed object here.
});

การใช้ค่าสถานะทำงานได้ แต่มีกลิ่นของรหัสเล็กน้อยคุณไม่คิดหรือ


1
นี่คือวิธีที่จะไป ใน Coffeescript มันง่ายเหมือนreturn unless oldValue?(? เป็นตัวดำเนินการที่มีอยู่ใน CS)
Kevin C.

1
อุปกรณ์ประกอบฉากเกี่ยวกับกลิ่นรหัสคำ! ยังตรวจสอบวัตถุพระเจ้า lol ดีเกินไป.
Matthew Harwood

1
แม้ว่านี่ไม่ใช่คำตอบที่ยอมรับ แต่ก็ทำให้รหัสของฉันทำงานเมื่อเติมวัตถุจาก API และฉันต้องการดูสถานะการบันทึกที่แตกต่างกัน ดีมาก.
Lucas Reppe Welander

1
ขอบคุณ - สิ่งนี้ช่วยฉัน
Wand Maker

1
นี่เป็นสิ่งที่ดี แต่ในกรณีของฉันฉันได้ยกตัวอย่างข้อมูลเป็นอาเรย์ที่ว่างเปล่าก่อนที่จะทำการเรียก AJAX ไปยัง API ของฉันดังนั้นตรวจสอบความยาวของประเภทนั้นแทนประเภท แต่คำตอบนี้แสดงวิธีที่มีประสิทธิภาพที่สุดอย่างน้อยที่สุด
Saborknight

4

ในระหว่างการโหลดครั้งแรกของเขตข้อมูลค่าเก่าค่าปัจจุบันจะไม่ได้กำหนด ตัวอย่างด้านล่างช่วยให้คุณไม่รวมการโหลดครั้งแรก

$scope.$watch('fieldcontainer', 
  function(newValue, oldValue) {
    if (newValue && oldValue && newValue != oldValue) {
      // here what to do
    }
  }), true;

คุณอาจต้องการใช้! == ตั้งแต่nullและundefinedจะจับคู่ในสถานการณ์นี้หรือ ( '1' == 1ฯลฯ )
Jamie Pate

โดยทั่วไปคุณมีสิทธิ แต่ในกรณีนั้น newValue และ oldValue ไม่สามารถเป็นค่ามุมได้เนื่องจากการตรวจสอบก่อนหน้า ค่าทั้งสองมีประเภทเดียวกันเนื่องจากเป็นของเขตข้อมูลเดียวกัน ขอบคุณ.
Tahsin Turkoz

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