Reputation: 3745
I'm trying to get jsTree (1.0-rc3) working with Knockout.js (2.2.1).
See example jsFiddle: http://jsfiddle.net/adeconsulting/qfr6A/
Note: I've included several JS resources in the Fiddle to match as close as possible my Visual Studio project, in case there's a conflict between libraries which might be causing this problem.
Run the Fiddle and navigate through the jsTree, it's a list of servers by their physical location and type. It helps to have Firebug's console open so you can see the ajax calls and responses. When you click a leaf node, an ajax call is made to retrieve the server details and display a form whose values use Knockout bindings. I hide the form when a non-leaf node is selected.
It works the first time you click a leaf node. After that, Knockout does not update the form for leaf node clicks. However, if you happen to click the Edit button, then all of a sudden the most recent server details ARE displayed.
I'm thinking that there's a conflict between jsTree and Knockout bindings, but don't know where to start troubleshooting what that might be.
Since stackoverflow apparently requires at least one code block, here's the JavaScript portion of the Fiddle:
// Global vars:
var prevJsTreeNodeId = null;
var serverModelBindingsApplied = false;
var serverLoadInProgress = false;
/*
* The knockout.js view model
*/
var ServerViewModel = function () {
// Data
var self = this;
self.IsReadOnly = ko.observable(true); // the form's input mode
self.btnEditSave = ko.observable("Edit"); // the Edit/Save button text
self.Server = ko.observable({}); // the Server object
// Operations
self.setEditable = function () {
self.IsReadOnly(false);
self.btnEditSave("Save");
};
self.setReadOnly = function () {
self.IsReadOnly(true);
self.btnEditSave("Edit");
};
self.doEditSave = function () {
var flag = self.IsReadOnly();
if (flag) {
// switch to Edit mode
self.setEditable();
}
else {
// switch back to readOnly
self.setReadOnly();
}
};
// use ajax to update the knockout.js view model's Server object for the specified server name
self.load = function (serverName) {
if (!serverLoadInProgress) {
serverLoadInProgress = true;
// use ajax to retrieve the server's details
var data = {
json: JSON.stringify({
ServerName: serverName,
PrimaryIP: "1.2.3.4",
BrandDesc: "Dell",
OSDesc: "Windows 2003 Server",
Location: "xyz"
}),
delay: 1
};
$.ajax({
url:"/echo/json/",
data:data,
type:"POST",
success:function(response)
{
console.log(response);
window.ServerViewModelInstance.Server = ko.mapping.fromJS(response);
// apply bindings the first time we retrieve a Server object
if (!serverModelBindingsApplied) {
ko.applyBindings(window.ServerViewModelInstance,
document.getElementById('servercontent'));
serverModelBindingsApplied = true;
}
else {
// hmmm... updating the view model's .Server property doesn't trigger the
// form to be updated, yet if we click the Edit button, the new values
// suddenly appear, so try emulating that here...
self.setReadOnly();
}
}
});
serverLoadInProgress = false;
}
};
}; // ServerViewModel
/*
* document load
*/
$(function () {
// configure the jsTree
$("#divtree")
.jstree({
"themes": { "theme": "default", "dots": true, "icons": true },
"plugins": ["themes", "html_data", "ui", "types"],
"types": {
"type_attr": "tag", // the attribute which contains the type name
"max_depth": -2, // disable depth check
"max_children": -2, // disable max children check
"valid_children": ["root"],
"types": {
"root": {
"valid_children": ["level1"]
},
"level1": {
"valid_children": ["level2"],
"start_drag": false,
"move_node": false,
"delete_node": false,
"remove": false
},
"level2": {
"valid_children": ["leaf"],
// use the theme icon for the level2 nodes
"start_drag": false,
"move_node": false,
"delete_node": false,
"remove": false
},
"leaf": {
"valid_children": "none"
}
}
}
});
// register to receive notifications from #divtree when a jsTree node is selected
$("#divtree").bind("select_node.jstree", function (event, data) {
// data.rslt.obj is the jquery extended node that was clicked
var key = data.rslt.obj.attr("key");
var id = data.rslt.obj.attr("id");
if (id == prevJsTreeNodeId) {
// user clicked the same node, nothing to do
return;
}
prevJsTreeNodeId = id;
// when a jsTree node is selected, reset the knockout.js view model to read only
window.ServerViewModelInstance.setReadOnly();
var idx = key.indexOf("Server");
if (idx === 0) { // "Server|servername"
// show the "servercontent" div
$("#servercontent").show();
// display the server details
var serverName = key.substr(idx + 7, key.length);
window.ServerViewModelInstance.load(serverName);
}
else {
// hide the "servercontent" div
$("#servercontent").hide();
}
});
// hide the "servercontent" div
$("#servercontent").hide();
// instantiate the knockout.js view model
window.ServerViewModelInstance = new ServerViewModel();
}); // document ready
// initialization timer routine to select the main jsTree node
setTimeout(function () {
// open the root node
$.jstree._reference("#divtree").open_node("#root");
}, 500);
Upvotes: 0
Views: 2135
Reputation: 56
Sorry for my bad formatting below - this editor is not my friend... :-/
If I understand you right, the detail panel for a clicked tree node isn't updated with the correct data - right?
Try to do the following:
change:
window.ServerViewModelInstance.Server = ko.mapping.fromJS(response);
to: window.ServerViewModelInstance.Server(response);
(e.g. not overwriting the initial ko.observable which you only binds once, instead updating its values)
and in view where you bind to the observables..
for instance instead of: ... "value: Server.ServerName, ...
change it to: ... "value: Server().ServerName, ...
(e.g executing the function before accessing the property)
It works and updates the form when clicking on a new server name node in the tree (tried in firefox)
A copy of your example with the modified code can be found at: http://jsfiddle.net/RZ92g/2/
Upvotes: 2