Primefaces tree with checkbox mode - setting multiple child nodes from Managed Bean - jsf

I want to set multiple child nodes under same parent to be selected or check marked. I saw this answer: https://stackoverflow.com/a/17025086/1297935 and tried the following and it does not work:
#ManagedBean
#ViewScoped
public class BackingBean extends GenericViewBean {
private TreeNode[] selectedNodes;
public void showChildSelected(){
...
...
selectedNodes = new TreeNode[]{p1child1,p1child2}; //two child nodes of same parent added
...
...
for(TreeNode treeNode : selectedNodes){
treeNode.setSelected(true);
}
RequestContext.getCurrentInstance().update("listView:tree");
}
}
Above code only shows the last child selected / check marked. Is there any Solution to select multiple child nodes of the same parent from Managed Bean?
Thanks in advance.

I think it seems to be some bug in Primefaces as the TreeNode[] selectedNodes in the Managed Bean properly shows 2 selected nodes but in the Javascript object PrimeFaces.widgets.treeWidget.selections array shows only one rowKey. This happens only when 2 children of the same parent are selected. And even after manually selecting the treenode using setSelected(true) in Managed Bean there is no change in Behaviour.
Currently i am using a Javascript workaround posted by alexSunder: https://stackoverflow.com/a/25716716, in his post he is using PrimeFaces.widgets.treeWidget.selectNode(node,silent) but i had a look at the tree.js and got another method for checkboxes: PrimeFaces.widgets.treeWidget.toggleCheckboxNode(node). Following is the change i did and it is working!!
#ManagedBean
#ViewScoped
public class BackingBean extends GenericViewBean {
private TreeNode[] selectedNodes;
public void showChildSelected(){
...
...
selectedNodes = new TreeNode[]{p1child1,p1child2}; //two child nodes of same parent added
...
...
for(TreeNode treeNode : selectedNodes){
StringBuilder sb = new StringBuilder();
sb.append("PrimeFaces.widgets.treeWidget.toggleCheckboxNode(");
sb.append("$(\"#listView\\\\:tree\\\\:");
sb.append(treeNode.getRowKey());
sb.append("\"))");
RequestContext.getCurrentInstance().execute(sb.toString());
}
RequestContext.getCurrentInstance().update("listView:tree");
}
}
Method Description:
PrimeFaces.widgets.treeWidget.selectNode(node,silent): silent boolean parameter when passed as false, will not fire the Node Select Event, but does not change checkbox state, only selects/highlights the Node.
PrimeFaces.widgets.treeWidget.toggleCheckboxNode(node): check box state is toggled along with selecting of Node and then Node Select Event is fired.

Related

How to specify a backing bean callback on click on a menu item?

I'm building the content of a p:slideMenu by binding the value to a MenuModel in a backing bean. This is necessary because the content is generated dynamically based on the result of a database query. Using
#Named
#ViewScoped
public class BackingBeanView0 implements Serializable {
private static final long serialVersionUID = 1L;
private MenuModel menuModel = new DynamicMenuModel();
#PostConstruct
private void init() {
DefaultMenuItem menuItem = new DefaultMenuItem("Click me!",
null, //icon
"/index.xhtml" //url
);
menuItem.setCommand("#{backingBeanView0.onMenuItemClick('Hello world!')}");
menuItem.setImmediate(true);
menuModel.addElement(menuItem);
}
[getter and setter for menuModel]
public void onMenuItemClick(String message) {
System.out.println(BackingBeanView0.class.getName()+" message: "+message);
}
}
as recommended by #Melloware (this does not show the need to create the model in the backing bean) causes backingBeanView0.onMenuItemClick to be not invoked
to be displayed with a delay for a few seconds. Moving the wanted backing bean method to a view scoped bean doesn't change this behavior.
The onXXX properties for Javascript callbacks on DefaultMenuItem can't be used to trigger a method in a backing bean afaik. I noticed that the command property in DefaultMenuItem isn't used in the Primefaces source code and it is not documented in the Primefaces 6.2 user guide.
I'm providing a SSCCE at https://gitlab.com/krichter/primefaces-menuitem-bean-callback. It doesn't contain more information than the MCVE above and merely exists to ease the investigation of the problem.
I'm using Primefaces 6.2.
I think I know what you are asking. In the example below, I call the bean controller method myController.changeAccount and I also provide an OnComplete Javascript callback as if I built the menu in XHTML.
final DefaultMenuItem item = new DefaultMenuItem(bean.getLongName());
item.setCommand("#{myController.changeAccount('" + bean.getShortName() + "')}");
item.setImmediate(true);
item.setOncomplete("melloware.handleAccountChange(xhr, status, args);");
Change:
DefaultMenuItem menuItem = new DefaultMenuItem("Click me!",
null, //icon
"/index.xhtml" //url
);
To:
DefaultMenuItem menuItem = new DefaultMenuItem("Click me!");
You cannot combine a "URL" parameter and an Action command in the same menuitem it uses the URL first. If you need to send to a new location have your Command simply return that as a string and you will navigate to that page for example:
public String onMenuItemClick(String message) {
System.out.println(BackingBeanView0.class.getName()+" message: "+message);
return "/index.xhtml";
}

p:picklist gets cached although new values are set

At this moment I've got a p:picklist which uses DualListModel<String>. The picklist has to be updated whenever a new entity is selected because the list of attributes changes depending on each entity.
But the values with which the picklist gets populated at first will be persisted even if onEntitySelect() gets called again, where I use setPicklistAttributes() to change the whole DualListModel.
I put sysouts inside of the setter and the right values get set by onEntitySelect(), but almost instantly the setter is being called again with the old values and my changes are overwritten.
Why does this happen? Is my way of resetting the DualListModel inside of onEntitySelect() wrong?
<p:picklist>:
<p:pickList id="pickList" value="#{dashboardBean.picklistAttributes}" var="attribute"
itemLabel="#{attribute)}"
itemValue="#{attribute}"/>
onEntitySelect():
public void onEntitySelect() {
currentAttributes = currentEntity.getAttributes();
List<Attribute> attributesSource =
currentAttributes.stream()
.sorted(this::compareLocalizedAttributeNames)
.collect(Collectors.toList());
setPicklistAttributes(new DualListModel<Attribute>(attributesSource, new ArrayList<Attribute>()));
}
Initialize DualListModel on init method which has #PostConstruct annotation. And instead of creating new DualListModel in each onEntitySelect set existing DuaListModels source and target with new ones. Here is a sample:
private DualListModel<Attribute> attributes;
#PostConstruct
public void init() {
// initialize service picklist
List<Attribute> source = new ArrayList<Attribute>();
List<Attribute> target = new ArrayList<Attribute>();
attributes= new DualListModel<Attribute>(source , target );
}
public void onEntitySelect() {
// create your newAttributesSoruce and Target here then set to existing DualistModel
attributes.setSource(newAttributesSource);
attributes.setTarget(newAttributesTarget);
}

Disable one checkbox out of many in <h:selectManyCheckbox where checkboxes come from a LinkedHashMap [duplicate]

I need your help in disabling and enabling an item from the selectManyCheckbox component in a jsf page. First of all, the selectManyCheckbox component is showing three chechboxes which are (Loan - Health - Transfer). The list will be populated from a bean which it has the code:
private List<hrCertificate> hrCertificatesList = new ArrayList<hrCertificate>();
//Getter and Setter
Private String loanFlag="";
#PostConstruct
public void init() {
this.hrCertificatesList.add(new hrCertificate(("Loan"), "LC"));
this.hrCertificatesList.add(new hrCertificate(("Health"), "HI"));
this.hrCertificatesList.add(new hrCertificate(("Trasnfer"), "TE"));
}
In the same bean, I will be running a SQL statement that will return either Yes or No and that value I am adding it to the loanFlag variable.So if the flag="Y", I need to enable the loan checkbox so the user can select it else I need to disable it from the selectManyCheckbox. The issue is that I am facing difficulties in applying the logic to disable and to enable the item selectManyCheckboxwhere in the above code I am listing and enabling them all the time.
The code for the selectManyChexkbox:
<p:selectManyCheckbox id="hrCertificates" value="#{user.selectedHRCertificates}" layout="pageDirectio>
<f:selectItems value="#{user.hrCertificatesList}"
var="hrCertificate" itemLabel="#{hrCertificate.hrCertificateName}"
itemValue="#{hrCertificate.hrCertificateCode}"/>
</p:selectManyCheckbox>
So how to apply the logic
Could you edit your hrCertificate class to add a disabled boolean field? If yes, then you can add itemDisabled="#{hrCerticate.disabled}" to your f:selectItems which should be the easiest solution.
Another option would be to use a Map<hrCertificate, Boolean> instead of a List<hrCertificate>.
private Map<hrCertificate, Boolean> hrCertificatesMap = new HashMap<hrCertificate, Boolean>();
#PostConstruct
public void init() {
hrCertificatesMap.put(new hrCertificate(("Loan"), "LC"), null);
hrCertificatesMap.put(new hrCertificate(("Health"), "HI"), null);
hrCertificatesMap.put(new hrCertificate(("Trasnfer"), "TE"), null);
}
// Then when you're done with your SQL query, update your Map to add the corresponding boolean values...
.xhtml
<p:selectManyCheckbox id="hrCertificates" value="#{user.selectedHRCertificates}" layout="pageDirectio>
<f:selectItems value="#{user.hrCertificatesMap.keySet().toArray()}" var="hrCertificate" itemLabel="#{hrCertificate.hrCertificateName}" itemValue="#{hrCertificate.hrCertificateCode}" itemDisabled="#{user.hrCertificatesMap.get(hrCertificate)}" />
</p:selectManyCheckbox>
First, note that a property does not retire an actual attribute backing it, you only need a getter. So you can have:
public class MyBean implements Serializable {
private FilterEnum certFilter = FilterEnum.NO_FILTER;
private List<Certificate> certificates;
... // including certificates initialization.
public FilterEnum getCertFilter() {
return this.certFilter;
}
public void setCertFilter(FilterEnum certFilter) {
this.certFilter = certFilter;
}
public List<Certificate> getCertificates() {
// I am sure there is a cooler way to do the same with streams in Java 8
ArrayList<Certificate> returnValue = new ArrayList<>();
for (Certificate certificate : this.certificates) {
switch (this.certFilter) {
case FilterEnum.NO_FILTER:
returnValue.add(certificate);
break;
case FilterEnum.ONLY_YES:
if (certificate.isLoan) {
returnValue.add(certificate);
}
break;
case FilterEnum.ONLY_NO:
if (!certificate.isLoan) {
returnValue.add(certificate);
}
break;
}
}
return returnValue;
}
}
If you insist that you want to do the filter "in the .xhtml", you can combine c:forEach from JSTL with <f:selectItem> (note item, not items), but it will make your xhtml more complicated and may cause issues if you want to use Ajax with it.

How to globally inform sessionscoped beans to reload their data?

I have a session scoped bean which stores the selected employee.
An always visible input offers the ability to change the selected employee at any time.
Because there are a plenty of adjustable settings (and views) a couple of session scoped beans hold their #PostConstruct loaded data inside member variables, and I wouldn't like to reload this data from storage any time the view is opened (#ViewScoped).
I don't have an idea how to inform all active beans about the new selected employee.
Any hint is welcome ... ;)
I use Tomcat 8, Primefaces 5, JSF 2.2.
Since you don't want to reload this data every time the page is loaded, you could at least search the selected employee and if it's the same, do the update. If not, you don't need to do a thing.
Please notice: this solution is a workaround for a poor design!
The design insufficiency is to cache the data inside a session scoped backing bean instead of using 2nd level JPA cache in backend (see comment BalusC on question).
The workaround:
my session scoped SessionManager keeps the selectedPerson. On changing this selection a timestamp is updated:
#Named
#SessionScoped
public class SessionManager implements Serializable {
private Date lastUpdateSelectedPerson; // +getter
public void setSelectedPerson(Person selectedPerson) {
this.selectedPerson = selectedPerson;
lastUpdateSelectedPerson = new Date();
}
}
Hence other backing beans may compare this timestamp to a local update timestamp:
#Named
#SessionScoped
public class MyData implements Serializable {
#Inject SessionManager sessionManager;
private Date lastUpdateData;
private void updateData() {
// update data
lastUpdateData = sessionManager.getLastUpdateSelectedPerson();
}
public List<Data> getData() {
if (sessionManager.getLastUpdateSelectedPerson().compareTo(lastUpdateData) > 0)
updateData;
return data;
}
}

null value when binding a field inside HashMap using jsf

I have a request scoped CDI manage bean(Also tried with jsf managed bean but the same problem!) :
#Named
#RequestScoped
public class myController{
private HashMap<String, MyModel> modelMap;
#PostConstruct
private void init() {
logModelMap = new HashMap<String, MyModel>();
logModelMap.put("CONSTANT_1", new MyModel());
logModelMap.put("CONSTANT_2", new MyModel());
logModelMap.put("CONSTANT_4", new MyModel());
}
public HashMap getModelMap() {
return logModelMap;
}
}
And MyModel class which is a simple pojo:
public class MyModel{
private String type = "";
private Date date;
//constructor, getter and setter methods
}
I have written a composit component using jsf and bind fields to textbox and calendar and I want to access fields inside hashmap and set some values:
#{myController.modelMap['CONSTANST_1'].type}
#{myController.modelMap['CONSTANST_1'].date}
#{myController.modelMap['CONSTANST_2'].type}
#{myController.modelMap['CONSTANST_2'].date}
#{myController.modelMap['CONSTANST_3'].type}
#{myController.modelMap['CONSTANST_3'].date}
but just the first two lines for constant_1 works and for other two constants, type and date are null !
I saw in firebug that values are sent to server properly but fields inside map are not set!
By the way I'm using primefaces command button with ajax to send data to server.
Finally I got where the problem was.
I had nested forms!!!
One form that wraps my composite component and one form where I used my composite component!

Resources