0

I am trying to get Knockout and a Bootstrap-TreeView to work together. (The component: https://github.com/jonmiles/bootstrap-treeview)

At the moment, I'm passing the JSON from an API call to the constructor of the View Model. This will change later but for simplicity, I'm doing this.

What I need then is to bind click events to each node. So if I click the root node, nothing happens, click a folder, and I can get a list of all it's direct child text values (Just alert them for now), and if I click a file node, I alert the 'data' value from that node.

Here's a fiddle to see what I have done so far.

https://jsfiddle.net/Cralis/h15n2tp7/

My View Model simply initialises with the json data. And then a computed in the view model does the setup of the Tree View.

// Create the View Model.
var ViewModel = function(jsonData) {
  var self = this;
  self.MyData = ko.observable(jsonData);

  ko.computed(function() {
    $('#tree').treeview({
        data: self.MyData()
      })
      .on('nodeSelected', function(event, data) {
        if (data.nodeLevel == 2) { // Are we clicking a File?
          alert("Clicked a File. Data: " + data.data)

        }
        else
        if(data.nodeLevel == 1) { // We're clicking a folder.
           alert("Clicked a folder. Would like to somehow alert a list of all child node text values.")
        }
      });
  })
}

// Create the View Model and initialise with initial data 
var vm = new ViewModel(getTree());

// Bind.
ko.applyBindings(vm, document.getElementById("bindSection"));

This works, but I don't think I'm using Knockout much. That's because my click events are in my javascript, and my Knockout view model doesn't really have any control.

How can I allow Knockout to 'see' the click events. So, onclick of a node, a knockout computed (I think?) fires and I can then control the UI based on bind events.

Outside of this, I have a DIV which shows a list of files. What I was was that when a folder level node gets selected, I can populate that div with all the 'text' values from the children of that selected folder node.

Any pointers in how I can achieve this would be amazing. I'm just not sure how I can get data-bind="click... to the nodes, which can then run the code that's currently in the 'onclick' in my fiddle.

Craig
  • 18,074
  • 38
  • 147
  • 248
  • No you aren't using knockout for anything. The computed isn't being assigned to a property and it's not computing anything. Also your data binding is being done by the treeview function not knockout. You could remove the knockout stuff completely and just have the treeview data assignment and the on nodeselect and it would work the exact same. If you want to use knockout you would need to build a viewmodel and a treeview template to bind to and get rid of the treeview extension. – Alex Terry Jan 08 '17 at 03:08

1 Answers1

3

I've updated your fiddle with a custom binding: https://jsfiddle.net/h15n2tp7/2/

As I already posted here in this question: add-data-bind-property-to-a...

I think this is the best way do it. The problem here is the synchronization between 1) fetching JSON 2) applying bindings 3) creating DOM elements. Creating custom binding lets you do that easily without much of messy code. In your case, when a getTree function is done via $.get, you need to create a view model in .done function, and apply bindings after that. So the provided fiddle will change a bit, but the idea is the same. Note, that you don't need any observables (if the tree data does not change while the app is running). If it does change though, make sure that you implement update function in a custom binding (knockout custom binding reference).

Community
  • 1
  • 1
Adam Wolski
  • 5,448
  • 2
  • 27
  • 46
  • Thanks @AdamWolski - that's extremely helpful. And this question was based on the answer to your previous question, as you showed me I was on the wrong path. I'm unsure how to apply a data-bind="click: MyClickMethod" to a node though. Is this possible? If I click 'Folder 1' as an example, do I have access to the 'data' for that node, which will include the 'nodes' property, so that I can iterate through them and get their child node properties? – Craig Jan 08 '17 at 22:09
  • Sure, you have since you are defining the handler inside the function that creates the custom binding. Therefore, the handler "holds" the scope of the init/update function :) – Adam Wolski Jan 08 '17 at 22:32
  • Sorry Adam - I'm not sure how I'd do that. Where would I define the data-bind? I'm used to adding it to the HTML control. Not sure how I would assign it to a node. – Craig Jan 08 '17 at 22:55
  • Updated the fiddle, as there was issues with the data. https://jsfiddle.net/h15n2tp7/3/ ... – Craig Jan 08 '17 at 23:20
  • It seems I don't have access to the 'node' property when I click a Level 1 (folder) node. alert(data.text); - that works... but data.nodes is undefined, even though the node has child nodes. – Craig Jan 08 '17 at 23:32
  • @Craig not sure what is the problem, u can surely access the `nodes` property in that object. Check out the console.log: https://jsfiddle.net/h15n2tp7/4/ – Adam Wolski Jan 09 '17 at 09:24
  • @Craig alert(data.nodes) gives me [object Object],[object Object] which is correct – Adam Wolski Jan 09 '17 at 09:25
  • @Craig clicking on folder 2 gives me undefined which is correct considering that it has no children – Adam Wolski Jan 09 '17 at 09:26
  • 1
    you're right! I have no idea what I was doing. It works like a charm! THanks for the assistance. I updated the jsfiddle with a fixed data setup as there were folder in strange levels. https://jsfiddle.net/h15n2tp7/5/ - thanks! – Craig Jan 10 '17 at 05:50