การผูก true / false กับปุ่มตัวเลือกใน Knockout JS


84

ในโมเดลมุมมองของฉันฉันมีค่า IsMale ที่มีค่าเป็นจริงหรือเท็จ

ใน UI ของฉันฉันต้องการเชื่อมโยงกับปุ่มตัวเลือกต่อไปนี้:

<label>Male
   <input type="radio" name="IsMale" value="true" data-bind="checked:IsMale"/>
</label> 
<label>Female
   <input type="radio" name="IsMale" value="false" data-bind="checked:IsMale"/>
</label>

ปัญหาที่ฉันคิดcheckedว่าคาดว่าจะมีสตริง "true" / "false" คำถามของฉันคือฉันจะรับการเชื่อม 2 ทางพร้อม UI และโมเดลนี้ได้อย่างไร


10
สำหรับเวอร์ชัน Knockout> = 3.0 โปรดดูคำตอบของ Natanสำหรับข้อตกลงที่ง่ายกว่าคำตอบที่ยอมรับ
Markus Pscheidt

คำตอบ:


81

ทางเลือกหนึ่งคือการใช้เขียนได้คำนวณสังเกต

ในกรณีนี้ฉันคิดว่าตัวเลือกที่ดีคือการทำให้สิ่งที่สามารถสังเกตได้จากการคำนวณที่เขียนได้กลายเป็น "สิ่งที่สังเกตได้" ของสิ่งที่คุณIsMaleสังเกตได้ โมเดลมุมมองของคุณจะมีลักษณะดังนี้:

var ViewModel = function() {
   this.IsMale = ko.observable(true);

   this.IsMale.ForEditing = ko.computed({
        read: function() {
            return this.IsMale().toString();  
        },
        write: function(newValue) {
             this.IsMale(newValue === "true");
        },
        owner: this        
    });          
};

คุณจะผูกไว้ใน UI ของคุณเช่น:

<label>Male
   <input type="radio" name="IsMale" value="true" data-bind="checked:IsMale.ForEditing"/>
</label> 
<label>Female
   <input type="radio" name="IsMale" value="false" data-bind="checked:IsMale.ForEditing"/>
</label>

ตัวอย่าง: http://jsfiddle.net/rniemeyer/Pjdse/


ดูเหมือนเป็นทางออกที่ดี ฉันใช้ปลั๊กอินการแมปเพื่อรีเฟรช VM ของฉัน คุณรู้หรือไม่ว่าจะกำจัด ForEditing บน IsMale?
CJ

26
นี่คืออีกทางเลือกหนึ่งที่ผลักดันการสร้างที่สังเกตได้จากการคำนวณไปสู่การเชื่อมโยงแบบกำหนดเองซึ่งจะทำให้คุณไม่ต้องกังวลเกี่ยวกับการจัดการกับมันในปลั๊กอินการแมป: jsfiddle.net/rniemeyer/utsvJ
RP Niemeyer

2
+1 เพื่อใช้การผูกแทนการสร้างสิ่งที่สังเกตได้ในทุกวัตถุ
Greg Ennis

1
@RPNiemeyer เป็นทางไปแน่นอน
Andrew

1
@RPNiemeyer ขอบคุณ! เสียงซอในความคิดเห็นเป็นสิ่งที่เหมาะกับฉัน
SFlagg

128

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

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

<input type="radio" name="a" data-bind="checked:IsChecked, checkedValue: true"/>
<input type="radio" name="a" data-bind="checked:IsChecked, checkedValue: false"/>

หรือ:

<input type="radio" name="b" data-bind="checked:Quantity, checkedValue: 1"/>
<input type="radio" name="b" data-bind="checked:Quantity, checkedValue: 2"/>
<input type="radio" name="b" data-bind="checked:Quantity, checkedValue: 3"/>

2
มองไปที่แหล่งที่มาก็ดูเหมือนว่าจะทำให้การตัดสินใจที่จะใช้ค่าการตรวจสอบที่นี่ 'การ useCheckedValue' กำหนดเป็นจริงถ้าใส่เป็นวิทยุหรือช่องทำเครื่องหมายที่มีค่าเป็นอาร์เรย์ นอกจากนี้ฉันใช้ Knockout 3.0 ดูว่าช่วยได้หรือไม่
Natan

1
ใช่ขอบคุณฉันอัปเกรดเป็น 3.0.0 แล้วและตอนนี้ก็อยู่ที่นั่น ยังคงต้องรวมค่าบูลีนของฉันในสตริงเนื่องจากรหัสเซิร์ฟเวอร์คาดหวังสิ่งเหล่านั้น ;-)
ZiglioUK

ข้อมูลที่ดี อีกสองประเด็น: (1) ฉันพบว่าก่อน v3.0 สิ่งที่น่าพิศวงฉันสามารถใช้การvalueผูกและการcheckedผูก (ตามลำดับนั้น) แต่ด้วย 3.0 ฉันต้องใช้การcheckedValueผูกแทนการvalueผูก (2) pre-v3.0 เป็นเรื่องที่จู้จี้จุกจิกเกี่ยวกับการกำหนดให้การvalueผูกเพื่อนำหน้าการcheckedเชื่อมโยงเพื่อให้ทำงานได้อย่างถูกต้องดังนั้นฉันจึงรู้สึกว่าสิ่งต่าง ๆ อาจทำงานได้ดีขึ้นใน v3.0 ในทุกสถานการณ์หากคุณใส่การcheckedValueโยงก่อนการcheckedผูกเช่น พวกเขาแสดงในเอกสาร
Jason Frank

@ZiglioNZ วิธีหนึ่งที่อาจล้มเหลวคือถ้าตัวcheckedตั้งค่าของคุณ(หรืออาจมีบางอย่างขึ้นอยู่กับมัน) ทำให้เกิดข้อผิดพลาด - จากนั้นไม่ได้เลือกช่องทำเครื่องหมายที่ถูกต้อง
Simon_Weaver

ฉันใช้เวอร์ชัน 3.4 และพบว่าฉันยังคงต้องใส่ค่า = "true" และข้างต้นเพื่อให้ใช้งานได้
เวียน

11

สิ่งนี้ใช้ได้กับฉัน:

http://jsfiddle.net/zrBuL/291/

<label>Male
   <input type="radio" name="IsMale" value="1" data-bind="checked:IsMale"/>
</label> 
<label>Female
   <input type="radio" name="IsMale" value="0" data-bind="checked:IsMale"/>
</label>

ปัญหาเดียวที่ฉันเห็นคือเมื่อทำให้อนุกรมวิวโมเดลส่งผ่านไปยังเซิร์ฟเวอร์คุณจะได้รับจำนวนเต็มในที่สังเกตได้ (แทนที่จะเป็นบูลีน) คุณจะต้องโทรvm.IsMale(!!vm.+IsMale());ก่อนทำการซีเรียลไลซ์ json เพื่อส่งไปยังเซิร์ฟเวอร์ (ในกรณีที่ฝั่งเซิร์ฟเวอร์ไม่สามารถจัดการได้อย่างถูกต้อง)
Artem

ฉันคิดว่าคุณพูดถูก แต่ความรู้ Javascript ของฉันยังขาดอยู่ ช่วยอธิบายให้ฟังหน่อยได้ไหมว่า !! + ทำงานอย่างไร ฉันไม่คุ้นเคยกับไวยากรณ์นั้น
CJ

1
@CJ สิ่งนี้จะแปลงสตริงหรือตัวเลขเป็นบูลีน - ตรวจสอบ jsfiddle.net/nickolsky/6ydLZนี้หากสตริงมีบูลอยู่แล้วจะทำให้เป็นบูล
Artem

ขอบคุณมาก ๆ. ฉันคิดว่านี่เป็นทางออกที่ดีเช่นกัน
CJ

1
มันใช้ไม่ได้ทั้งสองวิธี ไม่ได้ตรวจสอบปุ่มตัวเลือกเมื่อโหลด
Mikael Östberg

1
ko.bindingHandlers['radiobuttonyesno'] = {
    'init': function (element, valueAccessor, allBindingsAccessor) {
        var stateHandler = function (property, allBindingsAccessor, key, value, checkIfDifferent) {
            if (!property || !ko.isObservable(property)) {
                var propWriters = allBindingsAccessor()['_ko_property_writers'];
                if (propWriters && propWriters[key])
                    propWriters[key](value);
            } else if (ko.isWriteableObservable(property) && (!checkIfDifferent || property.peek() !== value)) {
                property(value);
            }
        };

        var updateHandler = function () {
            var valueToWrite;

            if ((element.type == "radio") && (element.checked)) {
                valueToWrite = element.value;
            } else {
                return; // "radiobuttonyesno" binding only responds to selected radio buttons
            }

            valueToWrite = (valueToWrite === "True") ? true : false;

            var modelValue = valueAccessor(), unwrappedValue = ko.utils.unwrapObservable(modelValue); //can be true of false

            stateHandler(modelValue, allBindingsAccessor, 'checked', valueToWrite, true);
        };
        ko.utils.registerEventHandler(element, "click", updateHandler);

        // IE 6 won't allow radio buttons to be selected unless they have a name
        if ((element.type == "radio") && !element.name)
            ko.bindingHandlers['uniqueName']['init'](element, function () { return true });
    },
    'update': function (element, valueAccessor) {
        var value = ko.utils.unwrapObservable(valueAccessor());

        value = value ? "True" : "False";

        if (element.type == "radio") {
            element.checked = (element.value == value);
        }
    }
};

ใช้สารยึดเกาะนี้แทนการสร้างสิ่งที่สังเกตได้จากการคำนวณโคโง่ ๆ

ตัวอย่าง:

<label>Male
        <input type="radio" name="IsMale" value="True" data-bind="radiobuttonyesno:IsMale"/>
     </label> 
     <label>Female
        <input type="radio" name="IsMale" value="False" data-bind="radiobuttonyesno:IsMale"/>
     </label>

1

เมื่อคุณทราบแล้วว่าการจับคู่ครั้งแรกของปุ่มตัวเลือกต้องการจับคู่เฉพาะสตริงและต้องการกำหนดค่าเป็นสตริงมันเป็นเพียงเรื่องของการแปลงค่าเริ่มต้นของคุณเป็นสตริงฉันต้องต่อสู้กับค่า Int

หลังจากที่คุณตั้งค่าสิ่งที่สังเกตได้แล้วให้แปลงค่าเป็นสตริงและ KO จะทำเวทมนตร์จากที่นั่น หากคุณกำลังจับคู่กับแต่ละบรรทัดให้ทำการแปลงในบรรทัดเหล่านั้น

ในโค้ดตัวอย่างฉันใช้ Json เพื่อแมป Model ทั้งหมดในคำสั่งเดียว จากนั้นให้ Razor แทรกค่าระหว่างเครื่องหมายคำพูดสำหรับการแปลง

script type="text/javascript">
    KoSetup.ViewModel = ko.mapping.fromJS(@Html.Raw(Json.Encode(Model)));
    KoSetup.ViewModel.ManifestEntered("@Model.ManifestEntered");       //Bool
    KoSetup.ViewModel.OrderStatusID("@Model.OrderStatusID");           //Int
</script>

ฉันใช้ "ถ่ายโอนข้อมูลทั้งหมดไปที่หน้าจอ" ที่ด้านล่างของหน้าเว็บระหว่างการพัฒนา

<h4>Debug</h4>
<pre data-bind="text: ko.toJSON($data, null, 2)"></pre>

นี่คือค่าข้อมูล ก่อน

"OrderStatusID": 6,
"ManifestEntered": true,

และ, หลังจากนั้น

"OrderStatusID": "6",
"ManifestEntered": "True",

ในโครงการของฉันฉันไม่จำเป็นต้องแปลง Bools เพราะฉันสามารถใช้ช่องทำเครื่องหมายที่ไม่มีปัญหาเหมือนกัน


0

ทำไมไม่เป็นจริงและเท็จแทนที่จะเป็น 1 และ 0 ล่ะ?

 <label>Male
    <input type="radio" name="IsMale" value="true" data-bind="checked:IsMale"/>
 </label> 
 <label>Female
    <input type="radio" name="IsMale" value="false" data-bind="checked:IsMale"/>
 </label>

2
หากคุณส่งวัตถุ json มันสร้างความแตกต่าง
แพทย์

0

คุณยังสามารถใช้ตัวขยายเพื่อให้ง่ายต่อการนำมาใช้ซ้ำเพื่อสังเกตการณ์เพิ่มเติม:

ko.extenders.boolForEditing = function (target, allowNull) {
    var result = ko.computed({
        read: function () {
            var current = target();
            var newValue = null;
            if (current === undefined || current === null || current === '') {
                if (!allowNull) {
                    newValue = 'false';
                }
            } else {
                newValue = current ? 'true' : 'false';
            }
            return newValue;
        },
        write: function (newValue) {
            var current = target();
            var valueToWrite = null;
            if (newValue === undefined || newValue === null || newValue === '') {
                if (!allowNull) {
                    valueToWrite = false;
                }
            } else {
                valueToWrite = newValue === 'true';
            }
            // only write if it changed
            if (valueToWrite !== current) {
                target(valueToWrite);
            } else {
                if (newValue !== current) {
                    target.notifySubscribers(valueToWrite);
                }
            }
        }
    }).extend({
        notify: 'always'
    });

    result(target());

    return result;
};

จากนั้นใช้ดังนี้:

this.IsMale.forEditing = this.IsMale.extend({boolForEditing: true});

พารามิเตอร์ที่ระบุเพื่อboolForEditingระบุว่าค่าอาจเป็นโมฆะหรือไม่

ดูhttp://jsfiddle.net/G8qs9/1/


0

หลังจากทำการวิจัยจำนวนมากสำหรับสิ่งที่น่าพิศวงรุ่นเก่าก่อน 3.0 อาจมีสองตัวเลือกที่ดีที่สุด

สร้างส่วนขยายที่น่าพิศวงเช่น

ko.extenders["booleanValue"] = function (target) {
    target.formattedValue = ko.computed({
        read: function () {
            if (target() === true) return "True";
            else if (target() === false) return "False";
        },
        write: function (newValue) {
            if (newValue) {
                if (newValue === "False") target(false);
                else if (newValue === "True") target(true);
            }
        }
    });

    target.formattedValue(target());
    return target;
};

ในการใช้ Extender กับโมเดลของคุณคุณต้องทำสิ่งต่อไปนี้:

function Order() {
  this.wantsFries= ko.observable(false).extend({ booleanValue: null });
}

<span>Do you want fries with that?</span>
<label>
  <input type="radio" name="question" value="True"
             data-bind="value: wantsFries.formattedValue" /> Yes
</label>
<label>
  <input type="radio" name="question" value="False"
             data-bind="value: wantsFries.formattedValue" /> No
</label>

แหล่งที่มา: http://www.timlabonne.com/2013/02/building-a-knockout-js-extender-for-boolean-values/

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