Knockout.Js doesn't detect programmatically ch

2019-04-21 08:26发布

I have a simple checkbox which has its value adjusted by a bit of infrastructure that I'm writing to persist state in localStorage. I am aware that this would normally be done by setting the viewModel but the infrastructure is unaware of any knockout bindings and has no access to them.

I didn't think this would be a problem as I assumed that knockout was running off the 'change' event, however $checkbox.prop('checked', true).trigger('checked') fails to trigger my observable. Even using the click event directly doesn't get me quite what I need.

What events does knockout wire to? How do I get it to read the state of the control?

jsbin showing this weird behavior here. Try selecting the checkbox directly versus clicking the button.

7条回答
我想做一个坏孩纸
2楼-- · 2019-04-21 08:39

you should change checkbox' checked status via your view model:

 var m = {
   isChecked: ko.observable(false)
  //,infrastructureUpdatesCheckbox: function() {
    //$chk = $(':checkbox');
    //$chk
    //  .prop('checked', !$chk.prop('checked'))
    //  .trigger('change');
    //this.isChecked(!this.isChecked());  // this - is your view model
 }
};

here is updated version:

function InfrastructureUpdatesCheckbox(){

  var $cb = $(':checkbox');
  var cb = $cb[0];
  cb.checked = !cb.checked;  // manually change checked status
  $cb.triggerHandler('click');  // run all event handlers bound to 'click' event
}

I guess the issue is related to checkboxes (radio buttons) only. Other controls should behaive correctly when you trigger events manually.

查看更多
虎瘦雄心在
3楼-- · 2019-04-21 08:42

In my case this turned out to be related to a jquery bug. I took to the github knockout issue tracker to track this down; see here.

Ultimately I ended up doing this in my component:

var isjQueryOld = /^(0|1)\.[0-8]\./.test($.fn.jquery); // <=1.8.*
var toggleCheckbox = isjQueryOld ? jQueryOldToggleCheckbox : function($el) { $el.trigger('click') } //https://github.com/knockout/knockout/issues/987


function jQueryOldToggleCheckbox($el) {
    //This should be simple right? See http://stackoverflow.com/a/8595601/5056
    //and "simulating events to adjust knockout models" jasmine spec
    //all this is nessessary to get everything working with knockout
    var changeTo = !$el.prop('checked');
    if(changeTo)
        $el.attr('checked', true);
    else
        $el.removeAttr('checked');
    $el.trigger('click');
    $el.prop('checked', changeTo);
}

and in my code

        toggleCheckbox($el);
查看更多
霸刀☆藐视天下
4楼-- · 2019-04-21 08:43

I don't know what event knockout is listening for, you could probably wade into the source itself and figure it out, but one solution would be to use a custom binding. In the init for the custom binding you can set whatever handlers you want for whatever events you want to capture (such as changed) and use that to force you binding to run update.

查看更多
Luminary・发光体
5楼-- · 2019-04-21 08:47

You've probably already found a solution to your problem but this might help someone else.

I have had the exact same problem. What I did was use a pub-sub pattern and have my component trigger its own change event. What you could then do is to have the instance of the infrastructure component that needs to update the model subscribe to that event and then update your model. Better still your model can subscribe to the event, that way your model knows about the infrastructure component rather than the other way around

That way your infrastructure component does not need to know about ko and you can re-use it anywhere else. I am sure it's only a matter of time before you find other uses for it if you plan to re-use that component.

Hope this helps (sorry there is no code as I can't post clients code on SO, but if anyone wants an example just say so and I'll do a jsfiddle to illustrate.

查看更多
来,给爷笑一个
6楼-- · 2019-04-21 08:49

I've spend hours today falling into on-click loop with checked & click approach, trying ko.observable, pureComputed write or even infrastructureUpdatesCheckbox without success too long to explain to anybody who pay for my work why I'm not able to check the radio button in the damn Knockout. Unfortunately my context (Magento 2 checkout with purchased plugins) didn't allow me to implement valid view model so finally I've managed to solve the issue with simple hack to add second input type="radio" and show it on click:

selectRadioPayment: function (obj, event, method) {
        this.selectPaymentMethod(obj, event, method);
        $('.ko-payment-selector').show();
        $('#p_method_' + method).hide();
        $('#p_method_' + method).closest('.radio-payment-selector').show();
    },

    renderedPaymentMethods: function() {
        if(quote.paymentMethod) {
            var selectedMethod = quote.paymentMethod().method;
            $('#p_method_' + selectedMethod).hide();
            $('#p_method_' + selectedMethod).closest('.radio-payment-selector').show();
        }
    },

    <input class="radio-payment-selector" type="radio" class="hidden" checked />
    <input class="ko-payment-selector" type="radio" name="payment[method]" class="radio" event: {change: function(data, event){$parent.selectRadioPayment(data, event, $data.method)}}"/>

I hope it would help someone someday.

查看更多
祖国的老花朵
7楼-- · 2019-04-21 08:57

Knockout bindings don't really work in the way you are thinking.

Check out: http://knockoutjs.com/documentation/custom-bindings.html

Here is one solution you can use, no jQuery, no custom binding.

Your html will stay the same.

var m = {
  isChecked: ko.observable(false),
  infrastructureUpdatesCheckbox: function() {
    this.isChecked( this.isChecked() === true ? false : true);
  }
};

ko.applyBindings(m);
查看更多
登录 后发表回答