Прив’язка 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.