Привязка true / false к переключателям в Knockout JS

84

В моей модели представления у меня есть значение 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что ожидается строка «истина» / «ложь». Итак, мой вопрос: как я могу получить эту двустороннюю привязку с этим пользовательским интерфейсом и моделью?

CJ
источник
10
Для версий Knockout> = 3.0 см . Ответ Натана для более простого решения, чем предлагается в принятом ответе.
Маркус Пшайдт 07

Ответы:

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

Вы бы связали его в своем пользовательском интерфейсе, например:

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

Р.П. Нимейер
источник
Похоже на отличное решение. Я использую подключаемый модуль сопоставления, чтобы обновить свою виртуальную машину. Знаете ли вы, что ForEditing на IsMale уничтожит?
CJ
26
Вот альтернатива, которая помещает вычисленное наблюдаемое создание в настраиваемую привязку, чтобы вам не приходилось беспокоиться о работе с ним в плагине сопоставления: jsfiddle.net/rniemeyer/utsvJ
RP Niemeyer
2
+1 к использованию привязки вместо создания наблюдаемого для каждого объекта
Грег Эннис
1
@RPNiemeyer - определенно лучший способ.
Эндрю
1
@RPNiemeyer спасибо! эта скрипка в комментариях сработала для меня.
SFlagg
128

Я знаю, что это старый поток, но у меня была та же проблема, и я обнаружил гораздо лучшее решение, которое, вероятно, было добавлено в нокаут после того, как на этот вопрос был официально дан ответ, поэтому я просто оставлю его для людей с той же проблемой.

В настоящее время нет необходимости в расширителях, пользовательских обработчиках привязок или вычислений. Просто укажите параметр «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"/>
Натан
источник
2
Глядя на источник, это , кажется, принимает решение использовать проверенное значение здесь . Для useCheckedValue установлено значение true, если ввод - это радио или флажок со значением в виде массива . Еще я использую Knockout 3.0. Посмотрим, поможет ли это.
Natan
1
да, спасибо, я обновился до 3.0.0, и теперь она есть. Мне все еще нужно обернуть мои логические значения в строку, потому что код сервера ожидал их ;-)
ZiglioUK
Хорошая информация. Два дополнительных момента: (1) Я обнаружил, что в Knockout до версии 3.0 я мог использовать valueпривязку, а затем checkedпривязку (в этом порядке), но с 3.0 мне пришлось использовать checkedValueпривязку вместо valueпривязки. (2) до valueверсии 3.0 требовалось, чтобы привязка предшествовала checkedпривязке для правильной работы, поэтому у меня есть ощущение, что в версии 3.0 все может работать лучше во всех сценариях, если вы поместите checkedValueпривязку перед checkedпривязкой, например они показывают в документах .
Джейсон Франк
@ZiglioNZ один способ это может потерпеть неудачу, если ваш checkedсеттер (или , возможно , что - то зависит от него) вызывает ошибку - то правильный флажок не установлен
Simon_Weaver
Я использую версию 3.4 и обнаружил, что мне все еще нужно ввести value = "true" и указанное выше, чтобы заставить его работать.
wirble
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 для отправки на сервер (в случае, если серверная сторона не может обработать его должным образом)
Артем
Думаю, вы правы, но мне не хватает знаний о Javascript. Не могли бы вы объяснить мне, как работает !! +? Я не знаком с этим синтаксисом.
CJ
1
@CJ преобразует строку или число в логическое значение - проверьте этот jsfiddle.net/nickolsky/6ydLZ , если строка уже имеет значение типа bool, оно будет сохранено как значение типа bool
Артем
Спасибо большое. Думаю, это тоже отличное решение.
CJ
1
Однако это не работает в обоих направлениях. При загрузке переключатели не проверяются.
Микаэль Эстберг
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>
KurtJ
источник
1

Как только вы выясните, что начальное совпадение для переключателя хочет совпадать только со строкой и хочет установить значение в строку, остается просто преобразовать ваше начальное значение в строку. Мне пришлось бороться с этим с помощью значений 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, потому что я могу использовать флажок, который не вызывает такого разочарования.

Грег Литтл
источник
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

После тщательного изучения старой версии knockout до 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/

Аншул Нигам
источник