1

I'm brand new to Knockout.js and have been following a tutorial. However, my code is not working as expected, because in my custom binding createDecimal, valueAccessor() is returning the signature of the function instead of the actual value.

I'm using the latest version of Knockout.js as of today, v3.4.2.

<p>First Name <strong data-bind="text: firstName"></strong></p>
<p>Last Name <strong data-bind="text: lastName"></strong></p>
<p>Change First Name <input data-bind="value: firstName, valueUpdate: 'afterkeydown'" type="text" name="firstName"></p>
<p>Change Last Name <input data-bind="value: lastName, valueUpdate: 'afterkeydown'" , type="text" name="lastName"></p>
<p>Full Name: <strong data-bind="text: fullName"></strong></p>
<p>Add new hobby: <input data-bind="value: hobby, valueUpdate: 'afterkeydown'" type="text" name="hobby"><button data-bind="click: addHobby">Add</button></p>
<p>Hobby to possibly be added: <strong data-bind="text: hobby"></strong></p>
<ul data-bind="foreach: hobbies">
  <div>
    <li data-bind="text: $data"></li>
    <button data-bind="click: $parent.removeHobby">Remove Hobby</button>
  </div>
</ul>
<p>Total Number of Properties: <strong data-bind="makeDecimal: numberOfHobbies"></strong></p>

<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<script type="text/javascript">
  ko.bindingHandlers.makeDecimal = {
    update: function(element, valueAccessor) {
      var numberOfHobbies = valueAccessor();
      console.log("Number of Hobbies", numberOfHobbies);
      var formattedText = "$" + numberOfHobbies.toFixed();
      element.innerText = formattedText;
    }
  };

  function IndexViewModel() {
    var self = this;
    self.firstName = ko.observable("George");
    self.lastName = ko.observable("Lopez");
    self.hobby = ko.observable();
    self.hobbies = ko.observableArray(["Acting", "Joking", "Speaking"]);
    self.addHobby = function() {
      self.hobbies.push(self.hobby());
    };
    self.removeHobby = function(hobby) {
      self.hobbies.remove(hobby);
    };

    self.fullName = ko.computed(function() {
      return self.firstName() + " " + self.lastName();
    });

    self.numberOfHobbies = ko.computed(function() {
      return self.hobbies().length;
    });

  }

  ko.applyBindings(new IndexViewModel());
</script>
Govind Rai
  • 14,406
  • 9
  • 72
  • 83
  • You just need to add `ko.track(this);` at the end of your `IndexViewModel`: https://codepen.io/anon/pen/qjPavm. And, of course, use the plugin (https://cdnjs.cloudflare.com/ajax/libs/knockout-es5/0.4.6/knockout-es5.js). – Jose Luis Jun 25 '17 at 11:45

1 Answers1

1

It's not natural, but here you need to use ko.unwrap() to get the value of the observable.

ko.bindingHandlers.makeDecimal = {
    update: function(element, valueAccessor) {
        var numberOfHobbies = ko.unwrap(valueAccessor());
        console.log("Number of Hobbies", numberOfHobbies);
        var formattedText = "$" + numberOfHobbies.toFixed();
        element.innerText = formattedText;
    }
};

It's because of the way Knockout manage observables, by replacing them by functions.

Jimbot
  • 696
  • 5
  • 20
  • Thanks Jimbot. That solves the problem! This is super weird because I was following along Steve Sanderson's (the original author's) tutorial and his code did not require the unwrap even though he was getting the value of a computed property.. – Govind Rai Jun 24 '17 at 23:16
  • 2
    See [video](https://www.youtube.com/watch?v=MNiUcuo3Wio). At 19:13, he creates a custom binding to add currency formatting to a computed property `grandTotal`, and he never unwraps the computed property in his custom binding `currencyText`... – Govind Rai Jun 24 '17 at 23:19
  • 2
    Yes it strange, not sure why it work for him at the time. Anyway, I find this about the [difference between () and unwrap](https://stackoverflow.com/questions/16710425/difference-between-unwrapobservable-and). Also, I read somewhere that observable are functions because javascript lack support of encapsulated properties (getter setter) in most browsers. But I can't remember where... – Jimbot Jun 25 '17 at 05:40
  • 2
    In this link (http://knockoutjs.com/documentation/custom-bindings.html) they get the last value with `var value = valueAccessor();`. If the property is not an observable, then you have the correct value. If it is an observable, then `value` is a function, and get the value with `var valueUnwrapped = ko.unwrap(value);`. `ko.unwrap()` can be called also to non observables, then it could be used allways. – Jose Luis Jun 25 '17 at 09:40
  • 1
    In this video, the author is using his own plugin (http://blog.stevensanderson.com/2013/05/20/knockout-es5-a-plugin-to-simplify-your-syntax/) to simplfity sintax. You could also see this link (https://stackoverflow.com/questions/44414554/optimizing-knockout-tko-alpha3) that talks a new version of Knockout. – Jose Luis Jun 25 '17 at 10:01