I am working with JSF 1.2 and could use a little advice. I have an FMB (PlanFMB.java) that contains an array list of select items and a single selectedItem.
When the page loads, the getProjects() method is correctly called and displays the Projects as expected. Strange thing is though, the getSelectedItem() method is called three times (once of each project). Not sure if this is typical behavior:
SystemOut O getSelectedItem = null
SystemOut O getSelectedItem = null
SystemOut O getSelectedItem = null
Also, I have a command link in my JSP, that when clicked does the same thing, calls the getSelectedItem() method three times:
<h:commandLink action="#{planDocBean.classAction}" id="classActionENLink">
PlanFMB.java
String selectedItem = null;
private List<SelectItem> selectItems = null;
public String getSelectedItem() {
System.out.println("getSelectedItem = " + selectedItem);
return selectedItem;
}
public void setSelectedItem(String selectedItem) {
this.selectedItem = selectedItem;
System.out.println("setSelectedItem = " + selectedItem);
}
public List<SelectItem> getProjects() {
if (selectItems == null) {
selectItems = new ArrayList<SelectItem>();
selectItems.add(new SelectItem("Project1", "Project1"));
selectItems.add(new SelectItem("Project2", "Project2"));
selectItems.add(new SelectItem("Project3", "Project3"));
}
return selectItems;
}
<h:selectOneMenu id="items" value="#{planDocBean.selectedItem}">
<f:selectItems value="#{planDocBean.projects}" />
</h:selectOneMenu>
One last thing, the actual selected item is not retained in the select menu when the page reloads.
Any feedback much appreciated. Thanks!
Related
I have button on my view. When I click it there is an action handler where I'm getting view root from faces context and then I get form. Then I loop through facets and children of the form and I look for OutputLabel instances. When I get it I'm trying to getValue from it. But it's always null, but on rendered page there is value. OutputLabel (p:outputLabel) is placed in composite component...
What do I wrong? Some ideas?
// EDIT
public void buttonHandler(){
FacesContext facesContext = FacesContext.getCurrentInstance();
UIForm form = facesContext.getViewRoot().findComponent("formId");
List<String> list = new ArrayList<>();
collectLabels(form, list);
}
public void collectLabels(UIComponent component, List<String> list){
Iterator<UIComponent> iterator = component.getFacetsAndChildren();
while(iterator.hasNext()){
UIComponent child = iterator.next();
collectLabels(child, list);
}
if(component instanceof OutputLabel){
list.add(((OutputLabel)component).getValue());
}
}
I'm not at work now. So I don't remember my composite component code now. But it contains <p:outputLabel for="#{cc.attr.id}" value="#{cc.attr.labelValue}" />
I cannot see why the question is duplicate. If I debug the code then - when the button is clicked - no new value of projectSelected is being detected. Even the hashCode is the same. The equals method of the ProjectEntity only contains the id which is the same since it comes from the database and is not changed anywhere. Null values don't exist in the selection.
There was, however, too much code to reproduce the problem. I removed unnecessary code and the problem still persists.
Original question: In the following form with 3 <p:selectOneMenu> -fields if the submit button is clicked a valueChangeEvent is fired for the projectSelector field although it hasn't changed. Why is that? Like that the actual action behind the button is never called. I would expect a valueChangeEvent to be fired only in case the project changes.
Update: Trying to find the cause I replaced the ProjectEntity with String and then it worked. So I thought it must be the equals method of ProjectEntity but that only compares the id. I debugged further and found out that the selected value is being compared with a ProjectEntity with all fields set to null which gives a false and hence a valueChangeEvent. So the question is why is there a ProjectEntity with all fields set to null? I debugged into UIInput.compareValues which has that "null"-ProjectEntity being the previous value. That is being returned by UIOuput.getLocalValue. Where does it come from?
Update2: Even when using the equals and hashCode from selectOneMenu shows after submit always the last item in the list as selected item the behaviour does not change. I created an ear file readily to be deployed to e.g. a wildfly and would appreciate any help since I am stuck on this question.
<h:form>
<p:outputLabel value="#{msgs.timeProject}"/>
<p:selectOneMenu value="#{timeBean.model.projectSelected}"
converter="projectConverter"
onchange="submit()"
valueChangeListener="#{timeBean.projectChanged}"
immediate="true"
required="true">
<f:selectItems value="#{timeBean.model.allProjects}"
var="singleProject"
itemValue="#{singleProject}"
itemLabel="#{singleProject.name}"/>
</p:selectOneMenu>
<p:commandButton value="#{msgs.send}"
action="#{timeBean.myAction}"
ajax="false"/>
<p:outputLabel value="#{timeBean.model.resultValue}"
rendered="#{not empty timeBean.model.resultValue}"/>
</h:form>
The converter
#FacesConverter(value = "projectConverter")
public class ProjectConverter implements Converter {
#Inject
private ProjectService projectService;
#Override
public Object getAsObject(final FacesContext facesContext, final UIComponent uiComponent, final String projectName) {
if (StringUtils.isEmpty(projectName)) {
return null;
}
final List<ProjectEntity> projects = projectService.findAll();
for (ProjectEntity project : projects) {
if (StringUtils.equals(projectName, project.getName())) {
return project;
}
}
return null;
}
#Override
public String getAsString(final FacesContext facesContext, final UIComponent uiComponent, final Object value) {
if (value == null) {
return null;
}
if (value instanceof ProjectEntity) {
return ((ProjectEntity) value).getName();
}
return "???projectName???";
}
}
The equals-method of the ProjectEntity
#Override
public boolean equals(final Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
final ProjectEntity that = (ProjectEntity) o;
return id != null ? id.equals(that.id) : that.id == null;
}
And the change listener inside the timeBean
public void projectChanged(final ValueChangeEvent event) {
final ProjectEntity projectSelected = (ProjectEntity) event.getNewValue();
model.setProjectSelected(projectSelected);
final FacesContext context = FacesContext.getCurrentInstance();
context.renderResponse();
}
The TimeModel
public class TimeModel {
private ProjectEntity projectSelected;
private List<ProjectEntity> allProjects;
private String resultValue;
... getters and setters ...
I'll guess, that the problem resides inside the ProjectConverter class, cause it may run into troubles to assign a valid projectService instance. Maybe you remove the injection and try to compute the value programatically in the getAsObject, getAsString methods by explicit cdi-finders.
I remember to run in a similar situation, when i was injecting in a ServletFilter.
I have 3 entities - markets, topics and items. Markets is the parent of topics which is the parent of items. I'm hoping to find a simple way to invoke an action by selecting a value from the the final child node (items) and being taken to the page where the selected item can be viewed. The JSF:
<p:tree value="#{treeTestBean.treeTest}" var="tree"
dynamic="true"
selectionMode="single"
selection="#{treeTestBean.selectednode}">
<p:ajax event="select" listener="#{treeTestBean.onNodeSelect}"/>
<p:treeNode>
<h:outputText value="#{tree}"/>
</p:treeNode>
</p:tree>
The managed bean:
#Named(value = "treeTestBean")
#SessionScoped
public class TreeTestBean implements Serializable {
private TreeNode treetest;
private TreeNode selectednode;
private TreeNode node0;
private TreeNode node1;
private TreeNode node2;
private List<Enmarkets> markList;
private List<Entopic> topList;
private ListDataModel<Enitem> itList;
private Enitem selItem;
public TreeNode getTreeTest() {
treetest = new DefaultTreeNode("Root", null);
markList = rootFacade.findAll();
for (Enmarkets m : markList) {
node0 = new DefaultTreeNode(m.getMarketname(), treetest);
int marketid = m.getMarketid();
topList = topfac.marketTopNorm(marketid);
for (Entopic t : topList) {
node1 = new DefaultTreeNode(t.getTopicname(), node0);
int topicid = t.getTopicid();
itList = itfac.itemFroTopic(topicid);
for (Enitem i : itList) {
node2 = new DefaultTreeNode(i.getItemname(), node1);
}
}
}
return treetest;
}
The onNodeSelect method used in the ajax is also in the managed bean. If the selected node is a leaf it will search the item name and return that in the navigated page:
public void onNodeSelect(NodeSelectEvent event) {
this.setSelectednode(event.getTreeNode());
String somekey = selectednode.getRowKey();
if(selectednode.isLeaf()){
String itemName = selectednode.getData().toString();
// Standard JPA call to search for item name here (omitted because this is not how i want to do it)
FacesContext
.getCurrentInstance()
.getApplication()
.getNavigationHandler()
.handleNavigation(FacesContext.getCurrentInstance(), null, "/Main/Starter.xhtml?faces-redirect=true");
}
else {
doNothing();
}
}
onNodeSelect is supposed to search the item name and navigates to the page with details of the selected item. The above method does this by searching for the Item name String and matching this to the name in a list of the item entity values created from the persistence layer. This will allow matching the selectednode String to the correct item name, so that the navigated jsf page is populated with the entity details (for example using a standard h:outputText tag). For several reasons, i prefer to search based on the entity ID instead of a String.
Comments from Kukeltje greatly helped me in the right direction. First I include a Map(String, int) when creating the leaf node:
for (Enitem i : itList) {
node2 = new DefaultTreeNode(i.getItemname(), node1);
String rowK = node2.getRowKey();
int itid = i.getItemid();
rowMap.put(rowK, itid);
Then, in the onNodeSelect method I use this map to match the rowKey of the selectednode to the corresponding entity Id:
public void onNodeSelect(NodeSelectEvent event) {
if(selectednode.isLeaf()){
String rKey = selectednode.getRowKey();
if(rowMap.containsKey(rKey)) {
String xKey = rowMap.get(rKey).toString();
Integer rKeyint = Integer.parseInt(xKey);
selItem = itfac.find(rKeyint);
FacesContext
.getCurrentInstance()
.getApplication()
.getNavigationHandler()
.handleNavigation(FacesContext.getCurrentInstance(), null, "/Main/Client/ItemDetails.xhtml?faces-redirect=true");
}
}
else {
doNothing();
}
This navigates to the page showing the detail of the selected node leaf. I suspect there might be an easier or more efficient way of doing this and would welcome any views. Like, I don't know if it's really necessary to make the string to integer conversions and I didn't think through a simpler way.
For now, this seems to solve my concrete problem. thanks
I am using primefaces. And my requirement is to have number of tabs which will be generated based on a list specified in the backing bean. Now the second criteria is that if the tab changes the content under that tab should also changes. So I kept the onChange event and tried to get the value through event.getTab().getTitle(), but it is returning null to the backing bean.
<p:tabView id="tabView" binding="#{dndProductsView.tabView}">
<p:ajax event="tabChange" listener="#{dndProductsView.onTabChange}"/>
</p:tabView>
Managed Bean required codes are as :-
#PostConstruct
public void init() {
user = SessionBean.getUserName();
categorylist = categoryLogics.findAllOrderedByCategoryName();
productList = productLogics.findAllOrderedByProductName();
droppedProducts = new ArrayList<Product>();
}
private TabView tabView;
#Inject
private FacesContext facesContext;
public void setTabView(TabView tabView) {
this.tabView = tabView;
}
public TabView getTabView() {
tabView = new TabView();
for (Category c : categorylist) {
Tab t = new Tab();
t.setTitle(c.getCategoryName());
tabView.getChildren().add(t);
}
return tabView;
}
public void onTabChange(TabChangeEvent event) {
String titleName = event.getTab().getTitle();
System.out.println("" + titleName);
}
The tab is getting generated properly and when the tab changes, the onTabChange() method is called but event.getTab().getTitle() returns null.
Integer index=(Integer)tabview.getActiveIndex();
It is not a name but it is index of activ tab witch in this case is the one you are interested in.
Index starts from 0 being the first tab :)
I've the following code in my edit user screen
<h:selectManyCheckbox id="selectedGroups" value="#{usersController.selectedGroups}">
<f:selectItems value="#{usersController.groupsList}" var="item" itemLabel="#{item.groupname}" itemValue="#{item.groupid}" />
</h:selectManyCheckbox>
I've user groups list with all the groups in it, and I've selectedGroups list with the groups that are enabled for the user. But, on the edit screen, they are not showing selected by default. What am I missing? Is this not the right way to bind selected many checkboxes?
An item value of the groupsList will be preselected only when equals() method has returned true for at least one item in the selectedGroups.
Assuming that groupid is a Long, then the selectedGroups should return a List<Long> containing the values to be preselected.
Following code will work perfectly for request scope bean...
xhml code....
<h:selectManyCheckbox binding="#{Page1.chk_1}">
<f:selectItem binding="#{Page1.chk_1_options}"/>
</h:selectManyCheckbox>
Java code....
HtmlSelectManyCheckbox chk_1 = new HtmlSelectManyCheckbox();
UISelectItems chk_1_options = new UISelectItems();
public HtmlSelectManyCheckbox getChk_1() {
return chk_1;
}
public void setChk_1(HtmlSelectManyCheckbox chk_1) {
this.chk_1 = chk_1;
}
public UISelectItems getChk_1_options() {
if (chk_1_options.getValue() == null) {
List<SelectItem> lst_chk_options = new ArrayList<SelectItem>();
lst_chk_options.add(new SelectItem(1, "Label1"));
lst_chk_options.add(new SelectItem(2, "Label2"));
chk_1_options.setValue(lst_chk_options);
}
return chk_1_options;
}
public void setChk_1_options(UISelectItems chk_1_options) {
this.chk_1_options = chk_1_options;
}
If you want for session scope then reply, because binding elements in session scope giving problems in some cases...