Liên kết true / false với các nút radio trong Knockout JS


84

Trong mô hình xem của tôi, tôi có một giá trị IsMale có giá trị đúng hoặc sai.

Trong giao diện người dùng của mình, tôi muốn liên kết nó với các nút radio sau:

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

Vấn đề tôi nghĩ là checkedmong đợi một chuỗi "true" / "false". Vì vậy, câu hỏi của tôi là, làm cách nào để có được ràng buộc 2 chiều với giao diện người dùng và mô hình này?


10
Đối với các phiên bản Knockout> = 3.0, hãy xem câu trả lời của Natan để có sự thận trọng hơn so với câu trả lời được chấp nhận đề xuất.
Markus Pscheidt

Câu trả lời:


81

Một tùy chọn là sử dụng có thể quan sát được tính toán có thể ghi .

Trong trường hợp này, tôi nghĩ rằng một lựa chọn tốt là làm cho cái có thể quan sát được bằng máy tính có thể ghi được trở thành "cái có thể quan sát được" của bạn IsMalecó thể quan sát được. Mô hình chế độ xem của bạn sẽ giống như sau:

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

Bạn sẽ liên kết nó trong giao diện người dùng của mình như:

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

Mẫu: http://jsfiddle.net/rniemeyer/Pjdse/


Có vẻ như một giải pháp tuyệt vời. Tôi sử dụng plugin ánh xạ để làm mới máy ảo của mình. Bạn có biết nó sẽ xóa sổ ForE Chỉnh sửa trên IsMale không?
CJ

26
Đây là một sự thay thế mà đẩy việc tạo ra quan sát được tính vào một ràng buộc tùy chỉnh, trong đó sẽ làm cho nó, do đó bạn không cần phải lo lắng về đối phó với nó trong các plugin mapping: jsfiddle.net/rniemeyer/utsvJ
RP Niemeyer

2
1 để sử dụng một ràng buộc thay vì tạo ra một quan sát được trên tất cả các đối tượng
Greg Ennis

1
@RPNiemeyer chắc chắn là con đường để đi.
Andrew

1
@RPNiemeyer cảm ơn! điều đó trong các bình luận là một trong những hiệu quả cho tôi.
SFlagg

128

Tôi biết đây là một chủ đề cũ, nhưng tôi đã gặp vấn đề tương tự và đã tìm ra một giải pháp tốt hơn nhiều có thể đã được thêm vào loại trực tiếp sau khi câu hỏi này được trả lời chính thức, vì vậy tôi sẽ chỉ để lại cho những người có cùng vấn đề.

Hiện tại không cần bộ mở rộng, trình xử lý ràng buộc tùy chỉnh hoặc máy tính. Chỉ cần cung cấp tùy chọn "ckedValue ", nó sẽ sử dụng tùy chọn đó thay vì thuộc tính html 'value' và bạn có thể chuyển bất kỳ giá trị javascript nào.

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

Hoặc là:

<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
Nhìn vào nguồn, nó dường như đưa ra quyết định sử dụng giá trị đã kiểm tra ở đây . 'UseCheckedValue' được đặt thành true nếu đầu vào là radio hoặc hộp kiểm có giá trị là một mảng . Ngoài ra, tôi đang sử dụng Knockout 3.0. Xem nếu điều đó có ích.
Natan

1
vâng, cảm ơn, tôi đã nâng cấp lên 3.0.0 và bây giờ nó ở đó. Vẫn cần phải quấn các giá trị boolean của tôi trong một String vì mã máy chủ được mong đợi những ;-)
ZiglioUK

Thông tin tốt. Hai điểm bổ sung: (1) Tôi thấy rằng với Knockout trước v3.0, tôi có thể sử dụng valueliên kết và sau đó là checkedliên kết (theo thứ tự đó), nhưng với 3.0 tôi phải sử dụng checkedValueliên kết thay vì valueliên kết. (2) trước v3.0 kén chọn yêu cầu valueràng buộc trước checkedràng buộc để hoạt động chính xác, vì vậy tôi có cảm giác rằng mọi thứ cũng có thể hoạt động tốt hơn trong v3.0 trong tất cả các tình huống nếu bạn đặt checkedValueràng buộc trước checkedliên kết, như chúng hiển thị trong tài liệu .
Jason Frank

@ZiglioNZ một cách này có thể thất bại là nếu bạn checkedsetter (hoặc có lẽ một cái gì đó phụ thuộc vào nó) đặt ra một lỗi - sau đó vào hộp kiểm đúng không được chọn
Simon_Weaver

Tôi đang sử dụng phiên bản 3.4 và nhận thấy rằng tôi vẫn phải đặt giá trị = "true" và ở trên để nó hoạt động.
wirble

11

Điều này phù hợp với tôi:

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>

vấn đề duy nhất tôi thấy là khi tuần tự hóa chế độ xem để chuyển tới máy chủ, bạn sẽ nhận được các số nguyên có thể quan sát được (thay vì boolean). Bạn sẽ cần phải gọi vm.IsMale(!!vm.+IsMale());trước serializing json để gửi đến máy chủ (trong trường hợp máy chủ bên không thể xử lý nó đúng)
Artem

Tôi nghĩ bạn đúng, nhưng kiến ​​thức Javascript của tôi còn thiếu. Bạn có thể vui lòng giải thích cho tôi cách !! + hoạt động không? Tôi không quen với cú pháp đó.
CJ

1
@CJ điều này chuyển đổi chuỗi hoặc số thành boolean - chek this jsfiddle.net/nickolsky/6ydLZ , nếu chuỗi đã là bool, nó sẽ giữ nó dưới dạng bool
Artem

Cảm ơn rất nhiều. Tôi nghĩ đây cũng là một giải pháp tuyệt vời.
CJ

1
Tuy nhiên, nó không hoạt động theo cả hai cách. Các nút radio không được chọn khi tải.
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);
        }
    }
};

Sử dụng chất kết dính này thay vì tạo các vật quan sát được tính toán không ngu ngốc.

Thí dụ:

<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

Khi bạn phát hiện ra rằng kết quả so khớp ban đầu cho nút radio chỉ muốn so khớp với một chuỗi và muốn đặt giá trị thành một chuỗi, thì việc chuyển đổi giá trị ban đầu của bạn thành chuỗi. Tôi đã phải đấu tranh với điều này bằng các giá trị Int.

Sau khi bạn đã thiết lập các vật có thể quan sát của mình, hãy chuyển đổi giá trị thành chuỗi và KO sẽ thực hiện phép thuật của nó từ đó. Nếu bạn đang ánh xạ với các dòng riêng lẻ, hãy thực hiện chuyển đổi trong các dòng đó.

Trong đoạn mã ví dụ, tôi đang sử dụng Json để ánh xạ toàn bộ Mô hình trong một lệnh duy nhất. Sau đó, để Razor chèn giá trị vào giữa các dấu ngoặc kép để chuyển đổi.

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>

Tôi sử dụng "Đưa tất cả ra màn hình" ở cuối trang web của mình trong quá trình phát triển.

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

Đây là các giá trị dữ liệu, Trước khi

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

và, Sau khi

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

Trong dự án của mình, tôi không cần phải chuyển đổi Bools, vì tôi có thể sử dụng hộp kiểm không có cùng sự thất vọng.


0

Tại sao không chỉ đơn giản là true và false thay vì 1 và 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
nếu bạn đang gửi các đối tượng json, nó sẽ tạo ra sự khác biệt.
Bác sĩ

0

Bạn cũng có thể sử dụng bộ mở rộng để có thể dễ dàng sử dụng lại chúng để dễ quan sát hơn:

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

Sau đó, sử dụng nó như thế này:

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

Tham số được cung cấp để boolForEditingcho biết liệu giá trị có thể là rỗng hay không.

Xem http://jsfiddle.net/G8qs9/1/


0

Sau khi thực hiện nhiều nghiên cứu cho phiên bản knockout cũ hơn trước 3.0, có thể có hai lựa chọn tốt nhất

Tạo một bộ mở rộng loại trực tiếp như

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

Để sử dụng bộ mở rộng trên mô hình của bạn, bạn sẽ làm như sau:

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>

nguồn: http://www.timlabonne.com/2013/02/building-a-knockout-js-extender-for-boolean-values/

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.