Knockoutjs computed passing parameters

2019-03-08 23:48发布

I am wondering if it is possible with knockoutjs to pass arguments when binding.

I am binding a list of checkboxes and would like to bind to a single computed observable in my viewmodel. In my viewmodel (based on parameter passed to the read function) I want to return true/false based on certain conditions.

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

Any suggestions?

4条回答
萌系小妹纸
2楼-- · 2019-03-09 00:02

Create a function whose sole purpose is to return a computed observable. It may take in parameters as you wanted. It will have to be a separate computed observable if you want it to be a two-way binding.

Then in your binding, call that function with the appropriate arguments. The computed observable it returns will be bound to in your view and will update as usual.

Here's a fiddle where I used this technique for creating event handlers. You can do something similar here.

You can keep it clean by making the function a method on the observable. Either by adding to the ko.observable.fn prototype or adding it directly to the observable instance.

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

Then apply to your view

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

Demo


However if you are just trying to bind all those checkboxes to a single value in your view model, you don't need to do that. Use the checked binding on an array in your view model and give your checkboxes a value. Every checked value will be added to the array. And it will be a two way binding.

<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([])
};

Demo

查看更多
劳资没心,怎么记你
3楼-- · 2019-03-09 00:12

Without knowing the specifics, it seems like what you should be doing is defining a ko.observableArray or computed array value

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

and not passing values from the markup to define the model state

查看更多
地球回转人心会变
4楼-- · 2019-03-09 00:14

The accepted answer is decent, but if you have a function that generates a ko.computed for each checkbox, you are adding unnecessary overhead with multiple anonymous computed observables, which adds up quickly when your checkbox lists exceed 4-5 options.

This is a simpler implementation of a bitwise scenario, but the computed function can be whatever need be.

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

Example fiddle here: http://jsfiddle.net/i_vargas3/RYQgg/

查看更多
forever°为你锁心
5楼-- · 2019-03-09 00:22

There is no reason to use a computed value. Simply define a function in your View Model and bind the checked to it.

Below is a very simplistic example.

--

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

​--

That being said it is a good idea to try and push as much of the logic into your View Model as is possible. It would be advisable to create a View Model that models your checkbox as an object, and then the logic as to if the checkbox should be checked could be encapsulated inside.

--

EDIT: Based on the requirement to do two-way binding I have written an extender to manage an observable.

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());​
查看更多
登录 后发表回答