Привязка true/false к переключателям в Knockout JS
В моей модели модели у меня есть значение IsMale, которое имеет значение true или false.
В моем пользовательском интерфейсе я хочу привязать его к следующим переключателям:
<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-х направлениях с этим пользовательским интерфейсом и моделью?
Ответы
Ответ 1
Один из вариантов заключается в использовании записываемого вычисленного наблюдаемого.
В этом случае я считаю, что хороший вариант - сделать записываемый вычисленный наблюдаемый "суб-наблюдаемым" вашего наблюдаемого 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
});
};
Вы бы связали его в своем интерфейсе, например:
<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/
Ответ 2
Я знаю, что это старый поток, но у меня была такая же проблема, и я обнаружил гораздо лучшее решение, которое, вероятно, было добавлено к нокауту после того, как этот вопрос был официально дан, поэтому я просто оставлю его для людей с одинаковыми проблема.
В настоящее время нет необходимости в расширителях, настраиваемых обработчиках привязки или вычислений.
Просто укажите параметр "checkedValue", он будет использовать это вместо атрибута html "value", и с этим вы можете передать любое значение javascript.
<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"/>
Ответ 3
Это работает для меня:
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>
Ответ 4
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);
}
}
};
Используйте это связующее вместо создания глупых ko вычисленных наблюдаемых.
Пример:
<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>
Ответ 5
Как только вы выясните, что начальное совпадение для переключателя хочет совместить только строку и хочет установить значение в строку, , это просто вопрос преобразования начального value to string. Мне пришлось бороться с этим значением Int.
После того, как вы настроите свои наблюдаемые объекты, преобразуйте значение в строку, и KO будет делать свою магию оттуда. Если вы выполняете сопоставление с отдельными строками, выполните преобразование в этих строках.
В примере кода я использую Json для сопоставления всей модели в одной команде.
Затем давайте 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, потому что я могу использовать флажок, который не имеет такого же разочарования.
Ответ 6
Почему не просто true и false вместо 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>
Ответ 7
Вы также можете использовать extender, чтобы его можно было повторно использовать для большего количества наблюдаемых:
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/
Ответ 8
После много исследований для более старой версии нокаута до версии 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;
};
Чтобы использовать расширитель в своей модели, вы делаете что-то вроде следующего:
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/