Подтвердить что ты не робот

Вычисляемые параметры передачи Knockoutjs

Мне интересно, можно ли с knockoutjs передавать аргументы при привязке.

Я связываю список флажков и хочу привязать к одному вычисленному наблюдаемому в моей модели viewmodel. В моей модели просмотра (на основе параметра, переданного функции чтения) я хочу вернуть true/false на основе определенных условий.

var myViewModel=function(){
    this.myprop=ko.computed({read: function(){
    //would like to receive an argument here to do my logic and return based on argument.
}
});
};

<input type="checkbox" data-bind="checked: myprop(someval1)" />
<input type="checkbox" data-bind="checked: myprop(someval2)" />
<input type="checkbox" data-bind="checked: myprop(someval3)" />

Любые предложения?

4b9b3361

Ответ 1

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

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

Здесь скрипка, где я использовал этот метод для создания обработчиков событий. Вы можете сделать что-то подобное здесь.

Вы можете сохранить его в чистоте, сделав функцию методом наблюдаемого. Либо добавив к прототипу ko.observable.fn, либо добавив его непосредственно в наблюдаемый экземпляр.

ko.observable.fn.bit = function (bit) {
    return ko.computed({
        read: function () {
            return !!(this() & bit);
        },
        write: function (checked) {
            if (checked)
                this(this() | bit);
            else
                this(this() & ~bit);
        }
    }, this);
};
// or
function ViewModel() {
    this.flags = ko.observable(0);

    this.flags.bit = function (bit) {
        return ko.computed({
            read: function () {
                return !!(this() & bit);
            },
            write: function (checked) {
                if (checked)
                    this(this() | bit);
                else
                    this(this() & ~bit);
            }
        }, this);
    }.bind(this.flags);
}    

Затем примените к виду

<input type="checkbox" data-bind="checked: flags.bit(0x1)"/>
<input type="checkbox" data-bind="checked: flags.bit(0x2)"/>
<input type="checkbox" data-bind="checked: flags.bit(0x4)"/>
<input type="checkbox" data-bind="checked: flags.bit(0x8)"/>

Демо


Однако, если вы просто пытаетесь связать все эти флажки с одним значением в своей модели просмотра, вам не нужно это делать. Используйте привязку checked в массиве в вашей модели представления и присвойте флажкам значение. Каждое проверенное значение будет добавлено в массив. И это будет двухсторонняя привязка.

<input type="checkbox" data-bind="checked: checkedValues, value: 1"/>
<input type="checkbox" data-bind="checked: checkedValues, value: 2"/>
<input type="checkbox" data-bind="checked: checkedValues, value: 3"/>
<input type="checkbox" data-bind="checked: checkedValues, value: 4"/>
var viewModel = {
    checkedValues: ko.observableArray([])
};

Демо

Ответ 2

Принятый ответ приличный, но если у вас есть функция, которая генерирует ko.computed для каждого флажка, вы добавляете ненужные накладные расходы с несколькими анонимными вычисленными наблюдаемыми, что быстро складывается, когда ваши списки флажков превышают 4-5 опций.

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

<input type="checkbox" data-bind="checked: checkedList, value: 1" />
<label>Value 1</label>
<input type="checkbox" data-bind="checked: checkedList, value: 2" />
<label>Value 2</label>
<input type="checkbox" data-bind="checked: checkedList, value: 4" />
<label>Value 4</label>
<input type="checkbox" data-bind="checked: checkedList, value: 8" />
<label>Value 8</label>

Script:

var vm = function() {
    var vm = this;

    this.checkedList = ko.observableArray();
    this.bitwiseValue = ko.computed({
        read: function () {
            return vm.checkedList().reduce(function (prev, curr) {
                return prev | curr;
            }, 0);
        },
        write: function (myVal) {
            vm.checkedList.removeAll();
            var placeValue = 1;

            while(myVal > 0) {
                if((myVal % 2) == 1) {
                    alert(placeValue);
                    vm.checkedList.push(placeValue.toString());
                }

                myVal = myVal >>> 1;                    
                placeValue = placeValue * 2;
            }
        }
    }, this);
}

ko.applyBindings(vm);

Пример скрипки здесь: http://jsfiddle.net/i_vargas3/RYQgg/

Ответ 3

Нет причин использовать значение computed. Просто определите функцию в вашей модели просмотра и привяжите к ней checked.

Ниже приведен очень упрощенный пример.

-

HTML

<input type="checkbox" data-bind="checked: isEven(1)" />
<input type="checkbox" data-bind="checked: isEven(2)" />
<input type="checkbox" data-bind="checked: isEven(3)" />​

JS

var MyViewModel=function(){
    this.isEven = function(num) {
        return (num % 2) == 0;
    };
};
ko.applyBindings(new MyViewModel());

-

Говоря, что это хорошая идея - попытаться как можно больше выдвинуть логику в вашу модель просмотра. Было бы целесообразно создать модель просмотра, которая моделирует ваш флажок как объект, а затем логику относительно того, следует ли проверять флажок, можно инкапсулировать внутри.

-

РЕДАКТИРОВАТЬ: На основании требования сделать двустороннюю привязку я написал расширитель для управления наблюдаемым.

http://jsfiddle.net/jearles/j6zLW/5/

ko.extenders.bitwise = function(target, bitCount) { 
    target.bits = [];    
    target.checked = ko.observableArray();

    // Create bit array based on requested number of bits
    for (i=bitCount-1; i>=0; i--) {
        target.bits.push(''+Math.pow(2, i));
    }        

    // Define a function to create bits
    function makeBits(newValue) {
       var num = !isNaN(newValue) ? parseInt(newValue) : 0;
       var arr = [];
       for (i=0; i<target.bits.length; i++) {
          var bitValue = parseInt(target.bits[i]);
          if ((num & bitValue) == bitValue) arr.push(target.bits[i]);
       }
       target.checked(arr);
    }

    // Define a function to combine bits
    function makeBitwise(newBits) {
       var num = 0;
       for (i=0; i<target.bits.length; i++) {
         if (newBits.indexOf(target.bits[i]) > -1) num += parseInt(target.bits[i]);
       }
       target(num);
    }

    // Create initial bits
    makeBits(target());

    // Make bits whenever the value changes
    target.subscribe(makeBits);

    // Make number whenever the bits change
    target.checked.subscribe(makeBitwise);

    // Return the original observable
    return target;
};

var MyViewModel=function(){
    var self = this;
    this.number = ko.observable(2).extend({ bitwise: 8});

};
ko.applyBindings(new MyViewModel());​

Ответ 4

Не зная специфики, кажется, что вы должны делать определение ko.observableArray или значение вычисляемого массива

HTML:

myprop: <input data-bind="value: myprop">
<div data-bind="foreach: selections">
  <label>
    <span data-bind="text: value"></span>
    <input type="checkbox" data-bind="checked: selected"/>
  </label>
</div>

JS:

  $(function() {
    function Model() {
        this.self = this
        self.myprop = ko.observable(14)
        self.bits = [1, 2, 4, 8, 16, 32, 64, 128]
        self.selections = ko.computed(function() {
            return self.bits.map(function(bit) {
                console.log(myprop() & bit)
                return {
                    value: bit,
                    selected: (myprop() & bit) == bit
                }
            })
        })
    }

    ko.applyBindings(new Model())
})

и не передавать значения из разметки для определения состояния модели