James
James

Reputation: 1945

subscribe event getting called while setting value

I have select list in my html binded to observable array

 <select data-bind="options: myData, optionsText: 'Name', value: selectedId"></select>

When i load my page i set value to observable selectedId

But when i set value it immediately calls its subscribe event

  selectedId.subscribe(function (row) {
      // some logic for retrieving data
  });

I dont want subscribe event to call when value is being set programatically but only want to call when user selects something from list.

Is it possible to do? Cant find any good examples.

Update1 i set value to my observable selectedId in this way

selectedId(ko.utils.arrayFirst(data(), function(item) {
                    return item.id=== 5;
}));

I think because of this its firing subscribe event

Upvotes: 0

Views: 411

Answers (1)

Matt Burland
Matt Burland

Reputation: 45135

If you set the initial value before you actually subscribe, then you shouldn't get a subscription event firing. For example:

function ViewModel() {
  var self = this;

  self.selectedID = ko.observable();
  self.selectedID(10); // this won't fire the event because we haven't subscribed yet

  self.selectedID.subscribe(function(value) {
    alert("value changed by user");
  });
}

ko.applyBindings(new ViewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<input type="number" data-bind="value: selectedID" />

Now if you want to solve the general case, where the value might change programmatically later and you don't want the subscribe to fire then, you might want to have a flag that you can use to simply skip the processing of the subscribe handler. Something like:

function ViewModel() {
  var self = this;

  self.selectedID = ko.observable();
  self.selectedID(10); // this won't fire the event because we haven't subscribed yet

  var _skipNotification = false; // this tells us whether or not we should process subscribe

  // putting this logic in a function makes it easier if you have multiple places 
  // where you need to programmatically set the id.
  function setSelectedID(value) {
    _skipNotification = true;
    self.selectedID(value);
    _skipNotification = false;
  }

  self.selectedID.subscribe(function(value) {
    if (!_skipNotification) {
      alert("value changed by user");
    }
  });

  self.changeProgrammatically = function() {
    setSelectedID(1);
  };
}

ko.applyBindings(new ViewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<input type="number" data-bind="value: selectedID" />
<input type="button" data-bind="click: changeProgrammatically" value="change programmatically" />

Here's your fiddle fixed to use the technique above. A local variable _skipNotification is defined in the view model and checked in the subscribe event. Then the importData function that is called from the mouse click looks like this:

 this.importData = function () {
     skipNotification = true;
     self.selectedGroup(ko.utils.arrayFirst(self.availableGroups(), function (val) {
         //debugger;
         return val.GroupId == 8;
     }));
     skipNotification = false;

     console.log("Lets see if it hits me first");
 };

This will set the selectedGroup without causing the body of the selectedGroup.subscribe to execute. Note that as a result the selectedGroupId doesn't get set so your one span will still say You have chosen Nothing despite the fact that something is selected in the drop-down. I'm not sure if that was what you were actually going for, but it seems pretty misleading. Especially since the only way to now get it to correctly read You have chosen Football Team is to select something else first and then reselect Football Team.

Upvotes: 1

Related Questions