JsTree how to get IDs of group node - javascript

I want to get IDs of all child, Surface nodes, and a higher level.
See the image below
When i click on C1 node :
1 - I want the IDs of all the lower level nodes contains : D1 , D2
2 - I want the IDs of node that is a higher direct level : B2
3 - And i want the IDs of all IDs of straight level : C2

jsTree's API provides with functions to identify and traverse between nodes. Here's a small script you could use to identify child, parent(direct level) & sibling nodes(straight level).
$('#jstree').bind('select_node.jstree', function (e, data) {
var tree = $('#jstree').jstree(true),
parentNode = tree.get_node(data.node.parent),
aChildren = data.node.children,
aSiblings = [];
parentNode.children.forEach(function(c){
if(c !== data.node.id) aSiblings.push(c);
});
console.log("1.)" + JSON.stringify(aChildren));
console.log("2.)" + JSON.stringify(parentNode.id));
console.log("3.)" + JSON.stringify(aSiblings));
});

Related

How to get dbId of only visible objects in Viewer?

I can get the dbId of all items in the Viewer via
const tree = viewerApp.myCurrentViewer.model.getData().instanceTree;
const dbIndices = Object.values(tree.nodeAccess.dbIdToIndex).slice(1);
But for models imported from Revit, their number is much larger than the actually visible objects in the Viewer (for example, for a project consisting of only three walls, this number is approximately 3,500). How do I get a dbId of only visible objects?
By default all nodes (assets to render for Viewer) are visible when a model is loaded. Each node can be uniquely identified by an unique dbid in addition to its externalId that corresponds to the UniqueID of a Revit component.
So the extra dbids that you observed are actually parent nodes. To isolate them, see here to traverse all the leaf nodes (that is nodes representing a single visible components):
function getAllLeafComponents(viewer, callback) {
var cbCount = 0; // count pending callbacks
var components = []; // store the results
var tree; // the instance tree
function getLeafComponentsRec(parent) {
cbCount++;
if (tree.getChildCount(parent) != 0) {
tree.enumNodeChildren(parent, function (children) {
getLeafComponentsRec(children);
}, false);
} else {
components.push(parent);
}
if (--cbCount == 0) callback(components);
}
viewer.getObjectTree(function (objectTree) {
tree = objectTree;
var allLeafComponents = getLeafComponentsRec(tree.getRootId());
});
}

BFS Tree Traversal

I'm trying to write a Breadth-First-Search algorithm in Javascript that also includes the number of repetitions of every node. For example, in a videogame crafting menu, I want to craft a desired item A that requires items B x20, C x30, and D x100. Items B, C, and D in turn require other items times another arbitrary amount. I want to know how many of the items at the bottommost level of the tree I would need in order to craft item A. Try as I might, I cannot find a solution for this. Basically, I am doing a BFS of the materials and a BFS of the material amounts "in parallel". The intended output is a 2D array (2 rows, x columns) with the (unique) material names in the first row and the associated material amounts in the second row. This current code gives me a blank result. I've written 10-20 variations of it by now over the course of 8 weeks after having searched StackOverflow, https://www.w3schools.com/jsref/jsref_obj_array.asp, https://www.geeksforgeeks.org/java-util-hashmap-in-java/, and https://developer.mozilla.org/en-US/docs/Web/JavaScript. No luck, or I am just so new to JS that I don't see an answer that's right in front of me. All of the functions I have called from this function work perfectly (I tested them separately).
function traversebreadth(key, keyamt, leaveschecked, repschecked, leavesqueue, repsqueue, roots, leaves, leafreps) { //initial parameters: string, number, [], [], string (same as key), number (same as keyamt), Array (column vector), 2D array (each row is a material list corresponding to the same row in "roots"), 2D array (each row is a material amount list corresponding to the same row in both "roots" and "leaves")
var keyleaves = getleaves(key, roots, leaves); //gets the leaves(/children) of the current node
var keyreps = getleafreps(key, roots, leafreps); //gets the children of the current node's repetitions (or "amounts")
keyreps = scalarmultiply(keyamt, keyreps); //multiplies the current repetitions by the number of repetitions of the parent node
leaveschecked += key; //push the key to the queue of checked nodes
repschecked += keyamt; //push the keyamt to the queue of checked node reps
if(!Array.isArray(leavesqueue)){ //ensure leavesqueue is an Object (Array), not a Number
var lq = [];
lq.push(leavesqueue);
leavesqueue = lq;
}
if(!Array.isArray(repsqueue)){ //ensure repsqueue is an Object (Array), not a Number
var rq = [];
rq.push(repsqueue);
repsqueue = rq;
}
if(isemptyarray(leavesqueue,"row")){ //if leavesqueue is empty, then there are no more leaves to check and this recursive function can return the result
return ArrayLib.transpose(leaveschecked).concat(ArrayLib.transpose(repschecked)); //return all of the unique nodes needed to craft item A, and the total amount of each node
}else{ //else, repeat this function
var newleavesqueue = queueleaves(keyleaves, leavesqueue); //queueleaves() appends keyleaves to leavesqueue and removes the first element of leavesqueue, then returns leavesqueue
var newrepsqueue = queueleafreps(keyreps,repsqueue); //queueleafreps() does the same thing as queueleaves() using keyreps and repsqueue
var newkey = ArrayLib.transpose(newleavesqueue).shift(); //gets the new node to use as current key
var newkeyamt = ArrayLib.transpose(newrepsqueue).shift(); //gets the reps of this new current key
traversebreadth(newkey, newkeyamt, leaveschecked, repschecked, newleavesqueue, newrepsqueue, roots, leaves, leafreps); //repeat this function with the new parameters
}

How to go to a parent node from a node in a treeview of wijmo flex grid

I am using wijmo flex grid to create a tree view for my data, I am able to find whether a specific node has children or not and what is the level of the node but I am unable to go to the parent node from a given node. The index for each row is also being retrieved.
Any insights on the topic would be highly helpful.
$scope.selectionChanged = function(sender,args){
var index = sender.selection.row;
var temp;
console.log(index);
temp = sender._rows[index]._data;
console.log(temp.reports);
};
FlexGrid rows come in two flavors: regular rows (Row objects) and nodes (GroupRow objects). Regular rows have no "level", but GroupRow objects do have a "level" property that you can use to get the node's level.
To get a row's parent node, you should scan the grid's rows collection up until you find a node that has a "level" smaller than the one you started with.
Here's a fiddle the demonstrates:
http://jsfiddle.net/Wijmo5/8n2yde6f/
Check out the implementation of the "getParentNode" method, that should be what you're looking for:
// gets the parent row for a given FlexGrid row.
// returns the parent row or null if original row doesn't have a parent.
function getParentNode(row) {
// get row level
var startLevel = row instanceof(wijmo.grid.GroupRow) ? row.level : -1;
var startIndex = row.index;
// travel up to find parent node
for (var i = startIndex - 1; i >= 0; i--) {
var thisRow = row.grid.rows[i],
thisLevel = thisRow instanceof(wijmo.grid.GroupRow) ? thisRow.level : -1;
if (thisLevel > -1) {
if (startLevel == -1 || (startLevel > -1 && thisLevel < startLevel)) {
return thisRow;
}
}
}
// not found
return null;
};
Hope this is useful.
What you want to do is access the dataItem of the selected row and see if it contains children, using the FlexGrid's childItemPath that you set.
Here is a working sample: http://jsfiddle.net/banzor/700e6bn2/1/
And here is the code for my selectionChanged event.
$scope.selectionChanged = function(sender, args){
var index = args.row;
var row = args.panel.rows[index].dataItem;
var childPath = sender.childItemsPath;
var children = row[childPath];
if (children && wijmo.isArray(children)) {
console.log("Has items: " + children.length);
}
};

Kendo UI Grid select by data item

I have a Kendo UI Grid with a large datasource and paging.
I have an event that fires where I know the underlying data item that I want to select, but am unsure on how to programatically page/select this item in the grid. If the item is not on the current grid page, I cannot use datasource.view() to poke through when the data is not on the current page.
Does anyone know how I can select an item by its underlying data source object?
I've got a similar situation to where i am at #:
http://jsfiddle.net/Sbb5Z/1050/
I can get the data item with the following:
change: function (e) {
var selectedRows = this.select();
var dataItem = this.dataItem(selectedRows[0]);
}
But then I don't know how to select the same row in the other grid.
Basically in the select event of one grid, I want to go select the same item in another grid. These are not the same datasource, as they have different page setups, but it is the same underlying data array.
I have the data item in the target grid -- but I have no clue how to page/select it in the target grid.
Edit:
The best I've come up with sofar is creating a datasource with the same parameters as the original, and paging through it programatically, until I find what I am looking for. Surely there must be a better way?
I've gotten this back from Telerik, and is a little cleaner:
http://jsfiddle.net/RZwQ2/
function findDataItem(theGrid, dataItem) {
//get grid datasource
var ds = theGrid.dataSource;
var view = kendo.data.Query.process(ds.data(), {
filter: ds.filter(),
sort: ds.sort()
})
.data;
var index = -1;
// find the index of the matching dataItem
for (var x = 0; x < view.length; x++) {
if (view[x].Id == dataItem.Id) {
index = x;
break;
}
}
if (index === -1) {
return;
}
var page = Math.floor(index / theGrid.dataSource.pageSize());
var targetIndex = index - (page * theGrid.dataSource.pageSize()) + 1;
//page is 1-based index
theGrid.dataSource.page(++page);
//grid wants a html element. tr:eq(x) by itself searches in the first grid!
var row = $("#grid2").find("tr:eq(" + targetIndex + ")");
theGrid.select(row);
console.log('Found it at Page: ' + page + 'index: ' + targetIndex);
}
You need to have a common id, or field in the data that you can use to uniquely identify the object in the other dataSource, because the kendo generated UID's are not going to be the same accross two different DataSource instances.
Most generally you define the id in the Model you bound to the grid, which you can use to quickly pluck items from the datasource
change: function (e) {
var selectedRows = this.select();
var dataItem = this.dataItem(selectedRows[0]);
var otherItem = otherGrid.dataSource.get(dataItem.id) // will get
}
if you don't have a common ID field specified in the model, but do know how to find the item you can loop through the data source looking for it
var selectedRows = this.select();
var dataItem = this.dataItem(selectedRows[0]);
var data = otherGrid.dataSource.view();
var otherItem;
for ( var i = 0; i < data.length; i++ ){
if( data[i].myCommonField === dataItem.myCommonField ) {
otherItem = data[i];
break;
}
}
UPDATE:
to select the item in the other grid you need to do this:
var elements = otherGrid.items(),
element;
element = elements.filter("[data-uid='" + otherItem.uid + "']")
otherGrid.select(element) // to select just the one item
//OR
otherGrid.select( otherGrid.select().add(element) ) // to add the item to the current selection
I the fiddle you provided uses a really old version of kendo Grid where this won't work...I just realized. are you stuck on the 2011 version? I can probably get something to work at least in theory but the above will work in the newer versions
essentailly you need to match the item you have to a DOM element, in later versions you can use UID because the dom elements all get that on them "data-uid" it looks like if you at id to your model: { } def you can get the tr elements to have data-id which you can use to select the right select using jquery. I use the items()1 method which also doesn't seem to exist on the early version but you can usegrid2.table.find("tr[data-id=]")` instead I believe
Assume div id will be Grid then first we need find the kendoGrid
var grid = $("#Grid").data("kendoGrid");
then call the grid.select() to select the currently selected one
finally call the grid.dataItem() to get the selected item.
var selectedDataItem = grid.dataItem(grid.select());
To expand upon others, I have a method that takes a single (or multiple) ids to match against:
function selectItems(grid, idAr)
{
if(!idAr instanceof Array)idAr = [idAr];
var items = grid
.items()
.filter(function(i, el)
{
return idAr.indexOf(grid.dataItem(el).Id) !== -1;
});
grid.select(items);
}
* Obviously Id could be replaced by any field that is in your data item.
Use for selection:
selectItems(grid, "5");
selectItems(grid, ["6", "7"]);

Does a documentFragment element reset itself?

This is a strange behavior I noticed. I did not reset a document but immediately used it after I had appended it, and the previous elements it contained were not there.
Should I be clearing it as such?
frag_inner = '';
Are there side-effects I'm not aware of?
frag_outer = NS.createDocumentFragment();
frag_inner = NS.createDocumentFragment();
NS.eachIndex(obj, function(val) {
// fragment element - inner
// image element
val.view_picture = (val.picture === '0') ? this.A.images + 'generic_large.jpg' :
this.A.pictures + val.h_file + '-2.jpg';
img_element = $A.createElement('img');
img_element.className = 'tweet';
img_element.src = val.view_picture;
frag_inner.appendChild(img_element);
// link element
link_element = $A.createElement('a');
link_element.className = 'tweet';
link_element.innerHTML = val.name + ' posted ' +
this.prettyTime(val.time) + '<br>';
frag_inner.appendChild(link_element);
// paragraph element
par_element = $A.createElement('p');
par_element.className = 'tweet';
par_element.innerHTML = val.tweet;
frag_inner.appendChild(par_element);
// div element
div_element = $A.createElement('div');
div_element.className = 'tweet';
div_element.appendChild(frag_inner);
// append the div which is now populated
frag_outer.appendChild(div_element);
}, this);
I think it's actually the expected behaviour, as...
1) it's a well-known feature of Node.appendChild to move existing Nodes. As said in the docs (MDN)...
[Node.appendChild]... adds a node to the end of the list of children of a specified
parent node. If the node already exists it is removed from current
parent node, then added to new parent node.
2) when you append documentFragment, you actually append all its children (MDN again):
Various other methods can take a document fragment as an argument
(e.g., any Node interface methods such as Node.appendChild and
Node.insertBefore), in which case the children of the fragment are
appended or inserted, not the fragment itself.
The point is, when you do someNode.append(documentFragment), you remove all its children from it, then append them to someNode. That's why documentFragment is empty as result.
Note that when you do this...
frag_inner = '';
... you're not clearing the documentFragment stored in this variable - you store a new value (an empty string, obviously) in it instead. The very first attempt to work with it as with documentFragment should result in something like TypeError: Object has no method 'appendChild'.

Categories