เปลี่ยนเหตุการณ์เมื่อเลือกด้วยการผูกสิ่งที่น่าพิศวงฉันจะรู้ได้อย่างไรว่าเป็นการเปลี่ยนแปลงจริง?


87

ฉันกำลังสร้าง UI การอนุญาตฉันมีรายการสิทธิ์พร้อมรายการเลือกถัดจากสิทธิ์แต่ละรายการ สิทธิ์จะแสดงโดยอาร์เรย์ของวัตถุที่สังเกตได้ซึ่งเชื่อมโยงกับรายการที่เลือก:

<div data-bind="foreach: permissions">
     <div class="permission_row">
          <span data-bind="text: name"></span>
          <select data-bind="value: level, event:{ change: $parent.permissionChanged}">
                   <option value="0"></option>
                   <option value="1">R</option>
                   <option value="2">RW</option>
           </select>
      </div>
 </div>

ตอนนี้ปัญหาคือ: เหตุการณ์การเปลี่ยนแปลงจะเพิ่มขึ้นเมื่อ UI เพิ่งเติมข้อมูลเป็นครั้งแรก ฉันเรียกใช้ฟังก์ชัน ajax ของฉันรับรายการสิทธิ์จากนั้นเหตุการณ์จะถูกยกขึ้นสำหรับแต่ละรายการสิทธิ์ นี่ไม่ใช่พฤติกรรมที่ฉันต้องการจริงๆ ฉันต้องการให้เพิ่มขึ้นก็ต่อเมื่อผู้ใช้เลือกค่าใหม่สำหรับสิทธิ์ในรายการเลือกเท่านั้นฉันจะทำอย่างไร

คำตอบ:


109

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

วิธีที่น่าพิศวงของการเพิ่มsubscriptionจะไม่ช่วยในทุกกรณีเพราะเหตุใดในรูปแบบส่วนใหญ่จะดำเนินการเช่นนี้

  1. เริ่มต้นโมเดลด้วยข้อมูลที่ไม่ได้กำหนดเพียงโครงสร้าง (actual KO initilization)
  2. อัปเดตโมเดลด้วยข้อมูลเบื้องต้น (logical init like load JSON , get data etc)
  3. การโต้ตอบและการอัปเดตของผู้ใช้

ขั้นตอนจริงที่เราต้องการจับภาพคือการเปลี่ยนแปลงใน 3 แต่ในขั้นตอนที่สองsubscriptionจะได้รับการโทรดังนั้นวิธีที่ดีกว่าคือเพิ่มการเปลี่ยนแปลงเหตุการณ์เช่น

 <select data-bind="value: level, event:{ change: $parent.permissionChanged}">

และตรวจพบเหตุการณ์ในpermissionChangedฟังก์ชัน

this.permissionChanged = function (obj, event) {

  if (event.originalEvent) { //user changed

  } else { // program changed

  }

}

5
ฉันคิดว่านี่ควรถูกทำเครื่องหมายว่าเป็นคำตอบที่ถูกต้อง ฉันมีสถานการณ์ที่แน่นอนตามที่อธิบายไว้ในคำถามและฉันทดสอบการส่งสตริงเป็นคีย์ไปยังselectองค์ประกอบ อย่างไรก็ตามแม้ว่าค่าเริ่มต้นของการเลือกจะตรงกับตัวเลือกใดตัวเลือกหนึ่ง แต่ก็ยังทริกเกอร์changeเหตุการณ์ เมื่อฉันใช้ifบล็อกง่ายๆนี้ในตัวจัดการทุกอย่างทำงานได้ตามที่คาดไว้ ลองใช้วิธีนี้ก่อน ขอบคุณ @Sarath!
Pflugs

ฉันชอบแนวคิดของการแยกความแตกต่างระหว่างการเปลี่ยนผู้ใช้และโปรแกรมที่เปลี่ยนแปลง สิ่งนี้มีประโยชน์ในกรณีปกติทั่วไปโดยเฉพาะอย่างยิ่งกับสิ่งที่น่าพิศวงที่ผู้สังเกตมักจะเปลี่ยนค่าโดยไม่ต้องโต้ตอบกับผู้ใช้
rodiwa

เห็นด้วยกับ @Pflugs การแยกความแตกต่างระหว่างประเภทเหตุการณ์ที่ได้ผลสำหรับฉันและนี่ควรเป็นคำตอบที่ยอมรับได้
Emma Middlebrook

1
def ควรเป็นคำตอบที่ยอมรับ ... คุณสมบัติประเภทเหตุการณ์สามารถใช้เพื่อแยกความแตกต่างของการเรียกใช้
premissionChanged

1
นอกจากนี้ยังใช้วิธีนี้เพื่อตรวจสอบว่าผู้ใช้เปลี่ยนการเลือกมากกว่าทริกเกอร์อื่นหรือไม่ ในกรณีของฉันการอัปเดตค่าตัวเลือกผ่านการผูก "ตัวเลือก" ทำให้เกิดเหตุการณ์ "การเปลี่ยนแปลง"
ภูมิพลอดุลยเดช

32

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

นอกจากนี้วิธีที่ "น่าพิศวง" มากขึ้นในการดำเนินการนี้คือการไม่ใช้ตัวจัดการเหตุการณ์ แต่ใช้การสังเกตและการสมัครสมาชิก ทำให้levelสังเกตแล้วเพิ่มการสมัครสมาชิกไปซึ่งจะได้รับการเรียกใช้เมื่อใดก็ตามที่levelมีการเปลี่ยนแปลง


หลังจาก 2 ชั่วโมงของการขุดสาเหตุแบบฟอร์มของฉันทำให้เกิดเหตุการณ์ 'การเปลี่ยนแปลง' หลังจากการโหลดหน้าเว็บคุณเดาว่าช่วยฉันได้
Samih A

คำตอบของคุณทำให้ฉันมีความคิด ... ฉันตั้งใจเปลี่ยนประเภทที่มาจาก url ดังนั้นฟังก์ชัน subscribe ของฉันจะทริกเกอร์เมื่อโหลดหน้าเว็บ (ฉันต้องการให้มันประมวลผล url var ไม่ใช่แค่เมื่อดรอปดาวน์ของฉันทริกเกอร์การเปลี่ยนแปลง) มีวิธีแฮ็กน้อยกว่านี้หรือไม่?
Jiffy

4

นี่คือวิธีแก้ปัญหาที่อาจช่วยให้มีพฤติกรรมแปลก ๆ นี้ได้ ฉันไม่พบวิธีแก้ปัญหาที่ดีไปกว่าการวางปุ่มเพื่อเรียกเหตุการณ์การเปลี่ยนแปลงด้วยตนเอง

แก้ไข: บางทีการผูกแบบกำหนดเองเช่นนี้อาจช่วยได้:

ko.bindingHandlers.changeSelectValue = {

   init: function(element,valueAccessor){

        $(element).change(function(){

            var value = $(element).val();

            if($(element).is(":focus")){

                  //Do whatever you want with the new value
            }

        });

    }
  };

และในแอตทริบิวต์การผูกข้อมูลที่คุณเลือกเพิ่ม:

changeSelectValue: yourSelectValue

โอ้ ... คุณหมายถึงเหมือนปุ่มบันทึกหรือไม่ .. ไม่ดีสำหรับฉัน .. ฉันแค่ต้องการสิ่งนี้เพื่อใช้งาน :)
guy schaller

แก้ไขคำตอบด้วยวิธีแก้ปัญหาที่ดีกว่า (หวังว่า)
Ingro

เฮ้ Ingro ถูกตั้งค่าสถานะ (ไม่ถูกต้อง) ว่าไม่ใช่คำตอบ ฉันเปลี่ยนประโยคเริ่มต้นของคุณเพื่อไม่ให้ผู้ติดธงคิดว่าคุณโพสต์คำถามเป็นคำตอบโดยบังเอิญ หวังว่านี่จะช่วยได้! :)
jmort253

@ jmort253: ขอโทษนั่นเป็นความผิดของฉัน ไม่ควรตอบโดยใช้ "ฉันก็มีปัญหาเหมือนกัน" เพราะดูเหมือนว่าคุณกำลังถามอย่างอื่นอยู่ แทนที่จะให้คำตอบโดยตรงและอธิบายวิธีการทำงาน
Qantas 94 Heavy

1
ขอโทษด้วยความผิดพลาดของฉัน นี่เป็นหนึ่งในคำตอบแรกที่ฉันโพสต์ใน stackoverflow และไม่รู้กฎทั้งหมด ขอบคุณสำหรับการแก้ไข!
Ingro

3

ฉันใช้การผูกแบบกำหนดเองนี้ (อ้างอิงจากซอนี้โดย RP Niemeyer ดูคำตอบของเขาสำหรับคำถามนี้ ) ซึ่งทำให้แน่ใจว่าค่าตัวเลขถูกแปลงจากสตริงเป็นตัวเลขอย่างถูกต้อง (ตามคำแนะนำของ Michael Best):

Javascript:

ko.bindingHandlers.valueAsNumber = {
    init: function (element, valueAccessor, allBindingsAccessor) {
        var observable = valueAccessor(),
            interceptor = ko.computed({
                read: function () {
                    var val = ko.utils.unwrapObservable(observable);
                    return (observable() ? observable().toString() : observable());
                },
                write: function (newValue) {
                    observable(newValue ? parseInt(newValue, 10) : newValue);
                },
                owner: this
            });
        ko.applyBindingsToNode(element, { value: interceptor });
    }
};

ตัวอย่าง HTML:

<select data-bind="valueAsNumber: level, event:{ change: $parent.permissionChanged }">
    <option value="0"></option>
    <option value="1">R</option>
    <option value="2">RW</option>
</select>

2

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


1

รวดเร็วและสกปรกโดยใช้แฟล็กง่ายๆ:

var bindingsApplied = false;

var ViewModel = function() {
    // ...

    this.permissionChanged = function() {
        // ignore, if flag not set
        if (!flag) return;

        // ...
    };
};

ko.applyBindings(new ViewModel());
bindingsApplied = true; // done with the initial population, set flag to true

หากวิธีนี้ไม่ได้ผลให้ลองตัดบรรทัดสุดท้ายใน setTimeout () - เหตุการณ์เป็นแบบ async ดังนั้นรายการสุดท้ายอาจยังคงค้างอยู่เมื่อส่งคืน applyBindings () แล้ว


สวัสดีขอบคุณสำหรับการตอบกลับสิ่งนี้ใช้ไม่ได้สำหรับฉันเพราะฉันเรียก ko.applyBindings เมื่อโดมพร้อม แต่สิทธิ์ของฉันจะถูกเติมลงในโมเดลของฉันในระยะต่อมา ... ดังนั้นการตั้งค่าสถานะจะเป็นจริง แต่ ปัญหาจะยังคงเกิดขึ้น
guy schaller

0

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

self.permissionChanged = function (l) {
    if (typeof l != 'undefined') {
        ...
    }
}

ดูเหมือนว่าจะได้ผลสำหรับฉัน



0

ลองอันนี้:

self.GetHierarchyNodeList = function (data, index, event)
{
    debugger;
    if (event.type != "change") {
        return;
    }        
} 

event.type == "change"
event.type == "load" 

พารามิเตอร์ที่ 2 ไม่ใช่ดัชนีสำหรับฉันมันเป็นเหตุการณ์
sairfan

@sairfan เพิ่มพารามิเตอร์สองสามตัวลงในฟังก์ชันและค้นหาจากพารามิเตอร์ที่คุณได้รับเหตุการณ์โดยใช้ดีบักเกอร์
Chanaka Sampath

-1

หากคุณกำลังทำงานโดยใช้ Knockout ให้ใช้ฟังก์ชันหลักของฟังก์ชันการทำงานที่สังเกตได้ที่น่าพิศวง
ใช้ko.computed()วิธีการและทำและทริกเกอร์การโทร ajax ภายในฟังก์ชันนั้น

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