I was trying to use dynatree with lazy loading on in my project.
When I tried to combine the select functionality with the checkboxes and select mode 3 I was disappointed to see that the select rule for mode 3 which is select everything including children children .... children when the parent is selected.
This is because the children havent yet been loaded.
Does anyone have a workaround to get this working ? I would very much appreciate any suggestions.
Zank ya!!
When adding the children of the parent you selected, check if the parent has children. If TRUE, add each child and set each of those children to selected.
Here is some code below. The onLazyRead will go up top. Every time you click on a lazy node, this function will trigger. Inside this function should be a call to your function that fetches child data for the node you just selected.
The code below that is the way I solved this problem. Pretty much all it is doing is checking if the parent of the node you are adding is selected. If TRUE, you add the node, then .select() it.
This is much simpler when deselecting a node because all the nodes are already loaded. When deselecting, just go down the hierarchy of the tree from the node that is being deselected and simply un-check each node.
I know this is a lot of code to just throw at you, but hopefully you can grasp the idea out of it. If you cannot, I will try to check back to this thread during work. Maybe you can even post what you have so far?
onLazyRead: function(node){
jQuery("#tree2").dynatree("getTree").disable();
var pParentID = node.data.key;
//Select the Node
doChildReport(pParentID); //Get Children for this node's ID
},
///....
//Methods to grab data from a "XMLHttpRequest GET" go here
//....
//When you finally want to add the children that you fetched using the ID of the node you selected...
//treeArray is an array of node data that has been parsed out of a
//string returned by a "XMLHttpRequest GET"
//Contents of array in order, repeating: treeArray[0] = ParentID, [1] = nodeID [2] = nodeName
//Example, the array would return [111], [222], ["Child Node"]
if(){ //IF Next fetched node is on the last level, ie. no children
//add normally
}
else{ //If NOT, add lazy.
if(treeArray[1] != "nill" && treeArray[1] != undefined){
//IF THE PARENT NODE IS SELECTED
if(jQuery("#tree2").dynatree("getTree").getNodeByKey(treeArray[0]).isSelected() == true){
//AND IF the child node does not exist
if(jQuery("#tree2").dynatree("getTree").getNodeByKey(treeArray[1]) == null){
//Add the child node and then mark it selected
addChildNodeLazy(treeArray[1], treeArray[2], treeArray[0]);
jQuery("#tree2").dynatree("getTree").getNodeByKey(treeArray[1]).select();
}
}else{
if( jQuery("#tree2").dynatree("getTree").getNodeByKey(treeArray[1]) == null){
addChildNodeLazy(treeArray[1], treeArray[2], treeArray[0]);
}
}
}
}
Lazy load function...
function addChildNodeLazy(NodeID, NodeName, ParentID){
jQuery("#tree2").dynatree("getTree").getNodeByKey(ParentID).addChild({title: NodeName, key: NodeID, icon: false, isFolder: true, isLazy: true});
}
Admittedly, the following is a bit of a hack...but it solves this problem:
onSelect: function (flag, node) {
if (flag && node.childList == undefined) {
node.reloadChildren(function() {
node.select(false);
node.select(true);
});
}
If node is being selected (flag == true) AND the node has not been loaded yet (childList == undefined) then call reloadChildren with a callback function. The callback runs after the data is loaded and simply toggles the checkbox off/on. This causes all the child nodes (which now exist) to be selected.
Related
I am working on a polymer2 shadow dom template project need to select children elements from parent elements. I found this article introduces a way to select child shadow dom elements that like this:
// No fun.
document.querySelector('x-tabs').shadowRoot
.querySelector('x-panel').shadowRoot
.querySelector('#foo');
// Fun.
document.querySelector('x-tabs::shadow x-panel::shadow #foo');
However, when I tried in my polymer2 project, like this:
//First: works!!
document.querySelector('container')
.shadowRoot.querySelector('app-grid')
.shadowRoot.querySelector('#apps');
//Second: Doesn't work!// got null
document.querySelector('container::shadow app-grid::shadow #apps')
// Thrird: document.querySelector('* /deep/ #apps') // Doesn't work, got null
I really need the second way or the third, which to put selectors in (), but both couldn't work. Does anyone know why the second one doesn't work? Thank you so much!
::shadow and /deep/ has never(?) worked in Firefox, and is depraved in Chrome 63 and later.
Source
Eric Biedelman has written a nice querySelector method for finding all custom elements on a page using shadow DOM. I wouldn't use it myself, but I have implemented it so I can "querySelect" custom elements in the console. Here is his modified code:
// EXAMPLES
// findCustomElement('app-grid') // Returns app-grid element
// findCustomElements('dom-if') // Returns an array of dom-if elements (if there are several ones)
// findCustomElement('app-grid').props // Returns properties of the app-grid element
function findCustomElement(customElementName) {
const allCustomElements = [];
customElementName = (customElementName) ? customElementName.toLowerCase() : customElementName;
function isCustomElement(el) {
const isAttr = el.getAttribute('is');
// Check for <super-button> and <button is="super-button">.
return el.localName.includes('-') || isAttr && isAttr.includes('-');
}
function findAllCustomElements(nodes) {
for (let i = 0, el; el = nodes[i]; ++i) {
if (isCustomElement(el)) {
el.props = el.__data__ || el.__data || "Doesn't have any properties";
if (customElementName && customElementName === el.tagName.toLowerCase()) {
allCustomElements.push(el);
} else if (!customElementName) {
allCustomElements.push(el);
}
}
// If the element has shadow DOM, dig deeper.
if (el.shadowRoot) {
findAllCustomElements(el.shadowRoot.querySelectorAll('*'));
}
}
}
findAllCustomElements(document.querySelectorAll('*'));
if (allCustomElements.length < 2) {
return allCustomElements[0] || customElementName + " not found";
} else if (customElementName) {
allCustomElements.props = "Several elements found of type " + customElementName;
}
return allCustomElements;
}
Remove the if (isCustomElement(el)) { statement, and you can querySelect whatever element and get an array of it if several of them exists. You can change findAllCustomElements to implement a smarter querySelect using the recursive loop on shadowDoom as base. Again, I wouldn't use this myself – and instead pass on variables from parent element(s) to children where the children have observers that activates specific behaviors – but I wanted to give you a general implementation of a fallback if nothing else works.
The problem with your question is that you don't give any specifics about WHY you want to select the children in the first place.
I have a vaadin-checkbox:
<vaadin-checkbox id=[[item.id]] disabled="true" checked="[[item.checked]]">[[item.description]]</vaadin-checkbox>
I defined my properties:
static get properties() {
return {
items: {
type: Array,
notify: true,
value: function() {
return [];
}
}
};
}
When I now change the data by pressing some button:
_selectItem(event) {
const item = event.model.item;
if (item.checked === true) {
this.$.grid.deselectItem(item);
} else {
this.$.grid.selectItem(item);
}
item.checked = !item.checked;
}
The state of the checkbox is still checked="true". Why isnt the checkbox getting updated? The same thing when I change the description of the item:
_selectItem(event) {
event.model.item.description = 'test';
}
The test description is never appearing. The checkbox is never getting updated.
The reason why the checkbox does not get updated by the button click handler is in the Polymer 2 data system. Polymer does not detect the change and does not update the template accordingly.
In order to make the change in a way that Polymer would detect it you have two options:
Use this.set(`items.${event.model.index}.checked`, !item.checked) if you can reliably assume that the index used by dom-repeat always matches that elements's index in the items array (it is not the case if you use sorting or filtering features of dom-repeat). See an example here https://jsfiddle.net/vlukashov/epd0dn2j/
If you do not know the index of the updated item in the items array, you can also use the Polymer.MutableData mixin and notify Polymer that something has changed inside the items array without specifying the index of the changed item. This is done by calling this.notifyPath('items') after making a change. However, this requires that your element extends the Polymer.MutableData mixin, and that dom-repeat has the mutable-data attribute set. See an example here: https://jsfiddle.net/vlukashov/epd0dn2j/24/
More information on this in the Polymer 2 docs.
I need some tree structure in Aurelia. I got some link for that. It's working fine. But My requirement is like accordion with tree view. Means when I clicked on closed parent all opened parents should close and clicked one should open same as bootstrap accordion. same thing should happen When ever I clicked on child parent element with in parent repeat.
Below is my image for tree structure.
Gist run Link: Gist
The above gist is just tree structure with open and collapse. From that when I clicked on closed tree node , that should open and remaining tree nodes should be closed.
In the above gist "node-model.js" is having events for open and close. So when ever I clicked on icon the clicked event inside this variable will get only clicked node. How can I get other node in that method to hide.
Answer:
Inside your tree-view.js, add the following code (3 methods):
attached() {
window.addEventListener('goCollapseAll', (e) => {
this.closeOtherBranches(e.detail);
}, false);
}
closeOtherBranches(exceptNode) {
// traverse node tree to find current one
var found = null;
for(var i = 0; i < this.nodes.length; i++){
if (this.subSearch(this.nodes[i], exceptNode)) {
found = i;
}
}
if (found !== null) {
for(var i = 0; i < this.nodes.length; i++){
if ((i != found) && (this.nodes[i].expanded)) {
this.nodes[i].toggleNode();
}
}
}
}
subSearch(node, findNode) {
// recursive search of tree for findNode
var match = null;
if (node === findNode) {
match = node;
} else {
for(var i = 0; i < node.children.length; i++){
if (node.children[i] === findNode) {
match = node;
} else {
match = this.subSearch(node.children[i], findNode);
}
}
}
return match;
}
Then, inside your node-model.js, add the following lines at the beginning of toggleNode():
// close other node branches
if (!this.expanded) {
var event = new CustomEvent('goCollapseAll', { 'detail': this });
window.dispatchEvent(event);
}
Explanation:
When a node is expanded, it publishes a custom event to trigger the recursive search to close all nodes that are part of a different branch. It's not the prettiest solution and I think there might be a cleaner way if you adopt a different structure for the tree, but this solution definitely works well and accomplishes your purpose.
GistRun:
I've updated your GistRun to demonstrate the functionality. You can see it working here:
https://gist.run/?id=828c3c79bff0dfbaffec3252ed376c8c
I have implemented a dijit tree with checkboxes as per the implementation provided on http://jsfiddle.net/5QcFY/14/ and that is working perfectly fine. I am displaying the tree on dialogue box. There are "categories" as parent nodes and "types" as the children nodes. Initially all the parents nodes are displayed collapsed. Once user selects options from children nodes from tree and closes the dialogue box, the selected items are getting passed to the further processing logic.
Below is my implementation:
typeTreeHandle = new dijit.Tree({
store: dataStore,
id: "docTree",
showRoot: false,
autoExpand: false,
_createTreeNode: function(args) {
var tnode = new dijit._TreeNode(args);
tnode.labelNode.innerHTML = args.label;
//As parent nodes don't need checkboxes
if (!args.isExpandable)
{
var cb = new dijit.form.CheckBox();
cb.set('checked', false);
cb.placeAt(tnode.labelNode, "first");
//To store reference of checkbox object to destroy afterwards.
checkBoxArr.push(cb);
dojo.connect(cb, "onChange", function() {
var treeNode = dijit.getEnclosingWidget(this.domNode.parentNode);
dojo.publish("/checkbox/clicked", [{
"checkbox": this,
"item": treeNode.item}]);
});
}
return tnode;
}
}, "myTree");
Now the issue is: when the user again open the dialog box, the previously expanded categories are still shown expanded. Because of which the loading of elements of tree gets slower. Even if I close the browser window and open again, I can still see the categories previously expanded. I tried destroying the references of the objects created for the checkboxes, but still the problem continues.
Any pointer related to this issue would be really appreciated.
The persist property is what you are looking for, it seems:
Enables/disables use of cookies for state saving.
By default, a Tree will remember which branches were opened/closed. To
use this feature you must specify an id for the Tree. To disable the
feature, set the “persist” parameter to false.
http://dojotoolkit.org/reference-guide/1.10/dijit/Tree.html#persistence
Somewhat confusingly, the API docs have the following to say about the autoExpand property (which I see you've tried already):
Fully expand the tree on load. Overrides persist.
It appears this is only true for autoExpand: true. Using persist: true (which is the default) will override autoExpand: false too, as you have seen.
http://jsfiddle.net/5QcFY/336/
I would like to create an event listener to detect when my nestedlist is at the top level, and then hide a component on the page. For example:
onNestedlistActiveItemChange: function(container, value, oldValue, options) {
if (this.getMyNestedList().atLevel(0)) <-- seudo code, does not work
{
Ext.getCmp('myButton').hide();
}
}
Thanks in advance for your help
The _backButton._hidden property of the nested list will return true when the default back button is hidden and false when the button is displayed. The button is hidden only when the nested list is at the top level.
The trick is to use the nested list's "back" event, which returns the state of the toolbar after the back event has occurred:
onMynestedlistBack: function(nestedlist, node, lastActiveList, detailCardActive, options){
if(nestedlist._backButton._hidden) {
Ext.ComponentQuery.query('#myButton')[0].hide();
}
}
That is, if the nested list's back button is hidden, then the list is at the top level, so hide myButton.
There are certainly more rigorous ways to do it, but most of them would involve overriding the toolbar's default button handling.
You can get the list level by checking the index of the list
var idx = nestedlist.getInnerItems().indexOf(list);
if (idx === 0) {
// top level...
}
[UPDATE]
You can also check the depth level of the record
var depth = record.getData().depth;
if (depth === 1) {
// top level...
}