Change the event to select with a knockout binding, how can I find out if this is a real change

I am creating a user interface with permissions, I have a list of permissions with a selected list next to each permission. Permissions are represented by an observable array of objects tied to a selection list:

<div data-bind="foreach: permissions"> <div class="permission_row"> <span data-bind="text: name"></span> <select data-bind="value: level, event:{ change: $parent.permissionChanged}"> <option value="0"></option> <option value="1">R</option> <option value="2">RW</option> </select> </div> </div> 

Now the problem is this: the change event occurs when the user interface just populates for the first time. I call my ajax function, get a list of permissions, and then the event is incremented for each permission element. This is really not the behavior I want. I want it to be raised only when the user really selects a new value for permission in the select list , how can I do this?

+80
Jun 18
source share
9 answers

In fact, you want to know if the event is fired by the user or the program , and it is obvious that the event will be triggered during initialization.

The knockout approach of adding a subscription will not help in all cases why, because in most models it will be implemented as

  • run the model with undefined data, just structure (actual KO initilization)
  • update the model with the source data (logical init like load JSON , get data etc)
  • User interaction and updates

The actual step that we want to capture is the change in 3, but in the second step the subscription will get the call. Thus, the best way is to add events to the change, for example

  <select data-bind="value: level, event:{ change: $parent.permissionChanged}"> 

and found an event in the permissionChanged function

 this.permissionChanged = function (obj, event) { if (event.originalEvent) { //user changed } else { // program changed } } 
+88
Dec 05 '13 at
source share

This is just an assumption, but I think this is happening because level is a number. In this case, binding the value will raise the change event to update the level string value. So you can fix this by making sure level is the line to start with.

In addition, the more β€œknockout” it does, it’s not to use event handlers, but to use observables and subscriptions. Make level observable, and then add a subscription to it that will run whenever level changes.

+32
Jun 20 2018-12-12T00:
source share

Here is a solution that can help with this strange behavior. I could not find a better solution than placing a button to manually trigger the change event.

EDIT: Maybe user binding like this can help:

 ko.bindingHandlers.changeSelectValue = { init: function(element,valueAccessor){ $(element).change(function(){ var value = $(element).val(); if($(element).is(":focus")){ //Do whatever you want with the new value } }); } }; 

And in the selected data binding attribute add:

 changeSelectValue: yourSelectValue 
+4
Jun 18 2018-12-12T00:
source share

I use this custom binding (based on this fiddle from RP Niemeyer, see his answer to this ), which ensures that the numeric value is correctly converted from string to number (as suggested by Michael Best's solution):

JavaScript:

 ko.bindingHandlers.valueAsNumber = { init: function (element, valueAccessor, allBindingsAccessor) { var observable = valueAccessor(), interceptor = ko.computed({ read: function () { var val = ko.utils.unwrapObservable(observable); return (observable() ? observable().toString() : observable()); }, write: function (newValue) { observable(newValue ? parseInt(newValue, 10) : newValue); }, owner: this }); ko.applyBindingsToNode(element, { value: interceptor }); } }; 

HTML example:

 <select data-bind="valueAsNumber: level, event:{ change: $parent.permissionChanged }"> <option value="0"></option> <option value="1">R</option> <option value="2">RW</option> </select> 
+3
Jun 13 '13 at 10:12
source share

If you use an observable instead of a primitive value, the selection will not trigger change events on initial binding. You can continue to be attached to the change event, and not subscribe directly to the observed.

+2
Jan 25 '13 at 17:51
source share

Quick and dirty using a simple flag:

 var bindingsApplied = false; var ViewModel = function() { // ... this.permissionChanged = function() { // ignore, if flag not set if (!flag) return; // ... }; }; ko.applyBindings(new ViewModel()); bindingsApplied = true; // done with the initial population, set flag to true 

If this does not work, try wrapping the last line in setTimeout () - the events will be asynchronous, so perhaps the last has not yet completed when applyBindings () is already returned.

+1
Jun 18 '12 at 7:11
source share

I had a similar problem, and I just modified the event handler to check the type of the variable. The type is set only after the user selects a value, and not at the first page load.

 self.permissionChanged = function (l) { if (typeof l != 'undefined') { ... } } 

It seems to work for me.

0
May 30 '14 at 15:41
source share

use this one:

 this.permissionChanged = function (obj, event) { if (event.type != "load") { } } 
0
May 17 '19 at 12:36
source share

If you are working with Knockout, use key knockout functionality for observed functions.
Use the ko.computed() method and execute and call the ajax call inside this function.

-one
Apr 26 '17 at 13:35 on
source share



All Articles