W moim modelu widoku mam wartość IsMale, która ma wartość true lub false.
W moim interfejsie użytkownika chcę powiązać go z następującymi przyciskami radiowymi:
<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>
Myślę, że problem polega na tym, że checked
oczekuje łańcucha „prawda” / „fałsz”. Więc moje pytanie brzmi: jak mogę uzyskać to dwukierunkowe wiązanie w tym interfejsie użytkownika i modelu?
Odpowiedzi:
Jedną z opcji jest użycie obserwowalnych obliczanych z możliwością zapisu .
W tym przypadku myślę, że fajną opcją jest uczynienie tego, co zapisywalne, wyliczane, obserwowalne, jako „subobserwowalne” twojego
IsMale
obserwowalnego. Twój model widoku wyglądałby następująco: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 }); };
Powiążesz to w swoim interfejsie użytkownika w następujący sposób:
<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>
Przykład: http://jsfiddle.net/rniemeyer/Pjdse/
źródło
Wiem, że to stary wątek, ale miałem ten sam problem i znalazłem znacznie lepsze rozwiązanie, które prawdopodobnie zostało dodane do nokautu po oficjalnej odpowiedzi na to pytanie, więc zostawię to dla osób z tym samym problemem.
Obecnie nie ma potrzeby stosowania rozszerzeń, niestandardowych programów obsługi powiązań ani obliczeń. Wystarczy podać opcję „CheckValue ”, która użyje jej zamiast atrybutu„ value ”html, dzięki czemu można przekazać dowolną wartość javascript.
<input type="radio" name="a" data-bind="checked:IsChecked, checkedValue: true"/> <input type="radio" name="a" data-bind="checked:IsChecked, checkedValue: false"/>
Lub:
<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"/>
źródło
value
wiązania, a następniechecked
wiązania (w tej kolejności), ale w wersji 3.0 musiałem użyćcheckedValue
wiązania zamiastvalue
wiązania. (2) wersja przed wersją 3.0 była wybredna, jeśli chodzi o wymaganie, abyvalue
wiązanie poprzedzałochecked
powiązanie, aby działało poprawnie, więc mam wrażenie, że wszystko może działać lepiej w wersji 3.0 we wszystkich scenariuszach, jeśli umieściszcheckedValue
wiązanie przedchecked
wiązaniem, na przykład pokazują w dokumentach .checked
ustawiacz (lub może coś od niego zależnego)To działa dla mnie:
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>
źródło
vm.IsMale(!!vm.+IsMale());
przed serializacją json, aby wysłać go na serwer (na wypadek, gdyby strona serwera nie mogła tego poprawnie obsłużyć)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); } } };
Użyj tego segregatora zamiast tworzyć głupie obserwable obliczone ko.
Przykład:
<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>
źródło
Gdy już zorientujesz się, że początkowe dopasowanie przycisku radiowego chce dopasować tylko ciąg i chce ustawić wartość na ciąg, wystarczy przekonwertować wartość początkową na łańcuch. Musiałem z tym walczyć wartościami Int.
Po skonfigurowaniu swoich obserwabli, zamień wartość na łańcuch, a KO wykona swoją magię z tego miejsca. Jeśli mapujesz z pojedynczymi liniami, wykonaj konwersję w tych liniach.
W przykładowym kodzie używam Json do mapowania całego modelu w jednym poleceniu. Następnie pozwalając Razorowi wstawić wartość między cudzysłowami do konwersji.
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>
Podczas programowania używam opcji „Zrzuć to wszystko na ekran” na dole mojej strony internetowej.
<h4>Debug</h4> <pre data-bind="text: ko.toJSON($data, null, 2)"></pre>
Oto wartości danych, przed
"OrderStatusID": 6, "ManifestEntered": true,
i po
"OrderStatusID": "6", "ManifestEntered": "True",
W moim projekcie nie musiałem konwertować Bools, ponieważ mogę użyć pola wyboru, które nie powoduje takiej samej frustracji.
źródło
Dlaczego nie po prostu prawda i fałsz zamiast 1 i 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>
źródło
Możesz także użyć przedłużacza, aby łatwo było ich ponownie użyć w celu uzyskania większej liczby obserwacji:
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; };
Następnie użyj tego w ten sposób:
this.IsMale.forEditing = this.IsMale.extend({boolForEditing: true});
Parametr podany w celu
boolForEditing
wskazania, czy wartość może być null.Zobacz http://jsfiddle.net/G8qs9/1/
źródło
Po przeprowadzeniu wielu badań dla starszej wersji knockout przed 3.0 są prawdopodobnie dwie najlepsze opcje
Utwórz przedłużacz do wybijania, taki jak
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; };
Aby użyć przedłużacza w swoim modelu, wykonaj coś takiego:
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>
źródło: http://www.timlabonne.com/2013/02/building-a-knockout-js-extender-for-boolean-values/
źródło