I have to retrieve a list of menu item from a database and display it in a tree structure I want to use the menu name as the node name and menu id as the id of the node.
The method I used was to retrieve the data using an ajax call and put them into a list and then display it as a tree.But I think dynamically creating nodes depending on the data is more efficient.
function createNodeList(){
$('#menuCPanel #contentData #tree').jstree({
"json_data" : {
/*"data" : [{
"data" : {title : "menuName"},
"attr" : {id : "menuId"},
"state" : "closed"
}
],*/
"ajax" :{
"type" : "POST",
"url" : "?m=admin?action=getNodeList",
"dataType" : "json",
"data" : function(result){
return {
id : result.attr ? result.attr("id") : result['menuId'],
title : result.attr ? result.attr("title") : result['menuName']
};
},
},
},
"callback" : {
},
"themes" : {
"theme" : "classic",
"dots" : true,
"icons" : true
},
"plugins" : ["json_data", "themes"]
}).bind("select_node.jstree", function (e, data) { alert(jQuery.data(data.rslt.obj[0], "jstree").id) });
}
}
this is the stucture of my json data
"data":[{"menuId":"1","menuName":"Top Menu"},{"menuId":"2","menuName":"Main Menu"},{"menuId":"3","menuName":"Bottom Menu"},{"menuId":"4","menuName":"Main Menu"}]}
I would like to know what is wrong with the above result and how to dynamically create a node within in the ajax.success();
I went through some examples but all of them use the jstree.cretate() which i can't invoke inside jstree.json_data.ajax.success()
thanks in advance :)
This is a standard jstree with json data, which also binds select_node to do actions when a node is selected. Nodes must not have an ID which are plain numbers or contain jquery special selector characters. Number IDs must have a standard character first. so 1 should be N1, 2 should be N2 for example.
$('#MyTreeDiv').jstree({
"json_data": {
"ajax": {
"type": "POST",
"url": "/MyServerPage.aspx?Action=GetNodes",
"data": function (n) { return { id: n.attr ? n.attr("id") : 0} },
}
},
"themes": {
"theme": "default",
"url": "/Content/Styles/JSTree.css",
"dots": false
},
"plugins": ["themes", "json_data", "ui", "crrm"]
}).bind("select_node.jstree", function (e, data) {
var selectedObj = data.rslt.obj;
alert(selectedObj.attr("id"));
});
The json returned from your server must be in the correct format as defined in the jstree documentation, and must no include several special characters, unless those characters are escaped or the json created using serialization.
Related
I have this Firebase database with the following data:
Here is a part of the JSON file:
"SWPL6X" : {
"-LznFoTKvdc5318O97h_" : {
"direction" : "in",
"excluded" : false,
"in" : 422,
"multiplier_out" : 1.05,
"name" : "MaM.OUT.90F8",
"out" : 6582,
"total" : -6160
},
"-M2ZQU2azNc-JTKKXojU" : {
"direction" : "in",
"excluded" : false,
"in" : 316,
"multiplier_out" : 1.05,
"name" : "CdG.Out.96A4",
"out" : 2509,
"total" : -2193
},
"-M3zVSWzqb4TaezY1Kua" : {
"direction" : "in",
"excluded" : false,
"in" : 4806,
"name" : "MaM.in.3FBC",
"out" : 748,
"total" : 4058
},
I just need to order alphabetically the last children by the field "name" (String) but I can't find a way to achieve it.
The code before I tried to sort is the following:
getSensors: function(uid, event_id, success){
var ref=firebase.database().ref('clients_events_sensors/'+ uid+ '/'+ event_id);
kap.firebase.bindings.liveObject(ref, success, function(e){
kap.log('cant get client event sensors', e)
});
},
I tried changing the first line to this with no effect:
var ref=firebase.database().ref('clients_events_sensors/'+ uid+ '/'+ event_id).orderByChild('name');
This function is only called once here, the logs retrieved keep the same order as the one coming initially from Firebase:
app.data.getSensors(kap.page.uid, kap.page.event_id, function(sensors) {
$.each(sensors, function(k, sensor){
//remove picture to avoid blowing localstorage memory
sensor.photo=null;
//Replace total by in+out/2 in the app only
sensor.total = Math.round((sensor.in+sensor.out) / 2);
sensor.sum = sensor.in+sensor.out;
});
kap.log('got sensors', sensors);
kap.page.sensors = sensors;
kap.page.fill();
});
The log:
got sensors {
"-LznFoTKvdc5318O97h_":{"direction":"in","excluded":false,"in":492,"multiplier_out":1.05,"name":"MaM.OUT.90F8","out":6983,"total":3738,"photo":null,"sum":7475},
"-M2ZQU2azNc-JTKKXojU":{"direction":"in","excluded":false,"in":339,"multiplier_out":1.05,"name":"CdG.Out.96A4","out":2686,"total":1513,"photo":null,"sum":3025},
"-M3zVSWzqb4TaezY1Kua":{"direction":"in","excluded":false,"in":5150,"name":"MaM.in.3FBC","out":836,"total":2993,"photo":null,"sum":5986},
"-M9Y7Ed7JtPSKTUQXRhd":{"direction":"in","excluded":false,"in":470,"multiplier_out":1.05,"name":"Volt.Out.99CE","out":2323,"total":1397,"photo":null,"sum":2793},
"-MAa2FZ5z5fMejpSL7zp":{"direction":"in","excluded":false,"in":4615,"multiplier_in":1,"name":"CdG.In_A011","out":486,"total":2551,"photo":null,"sum":5101},
"-MAa2QX5rvjWlT_2z7YQ":{"direction":"in","excluded":false,"in":3729,"name":"Volt.In_A013","out":1203,"total":2466,"photo":null,"sum":4932},
"-MAa2UaqVDyxQrQojFiR":{"direction":"in","excluded":false,"in":400,"multiplier_out":1.05,"name":"Pagnol.Out_A014","out":2205,"total":1303,"photo":null,"sum":2605},
"-MBiKvKAAWQkU8RABWR0":{"direction":"in","excluded":false,"in":2613,"name":"Pagnol.In_A015","out":987,"total":1800,"photo":null,"sum":3600}}
Am I missing something?
I'm implementing an asynchronous webpage in Grails where I make a call to a controller and render the response as a D3.js network. I stored the object into a global variable for further use. The function worked fine when data is returned. However when I tried to use the object later I found some fields became undefined.
var similar;
function asynchroNetwork() {
var jsonData = $.ajax({
url: "${createLink(controller:'environment', action:'asynchro')}",
dataType: "json",
async: true
}).done(function(jsonData) {
console.log(jsonData);
//console.log(jsonText);
document.getElementById("result").innerHTML="Done";
console.log(jsonData.all);
//set global variable
similar=jsonData;
console.log("The object got");
console.log(similar);
draw();
});;
}
function draw(){
console.log(similar);
//draw the network based on object
}
The controller returns the following JSON:
{
"\u521B\u4E1A" : {
"nodes" : [{
"group" : 1,
"name" : "\u6c88\u9633\u8f9b\u98962\u4e16"
}
],
"links" : []
},
"all" : {
"nodes" : [{
"group" : 1,
"name" : "qwe25322570"
}, {
"group" : 1,
"name" : "\u660e\u5fb7\u7b03\u884c"
}, {
"group" : 1,
"name" : "\u6c88\u9633\u53ef\u4e50"
}, {
"group" : 1,
"name" : "\u6c88\u9633\u5f6d\u79c0\u8363"
}, {
"group" : 1,
"name" : "\u6c88\u9633\u738b\u632f\u534e"
}, {
"group" : 1,
"name" : "\u6c88\u9633\u8f9b\u98962\u4e16"
}
],
"links" : [{
"value" : 1.0,
"target" : "\u6c88\u9633\u8f9b\u98962\u4e16",
"source" : "\u660e\u5fb7\u7b03\u884c"
}, {
"value" : 1.0,
"target" : "\u6c88\u9633\u8f9b\u98962\u4e16",
"source" : "qwe25322570"
}
]
}
}
In the draw() function it uses similar[all] to draw a graph. But in the links field I see the fields source and target are all undefined, while all fields in the nodes are all fine.
I don't think the encoding is the cause because nodes also contains UTF8 characters in the fields but none got missing. After the object is passed back from asychronous function the object similar is okay, but the next time draw() is called, I can see the field links go undefined.
Does anyone know what could be the cause of this problem? I guess it maybe related to nested fields.
Did you try passing your data as an argument to the draw function? If not, then:
In your ajax callback:
similar = jsonData;
draw(similar) //pass the json
draw function:
function draw(json) {
console.log(json);
}
I have a JSON file containing information for a tile based navigation app which uses the Router. Each tile could be a link directly to an external application, or it could contain subtiles which are displayed when the tile is clicked. Each of these could have their own subtiles, and so on. The JSON will eventually be delivered by an OData service so the app needs to dynamically create the navigation as it may change.
How can I implement the Router to have the URL looking like #tile1/tile1-1/tile1-1-3 which would indicate the user clicked on the first tile, which went to a screen where they clicked on the first tile there, followed by another screen on which they clicked the third tile? That route would, when coming from a bookmark, load the screen with subtiles from the tile1-1-3 node from the JSON.
The names 'tile1-1-3' etc are only to help visualise the position of the tile for this example. In the real version the names won't indicate the position in the tree, they will be more like #MyApps/MyApprovalApps.
I have a recursive function which crawls through every node and generates an individual route, but I'm unsure how to add the dynamic pattern like {tile}/{subtile}/{subtile} and also the parent route which I think will be needed to navigate between the levels properly.
I have a Home.view.xml which displays the top level tiles, and a Page1.view.xml for the rest of the levels of subtiles. Is this correct? How can I create the views dynamically if not?
Hopefully my goal is clear, I can elaborate more if needed.
Recursive function which creates the routes:
createRoutes: function(aData, oRouter){
for(var i=0; i<aData.length; i++){
oRouter.addRoute({name: aData[i].id,
pattern: aData[i].title,
view: "Page1"}); //parent?
if(aData[i].subtiles.length > 0){ // has subtiles
this.createRoutes(aData[i].subtiles, oRouter);
}
}
}
JSON:
{
"TilesCollection" : [
{
"id" : "tile1",
"title" : "tile1",
"target" : "#",
"subtiles" : [
{
"id" : "tile1-1",
"title" : "tile1--1",
"target" : "#",
"subtiles" : []
}
]
},
{
"id" : "tile2",
"title" : "tile2",
"target" : "#",
"subtiles" : [
{
"id" : "tile2-1",
"title" : "tile2--1",
"target" : "#",
"subtiles" : []
},
{
"id" : "tile2-2",
"title" : "tile2--2",
"target" : "#",
"subtiles" : []
},
{
"id" : "tile2-3",
"title" : "tile2--3",
"target" : "#",
"subtiles" : [
{
"id" : "tile2-3-1",
"title" : "tile2--3--1",
"target" : "#",
"subtiles" : []
},
{
"id" : "tile2-3-2",
"title" : "tile2--3--2",
"target" : "#",
"subtiles" : []
}
]
}
]
},
{
"id" : "tile3",
"title" : "tile3",
"target" : "#",
"subtiles" : []
},
{
"id" : "tile4",
"title" : "tile4",
"target" : "#",
"subtiles" : [
{
"id" : "tile4-1",
"title" : "tile4--1",
"target" : "#",
"subtiles" : []
}
]
}
]
}
How about this?
createRoutes: function(aData, oRouter, sParentPattern, iViewLevel) {
iViewLevel = iViewLevel || 0;
for (var i=0; i<aData.length; i++) {
var sPattern = sParentPattern ? sParentPattern +"/"+ aData[i].title : aData[i].title;
oRouter.addRoute({
name: aData[i].id,
pattern: sPattern,
view: "Page1",
viewLevel : iViewLevel
});
if (aData[i].subtiles.length > 0) {
this.createRoutes(aData[i].subtiles, oRouter, sPattern, iViewLevel+1);
}
}
}
In this example you just use the pattern to build the parent-child relationship just as you suggested.
If I understand you correctly you're asking how can you represent th clicking route as a string, whcih you can pass as prt of a URL ?
Referring to the tile IDs...
You could pass the text in as a JS array of objects I guess (ie include this JSON as part of the URL) :
a.b.com/xyz?route=[{"tile1-1",{"tile1-1"}},{"tile2",{"tile2-3"}}]
meaning they clicked tile1 -> tile1-1 -> tile2 -> tile2-3
Alternatively if the Ids are potentially secure or something, you could pass it by index no. as it's an aray :
a.b.com/xyz?route=[{0,{0}},{0,{2}}]
Then eval the passed string to turn it directly into Javascript object.
Or if you're concerned about people hacking, write a routine to interpret it.
(same clicking as above) - this relies on the tile arrangement never changing though.
When I store load, how can I get the success property??
load store:
store.load({
scope: this,
callback: function(records, operation, success) {
if (success) {
console.log('success');
} else {
console.log('error');
}
});
return json data:
{
"header" : {
"success" : "true",
"message" : "complete!"
},
"data" : [{
"id" : 1,
"userId" : 1,
"subject" : "Document 1"
},{
"id" : 2,
"userId" : 1,
"subject" : "Document 2"
}]}
I want get "success" property in "header" and use this in a condition of the if statement.
http://docs-origin.sencha.com/extjs/5.0/apidocs/#!/api/Ext.data.reader.Reader-cfg-successProperty
So where you define the store, you have to add a successProperty to the store's reader, which should be "header.success".
You can't use different successProperties for one store, depending on which operation you perform.
I have a problem with knockout mapping. I'm using knockout mapping plugin to represent a form that is serialized in JSON. It was working before using the knockout mapping but I need to use knockout mapping since I want my properties to be observable.
You can see the working html here : http://jsfiddle.net/etiennenoel/wG9SZ
Here's my not working javascript code:
var formData =
{"data":
[
{
"groupName" : "Properties",
"content" :
[
{
"title" : "Calculation Method",
"formType" : "select",
"value" :
[
{
"title" : "Voltage Drop - Unbalanced",
"selected" : true
},
{
"title" : "Voltage Drop - Balanced"
}
]
},
{
"title" : "Tolerance (% V)",
"formType" : "textBox",
"value" : 0.01
},
{
"title" : "Calculation Options",
"formType" : "radio",
"value" :
[
{
"title" : "Flat Start (at Nominal Conditions",
"checked" : false
} ,
{
"title" : "Assume Line Transposition",
"checked" : true
}
]
},
{
"title" : "Adjust Conductor Resistance at",
"formType" : "textBox",
"disabled" : true,
"value" : 77,
"appendLabel" : true,
"appendLabelText" : "°F"
}
]
},
{
"groupName" : "Properties",
"content" :
[
{
"title" : "Calculation Method",
"formType" : "select",
"value" :
[
{
"title" : "Voltage Drop - Unbalanced",
"selected" : true
},
{
"title" : "Voltage Drop - Balanced"
}
]
},
{
"title" : "Tolerance (% V)",
"formType" : "textBox",
"value" : 0.01
},
{
"title" : "Calculation Options",
"formType" : "radio",
"value" :
[
{
"title" : "Flat Start (at Nominal Conditions",
"checked" : false
} ,
{
"title" : "Assume Line Transposition",
"checked" : true
}
]
},
{
"title" : "Adjust Conductor Resistance at",
"formType" : "textBox",
"disabled" : true,
"value" : 77,
"appendLabel" : true,
"appendLabelText" : "°F"
}
]
}
]
};
ko.mapping.fromJS(formData);
Here's the jsfiddle of the same code: http://jsfiddle.net/etiennenoel/wG9SZ/3/
What is the problem between when I use mapping and when I don't use it ?
In your second case you forgot to ApplyBindings.
ko.applyBindings(formData);
I don't know if this is the case with your scenario, but it's worth a post.
I had issues with the mapping plugin, when I had a more complex viewmodel with nested properties or lists. It turned out that after mapping to an already constructed viewmodel, the sub-objects were no more observables. With this issue, for me this code worked, what I've found somewhere (unfortunately I already really don't know where). I called this function for my viewmodel after mapping to that.
function makeAllObservables(observable) {
// Loop through its children
for (var child in observable()) {
// If this child is not an observable and is an object
if ((!ko.isObservable(observable()[child])) && (typeof observable()[child] === "object")) {
// Make it an observable
observable()[child] = ko.observable(observable()[child]);
// Make all of its children observables
makeAllObservables(observable()[child]);
}
}
};
Usage (when updating the model from server response, the first line should not be there):
var model = ko.observable({});
ko.mapping.fromJS(myJSObject, {}, model);
makeAllObservables(model);
ko.applyBindings(model);
I ment mapping to an already constructed viewmodel for example, when you want to update your viewmodel with new JSON data from server. In that case I lost nested bindings without the code above.
UPDATE: I've found the source where I borrowed the technique from, here. Note that I slightly modified that code in that post, because somehow that was not working for me.
You need to bind the mapped viewmodel to the view:
ko.applyBindings(ko.mapping.fromJS(formData));
and since everything is now an observable the logic in the view needs to be changed to use the method syntax:
<!-- ko if: $data.formType() === "select" -->
To get the options to display, you need to tell knockout what the property name is on the object:
<select data-bind="options: $data.value, optionsText: 'title'"></select>