I use tree component in PrimeFaces. In this component, selected nodes is added in selected nodes arrays automatically. However, I need to add partial selected nodes into this selected nodes array. What can I do in this situation, can you help me?
<p:tree id="treCard" value="#{authorizeBean.rootCard}" var="Folder" propagateSelectionUp="false" showUnselectableCheckbox="true" style="border:0px none;background:none; " selectionMode="checkbox" dynamic="true" selection="#{authorizeBean.selectedNodes}">
<p:treeNode class="authorizationPage" expandedIcon="ui-icon-folder-open" collapsedIcon="ui-icon-folder-collapsed" >
<h:outputText value="#{Folder.tag}">
</h:outputText>
</p:treeNode>
<p:treeNode class="authorizationPage" type="page" icon="ui-icon-document">
<h:outputText value="#{Folder.tag}" />
</p:treeNode>
<p:treeNode class="authorizationPage" type="tab" icon="fa fa-bars">
<h:outputText value="#{Folder.tag}" />
</p:treeNode>
<p:treeNode class="authorizationPage" type="button" icon="fa fa-square-o">
<h:outputText value="#{authorizeBean.btnName(Folder.tag)}" />
</p:treeNode>
</p:tree>
you have to create a root node and attach the part that you want to show to your root example create root : root= new DefaultTreeNode( new YourJavaClass(), null);
after that you lined it with your partiel tree : PartielTree = new DefaultTreeNode( new YourJavaClass(), root);
As primeface does not include partial selected node into selected nodes,
so below function is a hack to include partial selected nodes into the selected nodes and It must be called before using selectedNodes array everytime, else selectedNodes will not contain partially selected nodes. (calling everytime because primefaces updates this array on every request- pretty much ;) )
here 'selectedNodes' is a array of TreeNode where primeface framework will be storing selected nodes
public void updatePartialSelectedNodes() {
Set<TreeNode> allSelected = new HashSet<>();
if (selectedNodes != null && selectedNodes.length != 0) {
for (TreeNode sel : selectedNodes) {
allSelected.add(sel);
if (!((yourTreeDataObject)sel.getData()).isSupplier() && sel.getParent().isPartialSelected()) {
allSelected.add(sel.getParent());
}
}
}
selectedNodes = new TreeNode[allSelected.size()];
selectedNodes = allSelected.toArray(selectedNodes);
}
Edit1 : Other option and probably good one if the length of your tree in not greater than 100 nodes and not deeper than 2 level than You can iterate over the tree root node children and check using TreeNode.isSelected() or TreeNodes.isPartialSelected(), as the primefaces framework also update it in the main tree whether the node is selected or partial selected or not selected.
for a two level deep .. an example
for (TreeNode tn : this.treeRoot.getChildren()) {
for (TreeNode child : tn.getChildren()) {
if (child.isSelected()) {
selectionString.append(((TreeNodeObjectDataWrapper)child.getData()).getId());
}
}
}
Related
I have a PrimeFaces (6.2.5) tree, where I have enabled draggable and droppable. I have a few issues with this, and have implemented the drag and drop manually with jquery in stead, while I figure this out;
<div class="plantree" id="realplantree">
<p:tree id="plantree" value="#{curriculumngTreeFacade.root}" var="node" dynamic="false" selectionMode="single" selection="#{curriculumngTreeFacade.selectedNode}" draggable="true" droppable="true">
<p:ajax event="select" listener="#{curriculumngTreeFacade.onNodeSelect}" update="administration-form:curriculummain" oncomplete="initDNDPlan();"/>
<p:ajax event="expand" listener="#{curriculumngTreeFacade.onNodeExpand}" />
<p:ajax event="collapse" listener="#{curriculumngTreeFacade.onNodeCollapse}" />
<p:ajax event="dragdrop" listener="#{curriculumngTreeFacade.onDragDrop}"/>
<p:treeNode expandedIcon="ui-icon ui-icon-folder-open" collapsedIcon="ui-icon ui-icon-folder-collapsed">
<h:outputText value="#{node}"/>
</p:treeNode>
</p:tree>
</div>
My issues are:
I have not added an "update='plantree'" variable to the p:ajax event
for dragdrop. Even still, if I try and drag a parent category to a
sub category, it breaks my tree, because the UI tries to update. I
actually run a check for parent / child connection in the backend
code, and I dont update the tree if that's the case, but in this
case, my method in the backing bean is not even called before the
tree explodes. It does the very same on the PrimeFaces Showcase
Backing code:
public void onDragDrop(TreeDragDropEvent event) throws Exception {
TreeNode dragNode = event.getDragNode();
TreeNode dropNode = event.getDropNode();
int dropIndex = event.getDropIndex();
if (dragNode.getData() instanceof CurriculumCategoryMetaModel && dropNode.getData() instanceof CurriculumCategoryMetaModel) {
CurriculumCategoryMetaModel drop = (CurriculumCategoryMetaModel) dropNode.getData();
CurriculumCategoryMetaModel drag = (CurriculumCategoryMetaModel) dragNode.getData();
if (drop.getType() == drag.getType() && drag.getClassid() > 0) {
drag.setParent(drop);
CurriculumBuilderProvider provider = CurriculumBuilderProvider.getProvider(drop.getType());
provider.saveCategory(drag, UserSessionFacade.getUserLocale());
this.initialize();
expandCategory(root, drop.getClassid(), drop.getType());
} else {
this.initialize();
}
}
FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_INFO, "Dragged " + dragNode.getData(), "Dropped on " + dropNode.getData() + " at " + dropIndex);
FacesContext.getCurrentInstance().addMessage(null, message);
List<String> updates = new ArrayList<>();
updates.add("administration-form:curriculummenu");
updates.add("administration-form:growl");
PrimeFaces.current().ajax().update(updates);
}
private boolean dragIsParentOfDrop(CurriculumCategoryMetaModel drop, CurriculumCategoryMetaModel drag) {
CurriculumCategoryMetaModel parent = drop.getParent();
while (parent != null) {
if (parent.getClassid() == drag.getClassid()) {
return true;
} else {
parent = parent.getParent();
}
}
return false;
}
Is there a way to add accepted nodes that you can drop into. I have 2
root nodes, for different categories, and it is not allowed to drag
and drop between them. In fact, I dont even want the root nodes to be
draggable. On the picture, the "Learning paths" and "Ad hoc" are not supposed to be draggable, since they're only containers for the categories underneath.
My goal is to create a panel where I can drag and drop elements of two different types: button and textfield. All of the elements are stored in a TreeMap and are iterated over with JSTL <c:forEach> tag. I can add extra components and remove the most recently selected ones thorough dedicated p:commandXxx fields outside of the viewPanel. Once draggable components are in the viewPanel, I can drag and drop them. Through javascript, I'm appending top and left coordinates of a particular component before the drop action to update component's location is invoked. The delete commandButton should remove the selected element from the list and trigger the update of the viewPanel.
Problem: Deleting any component except the most recently added fails to update the viewPanel resulting in the following exception:
Severe: Error Rendering View[/developer/testDr.xhtml]
org.primefaces.expression.ComponentNotFoundException: Cannot find >component for expression "button-" referenced from "defStep-10:j_idt13"..
It appears that in render time draggable's for attribute evaluates #{child.idstepNode} to null. Does anyone know why this happens, or how to circumvent this?
Interestingly, when Delete button is clicked again or the whole page is refreshed, viewPanel renders without a hitch.
<h:form id="viewPanel" class="default-step droppable" style="width:500px;height:500px;background:green;">
<p:droppable for="viewPanel" tolerance="touch" activeStyleClass="ui-state-highlight" >
<p:ajax listener="#{stepUtility.onDrop}" update="viewPanel" />
</p:droppable>
<c:forEach items="${stepUtility.stepNodesMap}" var="child">
<c:if test="${child.value.elementType == 'BUTTON'}">
<p:outputLabel id="button-${child.value.idstepNode}" style="top:${child.value.top};left:${child.value.left};
background-color: beige;position:absolute;" value="button ${child.value.elementValue}"/>
<p:draggable for="button-${child.value.idstepNode}"/>
</c:if>
<c:if test="${child.value.elementType == 'TEXT'}">
<p:outputLabel id="text-${child.value.idstepNode}" style="top:${child.value.top};left:${child.value.left};
background-color: beige;position:absolute;" value="text ${child.value.elementValue}"/>
<p:draggable for="text-${child.value.idstepNode}" />
</c:if>
</c:forEach>
</h:form>
//...
<p:commandButton value="Delete" actionListener="#{stepUtility.removeStepNode()}"
update=":viewPanel"/>
stepUtility bean
#Named(value = "stepUtility")
#SessionScoped
public class StepUtility implements Serializable {
private Integer selecteNode;
public void setSelectedElement(Integer idstepNode) //'StepNode')
{
selecteNode=idstepNode;
}
public void addNewComp(String typeNode)
{
if(stepNodesMap == null)
stepNodesMap = new TreeMap<>();
StepNodeSimple snode = new StepNodeSimple();
Integer id = 0;
if(stepNodesMap.size()>0)
{
Integer lastNodeId = ((StepNodeSimple) stepNodesMap.lastEntry().getValue()).idstepNode;
id = lastNodeId+1;
}
snode.idstepNode = id;
snode.elementType = typeNode;
snode.elementValue = typeNode +"value";
snode.left = "2px;";
snode.top = "2px;";
stepNodesMap.put(id, snode);
}
NavigableMap stepNodesMap;
public NavigableMap getStepNodesMap() {
return stepNodesMap;
}
public void setStepNodesMap(NavigableMap stepNodesMap) {
this.stepNodesMap = stepNodesMap;
}
public void removeStepNode()
{
if(selecteNode!=null)
{
stepNodesMap.remove(selecteNode);
}
}
public void onDrop(DragDropEvent dragDropEvent)
{
String dragId = dragDropEvent.getDragId();
Map<String, String> params = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap();
String left = params.get(dragId + "_left");
String top = params.get(dragId + "_top");
Integer idstepNode = Integer.parseInt(dragId.substring(dragId.lastIndexOf("-")+1));
setSelectedElement(idstepNode);
StepNodeSimple element = (StepNodeSimple) stepNodesMap.get(selecteNode);
element.left = left+"px;";
element.top = top+"px;";
}
}
I am working with Primefaces DataTable component. The user makes some city-district-service type choices before hitting the button and the datatable gets filled with the needed urban services (hospital, schools etc.), located at the needed spots of the city. The below code contains only the fetching button and the table itself.
When you fetch the first results you want, there isn't any problem. All is good, paginator works well. The problem starts when you filter the results by name. They actually get filtered very well too, BUT after a filtering is done; if you make other choices about the city, district or service and try fetching them; the columns of datatable gets filled with '| 0 | (empty) | (empty) |' rows (I guess its because the ID column is of type int and the other two are strings). Additional info: the number of the newly created empty rows are exactly equal to the former filter results.
I made the necessary debugging and found that at the backend everything is perfect. I get all my fetched objects. Actually when I type anything in the filter bar the new results become exposed too. It seems to be a problem totally in the client side rendering, though I couldn't find a way to solve it.
Any help would be greatly appreciated.
Update: After working on it for several more hours, I concluded that the reason might not be the filter but the paginator. Because when I turn off the paginator and get a full scrollable table, everything works perfectly well. Still not sure about the exact reason, so I edited the question subject according to that.
p.s: I'm using Primefaces 5.3, JSF 2.2 Mojarra, JDK 1.7, TomEE Plume
<p:commandButton action="#{filterByLocation.fetchServicesByLocation}" id="elbuton" value="Fetch" icon="ui-icon-check" onclick="PF('servisWidget').clearFilters();" update="servisList" />
<p:dataTable id="servisList" paginator="true" paginatorTemplate="{CurrentPageReport} {FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink} {RowsPerPageDropdown}" rowsPerPageTemplate="5,10,15" rows="10" var="loc" value="#{filterByLocation.finalResults}"
widgetVar="servisWidget">
<p:column headerText="ID">
<h:outputText value="#{loc.id}" />
</p:column>
<p:column headerText="NAME" filterBy="#{loc.name}" filterFunction="#{filterByLocation.filterResults}">
<h:outputText value="#{loc.name}" />
</p:column>
<p:column headerText="SERVICE NAME">
<h:outputText value="#{loc.serviceName}" />
</p:column>
</p:dataTable>
My backing bean (getters/setters excluded for clarity):
#ManagedBean
#ViewScoped
public class FilterByLocation {
private List<LocationEntity> cities;
private List<LocationEntity> districtSelection;
private List<LocationEntity> finalResults = new ArrayList<LocationEntity>();
private List<Integer> selectedNodes = new ArrayList<Integer>();
private String filterText;
private String filterName;
private int city;
private int district;
public FilterByLocation() {
setCities(LocationTreeDAO.fetchCities());
}
public void listDistricts() {
setDistrictSelection(LocationTreeDAO.fetchDistricts(city));
}
public void selectNode(NodeSelectEvent node) {
TreeNode treeNode = node.getTreeNode();
LayerEntity layer = (LayerEntity) treeNode.getData();
selectedNodes.add(Integer.valueOf(layer.getId()));
}
public void unselectNode(NodeUnselectEvent node) {
TreeNode treeNode = node.getTreeNode();
LayerEntity layer = (LayerEntity) treeNode.getData();
selectedNodes.remove(Integer.valueOf(layer.getId()));
}
public void fetchServicesByLocation() {
setFinalResults(LocationTreeDAO.fetchFilteredServices(getCity(), getDistrict(), getSelectedNodes(), getFinalResults()));
setFilterText("");
}
public boolean filterResults(Object value, Object filter, Locale locale) {
setFilterText((filter == null) ? null : filter.toString().trim());
if (filterText == null || filterText.equals("")) {
return true;
}
if (value == null) {
return false;
}
String searched = value.toString().toUpperCase();
filterText = filterText.toUpperCase();
if (searched.contains(filterText)) {
return true;
} else {
return false;
}
}
Two screenshots depicting the situation
before
after
The filterFunction parameter of
<p:column ... filterFunction="#{filterByLocation.filterResults}" ...> is not necessary.
Change the statement to
<p:column headerText="NAME" filterBy="#{loc.name}" filterMatchMode="contains">
Add a List for the filtered values
<p:dataTable ... filteredValue="#{filterByLocation.filteredResults} ...>
#ManagedBean
#ViewScoped
public class FilterByLocation {
...
private List<LocationEntity> filteredResults;
...
}
This question already has an answer here:
What event should be used on a p:tree to select a tree node and have a context menu?
(1 answer)
Closed 6 years ago.
I'm creating a PrimeFaces (5.3) tree with a context menu. Selected nodes should be stored in #{myBean.selectedNode}. When I select a node using the left mouse button the correct node is set. But, when I try to run an action on a node from a context menu, without selecting it first, the correct node isn't set (the setter in my bean is not called).
I'm following the example in the PrimeFaces showcase, and I suppose I've got everything lined up. What am I doing wrong?
As you can see, in the PrimeFaces showcase you are able to immediately right click a node, click "View", and the growl will display the correct node.
Bean
I don't think the bean code is relevant (it is ViewScoped and there is a private TreeNode selectedNode with getter and setter).
Here are the interesting bits though:
public void onNodeSelect(NodeSelectEvent event) {
MyTreeNode myTreeNode = (MyTreeNode) event.getTreeNode();
myController.setSelected(myTreeNode.getEntity());
}
public void addChild(String name) {
MyTreeNode myTreeNode = (MyTreeNode) selectedNode;
MyTreeNode childNode = myTreeNode.addChild(name);
myController.setSelected(childNode.getEntity());
myController.insert();
}
XHTML
<h:form id="mainForm">
<p:tree value="#{myBean.root}" var="node"
id="myTree" dynamic="true"
selectionMode="single" selection="#{myBean.selectedNode}">
<p:treeNode expandedIcon="ui-icon-folder-open" collapsedIcon="ui-icon-folder-collapsed"
type="myType">
<h:outputText value="#{node}"/>
</p:treeNode>
<p:ajax event="select" listener="#{myBean.onNodeSelect}" />
</p:tree>
<p:contextMenu for="myTree">
<p:menuitem action="#{myBean.addChild('new')}"
value="Add"
process="#this"
update=":mainForm:myTree"/>
</p:contextMenu>
</h:form>
hy,
is a bug in primefaces.
You can fixed this issue by youself. For it you neccessary add verification in the primefaces js file which mouse button has been pressed. You can override file primefaces.js in your project, find first call function selectNode(d) and add follow checking:
if (e.which == 1) {
this.selectNode(d)
this.cursorNode = d;
}
This is part of code you can find then function nodeClick: function (e, a) is called:
nodeClick: function (e, a) {
PrimeFaces.clearSelection();
if ($(e.target).is(":not(.ui-tree-toggler)")) {
var d = a.parent();
if (this.cfg.onNodeClick) {
this.cfg.onNodeClick.call(this, d)
}
if (a.hasClass("ui-tree-selectable") && this.cfg.selectionMode) {
var c = this.isNodeSelected(d), f = e.metaKey || e.ctrlKey, b = e.shiftKey;
if (this.isCheckboxSelection()) {
this.toggleCheckboxNode(d)
} else {
if (c && f) {
this.unselectNode(d)
} else {
if (this.isSingleSelection() || (this.isMultipleSelection() && !f)) {
this.unselectAllNodes()
}
if (this.isMultipleSelection && b) {
} else {
if (e.which == 1) {
this.selectNode(d);
this.cursorNode = d;
}
}
}
}
}
}
}
It turns out you need the undocumented contextMenu Ajax event listener.
See:
What event should be used on a p:tree to select a tree node and have a context menu?
I am still working on with the primefaces component p:orderlist.
I have managed to implement a drag and drop behaviour (to put items from another list in to the p:orderlist) and I also make the p:commandButton work inside the p:orderlist.
Now I have a new problem. You can drag and drop items inside the p:order list, to give them a new order. (That is, why the component is called orderlist, nothing to do with a shopping order ;-) ).
But if I reorder my items I get no notification or event, which tells my bean, that something was changed.
Has anybody an idea how to make this possible?
Here is my p:orderlist, like it looks know:
<h:panelGroup>
<p:remoteCommand name="removeTechniker" actionListener="#{systemlandschaftRessourceHandler.removeTechnikerByRemoteCommand}"
out="technikersTable" update="#form"/>
<p:orderList id="technikersTable"
value="#{systemlandschaftRessourceHandler.entity.technikers}"
var="_techniker"
itemValue="#{_techniker}"
converter="#{entityConverter}"
controlsLocation="none">
<f:facet name="caption">Techniker</f:facet>
<p:column>
<p:commandButton id="deleteTechnikerFromListButton"
styleClass="colButton"
icon="ui-icon-trash"
type="button"
onclick="removeTechniker([{name:'id', value:'#{_techniker.id}'}]);"
update="#form"/>
</p:column>
<p:column style="width:75%;" id="outTech">
<p:outputLabel value="#{_techniker.verantwortlich.displayName}"/>
</p:column>
</p:orderList>
<p:droppable id="technikerDrop"
for="technikersTable"
tolerance="touch"
activeStyleClass="ui-state-highlight"
datasource=":systemLandschaftTabView:sysObjektDetailPanelForm:userTable"
scope="userDraggable">
<p:ajax listener="#{systemlandschaftRessourceHandler.onDropTechniker}" update="#form" />
</p:droppable>
</h:panelGroup>
I already found something on the primefaces community, but this doesn't work, can' even say why.
Link: http://forum.primefaces.org/viewtopic.php?f=3&t=26539
Regards
LStrike
Just for info, here are my solved items, regarding the p:orderlist:
Primefaces: CommandButton inside Orderlist not working
and
Primefaces: Orderlist: index out of bound exception while reordering
Figured out a solution myself.
It's some kind of hack, but it works.
I use the setter of the list, I want to have sorted and send it to the database. For reading the list I use the #OrderBy annotation on the getter.
#OrderBy("ranking ASC")
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name="MWEB_SYSLAND_RES_ENTWUSER_L", joinColumns = {#JoinColumn(name = "ID_SYSLAND_RES", nullable = false, updatable = false)} ,inverseJoinColumns = { #JoinColumn(name ="ID_SYSLAND_USER", nullable = false, updatable = false) })
public List<SystemlandschaftUser> getEntwicklers() {
return entwicklers;
}
public void setEntwicklers(List<SystemlandschaftUser> entwicklers) {
this.entwicklers = sortSysUserList(entwicklers);
#Transient
private List<SystemlandschaftUser> sortSysUserList(List<SystemlandschaftUser> input){
if(input != null && input.size() > 0){
for(int i=1, size=input.size(); i <= size; i++){
SystemlandschaftUser t = input.get(i-1);
t.setRanking(i);
}
}
return input;
}