0
votes

I'm running into an issue with KnockoutJS where it appears the data-bindings aren't updating as expected.

In my view model, I have an array of objects with an Enabled property that I initialize to True, and have that property bound to enable on a checkbox with a click handler CheckItems:

self.Answers(data.filter(function (step) {
  if (step.Type == 0) {
    step.Enabled = true;
    return true;
  }
  else {
    return false;
  }
}));

...
<ul data-bind="foreach: $root.Answers">
  <input type="checkbox" data-bind="click: $root.CheckItems, ...,  enable: Enabled>
</ul>

Everything work fine and dandy, until the CheckItems handler runs and I try to set the Enabled property to false:

self.CheckItems = function (step) {
  ...
  self.Answers().forEach(function (option) {
    <call my handler>.done(function (data) {
        option.Enabled = data;
      }
    );
  });
  // EDIT: added valueHasMutated() call here
  self.Answers.valueHasMutated();
  return true;
}

When I hit return true, I can inspect the self.Answers() object and it shows that the answers that should be disabled have Enabled = false (correctly), but once we're the handler, none of the checkboxes are disabled, and clicking on any other checkbox present and going through the handler shows that the Enabled property seems to have been reset to True. I triple checked and made sure there's nothing else touching the Enabled property anywhere in the code between checkbox clicks.

The binding itself seems to be working, too, since when I switch the initial Enable set to false, all of the checkboxes are disabled.

I'm making changes to the self.Answers array various other places in the script as well, and those go through fine, when a change is made it goes into knockout-latest.debug.js and lands in the notifySubscribers function with the appropriate updates to make. Maybe there's something special about the click event for input fields in knockout?

Any ideas? Still fairly new to Knockout overall, but I thought this was the entire point, that I could update a property in the Answers() observable array and it would update in the corresponding UI.

Edit: based on comments I tried calling valueHasMutated() after the edits were made, but it still isn't updating properly.

Edit2: tried making a copy of the self.Answers array, doing the handler calls to update Enabled, then setting with self.Answers(newAnswers), and this had the same result.

1
yourObservable.valueHasMutated(), try this after you make the change. I havent tested this with your code, but this might solve your problem - TEK
The reason seems to be that you are changing the value contained in you observable without ko being aware of that change. One solution (as @TEK already mentioned) should be the usage of valueHasMutated(). - Benjamin Eckardt
Thanks, I tried adding self.Answers.valueHasMutated(), but unfortunately no dice. I'm starting to jump into debugging the Knockout code entered when calling valueHasMutated(), does Knockout have any documentation on that? Can't find anything on their website, and most other places I've seen just say it's used to update subscribers of a change - starvingprogrammer
1. the '.done' is async and will run after the 'valueHasMutated()' call, if you put it inside the done function it will run (but it be called for every option). one option is to only execute the 'valueHasMutated()' when it is the last option in the foreach. 2. why are you using the click event on a checkbox instead of checked? you could go the self.Answers.subscribe((options) => {}) that way.. - Sam

1 Answers

0
votes

I was advised that the issue was occurring because properties of the objects in CurrentAnswers() weren't observable as well. Since there weren't changes to the array itself, knockout wasn't registering that it had to rebind anything, which is why calling valueHasMutated() didn't change anything as the array looked exactly the same before and after CheckItems() from an array standpoint (also why updating with a copy of the array didn't work, same issue). When setting CurrentAnswers() to a blank array (self.CurrentAnswers([])), then setting it to the updated value, things changed properly since KO recognized the actual array changes.

The solution I ended up using was to utilize ko.mapping.fromJS() method to fill CurrentAnswers() with objects that had observable properties. After some syntax changes in the HTML and JS to properly reference the new observables, everything worked properly.