Primefaces tree checkbox single selection - checkbox

I'm need primefaces tree in "checkbox" selection mode, but only one and only one node can be selected at a time. By default, primefaces tree selects all descendants when a node is selected and that is actually what I want to change.
Can anybody help me figure it out, please?

I finally found a way to realize this by looking at the Javascript source code of the tree. You can e.g. create a singleton that caches the previously selected node. By using the "onNodeClick" attribute of the tree you can call a Javascript function that unselects the previous node before the widget internally sets the new selected node (and potentially posts the new selection when using ajax events).
Update:
I modified the Facelet and the Javascript to search the tree for a preselected node on initialization. Be aware that the preselected node has to be visible to make it work correctly. See this answer for details about expanding the parent nodes.
Bean:
#Named
#ViewScoped
public class BackingBean implements Serializable {
private TreeNode rootTreeNode;
// IMPORTANT: Use array!
private TreeNode[] selected;
public TreeNode getRootTreeNode() {
if (rootTreeNode == null) {
rootTreeNode = new DefaultTreeNode("Root", null);
// init child tree nodes
}
return rootTreeNode;
}
public TreeNode[] getSelected() {
return selected;
}
public void setSelected(TreeNode[] selected) {
this.selected = selected;
}
}
Facelet:
<p:tree id="tree"
onNodeClick="TREE_SELECTION.updateSelection(node, event)"
propagateSelectionDown="false" propagateSelectionUp="false"
selection="#{backingBean.selected}" selectionMode="checkbox"
value="#{backingBean.rootTreeNode}"
var="data"
widgetVar="treeWidget">
<p:treeNode>
<h:outputText value="#{dataWrapper.label}" />
</p:treeNode>
</p:tree>
Javascript:
<script type="text/javascript">
// singleton for tree selection
var TREE_SELECTION = {
init: function (treeWidgetVar) {
this.treeWidget = PF(treeWidgetVar);
this.selectedNode = this.treeWidget.jq.find('.ui-treenode-selected');
},
treeWidget: null,
selectedNode: null,
updateSelection: function (node, event) {
// get the checkbox state of the clicked node
var checkbox = node.find('> .ui-treenode-content > .ui-chkbox'),
checked = checkbox.find('> .ui-chkbox-box > .ui-chkbox-icon').hasClass('ui-icon-check');
if (checked) {
// checkbox is going to be unchecked
this.selectedNode = null;
return;
}
// check for previously selected node
if (this.selectedNode !== null) {
// get the checkbox of the previously selected node
checkbox = this.selectedNode.find('> .ui-treenode-content > .ui-chkbox');
// update tree
this.treeWidget.uncheck(checkbox);
this.treeWidget.removeFromSelection(this.treeWidget.getRowKey(this.selectedNode));
}
// cache selected node
this.selectedNode = node;
}
};
// initialize singleton when document is loaded
$(function () {
TREE_SELECTION.init('treeWidget');
});
</script>

Related

ExtJs 6 or 7 focus treelist item into view

I have this treelist with a toolbar that I use to create new elements (it's a document list) and I want to focus the newly created item. I can select it but I don't seem to find a way of focusing it:
const selected = documents.treeStore.getAt(documents.treeStore.findExact(
"documentId", current.id
));
if (selected) {
tree.cmp.setSelection(selected);
const node = tree.cmp.getSelection();// maybe redundant
// how do I focus this node into view?
}
If by focus you mean that the element should appear in the visibility range, you can use this method
ensureVisible
const selected = documents.treeStore.getAt(documents.treeStore.findExact(
"documentId", current.id
));
if (selected) {
var path = [];
do {
path.push(selected.getId());
}while (selected = selected.getRefOwner());
tree.cmp.expandPath('/' + path.reverse().join('/'), {focus:true});
}

Add double click event on child node in angular-tree-widget

I am new to angular-tree-widget.js. We need to implement double click event once we click on any tree child node. All I found only two events are supported.
$scope.$on('selection-changed', function (e, node) {
//node - selected node in tree
$scope.selectedNode = node;
});
$scope.$on('expanded-state-changed', function (e, node) {
// node - the node on which the expanded state changed
// to see the current state check the expanded property
//console.log(node.expanded);
$scope.exapndedNode = node;
});
How do I add double click event on a node here? Please help me. Thanks in advance.
You can add ng-dblclick, as #Manikandan suggested, in the following way:
HTML
<body ng-controller="TreeController" ng-dblclick="dblclick($event)">
<tree nodes='treeFamily'></tree>
</body>
JavaScript
controller('TreeController', ['$scope', function ($scope) {
$scope.dblclick = function(evt) {
angular.element(evt.target).toggleClass('red')
}
...
}])
Live Demo
https://plnkr.co/edit/nWfiDA82WDpgRGnLqJUs?p=preview
As you can see, the idea is to subscribe to event on parent element of the tree and distinguish between tree items using evt.target, which is passed to event handler using angular's $event

Change the way JSTree identifies leaf nodes

I want to change the way JSTree identifies leaf nodes. Today, it checks for children property of the node to see if it is boolean true or an array with at least one child node. Reference
I want to create a node property called isLeaf so that jsTree would understand that the node is openable for AJAX loading of the subtree.
I tried to override default is_parent method, like this:
$.jstree.core.prototype.is_parent = function(node) {
return !node.original.isLeaf;
};
But for some reason this method is not called for all tree nodes.
I am using ngJsTree
Does anyone know how can I achieve this behavior?
I managed to deal with it by iterating over AJAX response and changing every node without children to children = true.
success : function(nodeRoot) {
var validateChildrenArray = function(node) {
if (!node.children || node.children.length === 0) {
node.children = true;
}
else {
for (var i = 0; i < node.children.length; i++) {
validateChildrenArray(node.children[i]);
}
}
};
for (var i = 0; i < nodeRoot.length; i++) {
validateChildrenArray(nodeRoot[i]);
}
}
Waiting for a better solution...

extjs treepanel load children without expand

I use Extjs 3.4. I am working on TreePanel with checkbox solution.
What I need is: When I check father node, all childNodes also checked. It's easy, but it depends on extjs expand all childNodes.
If I do
tree.expandAll();
tree.collapseAll();
My check strategy will work, but I don't want the expand effect.
My extjs code(checkchange event) is something like the following:
var checkchange = function(node, flag) {
if (node.hasChildNodes()) {
node.cascade(function(node) {
node.attributes.checked = flag;
node.ui.checkbox.checked = flag;
return true;
});
}
var pNode = node.parentNode;
for (; pNode != null; pNode = pNode.parentNode) {
if (flag || tree.getChecked("checked", pNode).length - 1 == 0) {
pNode.attributes.checked = flag;
pNode.ui.checkbox.checked = flag;
}
}
};
var tree = new Ext.tree.TreePanel({
listeners: {
'checkchange': checkchange
},
})
How can I do? Thank every one for giving advice.
[ EDIT ]
I create A Demo In jsfiddle, that can be easily test.
(Since Extjs 3.4.0 cdn used by jsfiddle not work, I append another cdn extjs resource from https://cdnjs.com/libraries/extjs/3.4.1-1)
I am not sure whether you really want the whole tree to be loaded node by node when checking the root node. I would recommend to lazily check the child nodes when they are loaded for an already-checked parent node, by attaching to the load event. Something like this:
load:function(me, node) {
if(node && node.attributes.checked) node.cascade(
... [function to check all children]
)
}
Depending on your intentions for further processing and your tree size, this could be better than expanding the whole tree to render and check all checkboxes.
If you want the tree to be loaded directly, I would recommend to use preloadChildren:true on the TreeLoader. This is easier than a manual implementation of cascaded load.
I didn't really solve this problem. But I got an eclectic solution.
Only expand when needed
checkchange : function(node, flag){
node.cascade(function(node) {
// when you check, first expand, then child nodes can be checked too
if(node.expanded == false)
node.expand(true);
node.attributes.checked = flag;
node.ui.checkbox.checked = flag;
return true;
});
......
}
This will meet the precondition that all child nodes should have expanded. But also no need expanded when first loaded.
If a parent node checked when first loaded, all child nodes need to be expand
this.treeLoader = new Ext.tree.TreeLoader({
url : 'xxx',
baseParams : {
someparam: ""
},
listeners : {
'load' : function(tree,node,response) {
var res = Ext.util.JSON.decode(response.responseText);
if(res.success != undefined && !res.success) {
Ext.Msg.alert('Hint', res.message);
return;
}
node.cascade(function(node) {
if(node.attributes.checked == true) {
node.expand(true);
}
});
},
}
});
This two methods does solve my prolem though not very perfect.
Hope this can help others.

Component is detached while registration of scope-based event listener is in progress

<form>
...
<button ng-click...>Save</button>
</form>
<overlay ng-if="saving" style="background-color: #ff0000">
<pindicator size="2em" color="red"></pindicator>
</overlay>
void handleOnSaveClicked(final html.Event e) {
_logger.info("Ready to save:",_tempJob);
saving = true;
// Angular starts to register Listener
_proxy.save(tempJob).then((_) {
// Template/Component has already received "detach"
_logger.info("Job saved!");
editable = false;
saving = false;
});
}
...
void detach() {
_logger.info("Detached!");
}
proxy.save saves tempJob into a list, this list is observed by Angular.
If this list changes Angular updates all the UI-Elements attached to the list.
saving=true should show a message but if proxy.save is so fast that Angular has no chance to register a Listener for "saving" (https://github.com/angular/angular.dart/blob/master/lib/core/scope.dart - Line 352). the whole UI-Block is remove while Angular tries to register the Listener.
This leads to
JobEdit: (15:19:43.096) Ready to save:
JobEdit: (15:19:43.119) Job saved!
JobEdit: (15:19:43.123) Detached!
'package:angular/core/scope.dart': Failed assertion: line 352 pos 12: 'isAttached' is not true.
Any hints to avoid this kind of "race condition"?
I haven't tried it but I think to remember that you should use VMTurnZone to execute async operations that affect the model.
class YourComponent implements ScopeAware {
Scope scope;
VmTurnZone zone;
YourComponent(this.zone);
void handleOnSaveClicked(final html.Event e) {
_logger.info("Ready to save:",_tempJob);
saving = true;
// Angular starts to register Listener
zone.run(() {
return _proxy.save(tempJob).then((_) {
// Template/Component has already received "detach"
_logger.info("Job saved!");
editable = false;
saving = false;
});
});
}
}
The main problem is replacing the list item with a new instance of your class (new object)
Angular tests the objects hashCode if you replace a list item.
If the hashCode is different it deletes the according component, but if the hashCode is the same
it updates the vars in your html-file.
Here it also deletes the component and replaces it with the new device:
...
// hashCode of devices[42] changes
devices[42] = new Device(new Location(40.123,10.567));
But this updates! your component:
...
// hashCode of devices[42] stays the same
final Device device = devices[42];
device.location = new Location(40.123,10.567);

Resources