p:picklist gets cached although new values are set - jsf

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);
}

Related

Dropdown list is not populating in JSF

I am working on Managedbeans and JSF. As shown below that my ManagedBean contains all the requirements that are required for the JSF to get the value. I have initialised my dropdown list as below. In selectOneMenu, I have chosen the country as a string where it will store the value selected by the dropdown list and the dropdown list will bring up the list that I declared in the Beans.
Unfortunately, it is not happening like that. Every time dropdown renders it gives me an empty value. I have spent days on it but cannot figure out the exact solution to it. I have cleaned my server, build workspace and also change servers but nothing is working.
** ManagedBean_list **
private List<String> listCountry;
private String country;
public void tada(){
listCountry=Arrays.asList("India", "pakisatan","America");
}
public List<String> getListCountry() {
return listCountry;
}
public void setListCountry(List<String> listCountry) {
this.listCountry = listCountry;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
JSF
<p:selectOneMenu id="country" value="#{loginBeans.country}">
<f:selectItems value="#{loginBeans.listCountry}" />
</p:selectOneMenu>
Your help is appreciated. Empty dropdown list image
enter image description here
Which bean annotation are you using? You say "Managedbeans", but the source you posted does not show the entire bean, or does it? Check to make sure you are not mixing old style JSF managed bean annotations with CDI annotations.
The issue is that on initialization, the list is not being called up. I resolved it by including the list function inside the constructor of managed beans class. so that when the constructor fired up. It also generates the dropdown list.
Either convert your listCountry to a
private Map<String, String> listCountry = new HashMap<>();
listCountry.put("India", "India");
listCountry.put("Pakistan", "Pakistan");
listCountry.put("America", "America");
or
private List<SelectItem> listCountry = new ArrayList<>();
listCountry.add(new SelectItem("India", "India"));
listCountry.add(new SelectItem("Pakistan", "Pakistan"));
listCountry.add(new SelectItem("America","America"));

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";
}

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

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.

p:dataTable and optimistic locking in JPA

Given below an exemplary <p:dataTable> using the PrimeFaces lazy data model. The row version field as marked by #javax.persistence.Version in the associated JPA entity is temporarily displayed in one of the columns in read-only mode.
Needless to mention that every action is Ajaxical.
Editing this <p:dataTable> using a <p:rowEditor/> as follows (the first row in the table. The row version is 1).
When a row being edited is updated by clicking the tick mark in the Edit column as above, the row version is incremented in the database to 2 on successful update but the row version held by the data table is still 1 (see the picture below). After update is completed, the status of the data table can be viewed as follows.
What happens now is if the same row is attempted again (without refreshing the page by sending a synchronous GET request), then it will not match the row version in the database which is currently 2 because the data table still supplies the old previous value of the version which is 1. This will cause the javax.persistence.OptimisticLockException to be thrown as if concurrent updates are detected which is evidently untrue.
Something may need to be changed drastically in the normal Ajaxical update facility provided by a <p:dataTable> (using a <p:rowEditor/>) in order for it to work correctly and synchronously with the optimistic locking strategy in JPA.
What would be the correct way to get around the situation? Dropping the <p:rowEditor/> in its entirely will need tremendous changes to existing applications, by the way which is quite undesired.
While editing/updating rows in a <p:dataTable> using a <p:rowEditor>, it is required to synchronize changes manually to the data model (LazyDataModel<T>) backed by the <p:dataTable> after the update operation finishes successfully.
One way to do so is to use the getWrappedData() method available in LazyDataModel<T> which returns the data currently held by the data model backed by the data table in the form of java.lang.Object. This is a basic usage.
#Named
#ViewScoped
public class Bean extends LazyDataModel<Entity> implements Serializable {
#Inject
private Service service;
private static final long serialVersionUID = 1L;
public Bean() {}
#Override
public List<Fruit> load(int first, int pageSize, List<SortMeta> multiSortMeta, Map<String, Object> filters) {
setRowCount(service.getRowCount());
// Do something, if necessary.
// Turn the List<SortMeta> into another type like a LinkedHashMap<String, String>.
return service.getList(first, pageSize, multiSortMeta, filters);
}
public void onRowEdit(RowEditEvent event) {
if (event.getObject() instanceof Entity) {
Entity entity = (Entity) event.getObject();
Entity newEntity = service.update(entity); // A new entity from the database.
if (newEntity != null) {
List<Entity> entities = (List<Entity>) getWrappedData();
int index = entities.indexOf(entity);
if (index >= 0) { // The test may be omitted.
entities.set(index, newEntity);
// Just replace the stale/old entity by a newly updated entity in the database.
}
// Add an appropriate FacesMessage to indicate a success.
} else {
// Add an appropriate FacesMessage to indicate a failure.
}
} else {
// Add an appropriate FacesMessage to indicate a failure.
}
}
}
The onRowEdit() method is bound to an Ajax listener inside a <p:dataTable>.
<p:ajax event="rowEdit" listener="#{bean.onRowEdit}"/>
I am not going to accept this answer as there could be a better way to do so which I am unaware of. More comprehensive answers are welcomed, if any.

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