Tree View control with JavaScript and Knockout for large trees

Most of the existing tree views used to crash my browser, they are making the naive mistake to load all the nodes in one shot. The simple solution was to load the nodes on demand, and when you open a node with thousands of children, and then you open another node which again has thousands of children, it is better to close the previous node and let go all their children than crash the browser.

Read more here;

Change visible status based on if array is empty or not in knockout.js

I want to be able to have a table show only if there are items in an array.

JS

var view_model = {
    lines: ko.observableArray([
        {
        content: 'one'},
    {
        content: 'two'},
    {
        content: 'three'},
    {
        content: 'four'},
        ]),
    remove: function(data) {
        view_model.lines.remove(data);
    }
};

ko.applyBindings(view_model);

HTML

<span data-bind="visible:lines">Lines Exist</span> 
<ul data-bind='foreach:lines'>
    <li>
        <button data-bind="click:$parent.remove">
            Remove
        </button>
        <span data-bind="text:content"></span>
    </li>
</ul>

Here is one solution;

<span data-bind="visible:lines">Lines Exist</span> 
<!-- ko if: lines().length > 0-->
<p>Here is my table</p>
<ul data-bind='foreach:lines'>
    <li>
        <button data-bind="click:$parent.remove">
            Remove
        </button>
        <span data-bind="text:content"></span>
    </li>
</ul>
<!-- /ko -->​

Refer to Stackoverflow

Knockout debugging

There are chrome extension available but we can use a very basic statement;

console.log("View model: " + ko.toJSON(dataVM));

If we are dealing with array then;

console.log(dataVM()[0]);

If we want to unwrap a view model then (make sure you have created the view model with ko.mapping.fromJS method;

var dataVMUnwrap = ko.mapping.toJS(dataVM);
//convert view model back to js
console.log(JSON.stringify(dataVMUnwrap));

What is the difference between $root and $parent in Knockout?

They are similar but different:

  • $root refers to the view model applied to the DOM with ko.applyBindings;
  • $parent refers to the immediate outer scope;

Or, visually, from $data‘s perspective:

Or, in words of the relevant documentation:

  • $parent: This is the view model object in the parent context, the one immeditely outside the current context.
  • $root: This is the main view model object in the root context, i.e., the topmost parent context. It’s usually the object that was passed to ko.applyBindings. It is equivalent to $parents[$parents.length - 1].
  • $data: This is the view model object in the current context. In the root context, $data and $root are equivalent.

You’ll only see a practical difference if you have view models nested more than one level, otherwise they will amount to the same thing.

It benefit is rather simple to demonstrate:

var Person = function(name) {
  var self = this;
  self.name = ko.observable(name);
  self.children = ko.observableArray([]);
}
  
var ViewModel = function() {
  var self = this;
  self.name = 'root view model';
  self.mainPerson = ko.observable();
}

var vm = new ViewModel(),
    grandpa = new Person('grandpa'),
    daddy = new Person('daddy'),
    son1 = new Person('marc'),
    son2 = new Person('john');

vm.mainPerson(grandpa);
grandpa.children.push(daddy);
daddy.children.push(son1);
daddy.children.push(son2);

ko.applyBindings(vm);
th, td { padding: 10px; border: 1px solid gray; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>

<script type="text/html" id="person">
  <tr>
    <td data-bind="text: $root.name"></td>
    <td data-bind="text: $parent.name"></td>
    <td data-bind="text: $data.name"></td>
  </tr>
  <!-- ko template: { name: 'person', foreach: children } --><!-- /ko -->
</script>

<table>
  <tr>
    <th>$root</th>
    <th>$parent</th>
    <th>$data</th>
  </tr>
  <!-- ko template: { name: 'person', data: mainPerson } --><!-- /ko -->
</table>

The $root is always the same. The $parent is different, depending on how deeply nested you are.

Here is another good example on StackOverflow.

Knockout Boolean binding not working

Here’s a quick fix if you’re doing data binding in knockout and wondering why a binding like the following isn’t working:

<div data-bind="visible: !isMessageHidden">

<script>
// In your view model
self.isMessageHidden: ko.observable(true) };
// ...
</script>

You need to add a set of parentheses after the isMessageHidden like so.

<div data-bind="visible: !isMessageHidden()">any message</div>

The key to understanding this is understanding that isMessageHidden is not a boolean value, but an observable which stores a boolean value. If we simply refer to this observable by name in our data-binding code, then knockout will infer that we want to retrieve the stored value. However, if we wish to refer to the stored value within a statement, such as !isMessageHidden(), then the parentheses are required in order to indicate that we want the stored value and not the observable itself. This tripped me up a few times when I was learning Knockout.