Прив’язка 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передбачає рядок "true" / "false". Отже, моє питання полягає в тому, як я можу отримати це двостороннє прив’язування з цим інтерфейсом та моделлю?


10
Для версій нокауту> = 3.0 див . Відповідь Натана для більш простого змісту, ніж пропонується прийнятою відповіддю.
— 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        
    });          
};

Ви б прив'язали його у своєму інтерфейсі, як:

<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. Подивіться, чи це допомагає.
— Натан

1
так, дякую, я перейшов на 3.0.0 і тепер він там. Ще потрібно обернути мої булеві значення у рядок, оскільки їх очікував код сервера ;-)
— ZiglioUK

Хороша інформація. Два додаткові моменти: (1) Я виявив, що за допомогою нокауту до версії 3.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 і виявляю, що мені все-таки потрібно ввести 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>

Єдина проблема, яку я бачу, полягає в тому, що при серіалізації viewmodel для передачі на сервер ви отримаєте цілі числа в спостережуваному (замість булевих). Вам потрібно буде зателефонувати vm.IsMale(!!vm.+IsMale());перед серіалізацією json для надсилання на сервер (на випадок, якщо сторона сервера не може впоратися з цим належним чином)
— Артем

Я думаю, ви маєте рацію, але моїх знань щодо Javascript бракує. Ви можете пояснити мені, як !! + працює? Я не знайомий із цим синтаксисом.
— CJ

1
@CJ це перетворює рядок або число в логічне значення - перевірте це jsfiddle.net/nickolsky/6ydLZ , якщо рядок уже є bool, він збереже його як bool
— Артем

Дуже дякую. Я думаю, це теж прекрасне рішення.
— 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, щоб зіставити всю модель однією командою. Потім дозволяючи 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

Чому б не просто 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>

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

Щоб використовувати розширювач на вашій моделі, ви зробите щось на зразок наступного:

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/

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.