How do I know which element is clicked in ui:repeat? - jsf

In JSF, we can bind HtmlDataTable to backing bean and get the row data. But ui:repeat doesn't even have a binding attribute. So, how do I know which row (element) is clicked in ui:repeat?

Either use f:setPropertyActionListener
<h:form>
<ui:repeat value="#{bean.items}" var="item">
<h:outputText value="#{item.value}">
<h:commandButton value="submit" action="#{bean.submit}">
<f:setPropertyActionListener target="#{bean.item}" value="#{item}"/>
</h:commandButton>
</ui:repeat>
</h:form>
with
private List<Item> items;
private Item item;
public void submit() {
System.out.println(item);
}
Or just put action method in iterated item
<h:form>
<ui:repeat value="#{bean.items}" var="item">
<h:outputText value="#{item.value}">
<h:commandButton value="submit" action="#{item.submit}" />
</ui:repeat>
</h:form>
Either case, you need to ensure that the same items is preserved in subsequent request.
Both ways by the way also just works in a h:dataTable.

Related

How to reset inputText field in JSF Primefaces

InputText field in the following dialog retains previous value even though I set it to blank before calling show(). The inputText field is only displayed blank when show() is called for the first time. My bean is session scoped.
<p:dialog id="dlgId" widgetVar="dlgVar" dynamic="true">
<h:form>
<h:panelGrid columns="1">
<h:outputLabel for="nametext" value="Name" />
<p:inputText id="nametext" value="#{myBean.name}" />
</h:panelGrid>
<p:commandButton value="Save" actionListener="#{myBean.saveAction}" />
</h:form>
public void add(TreeNode selectedTreeNode) {
setName("");
RequestContext.getCurrentInstance().execute("PF('dlgVar').show()");
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
How can I get the inputTEext field to display the value I set before calling show() rather then the value previously entered by the user?
The thing is: you need to update your form. To make it, you can use one of these solutions.
Solution 1 : update it from your xhtml
<h:form id="form">
<h:panelGrid columns="1">
<h:outputLabel for="nametext" value="Name" />
<p:inputText id="nametext" value="#{myBean.name}" />
</h:panelGrid>
<p:commandButton value="Save" actionListener="#{myBean.saveAction}" update=":form" />
</h:form>
Solution 2 : update it from your managedBean
YourXhtml
<h:form id="form">
...
</h:form>
YourManagedBean
public void saveAction() {
...
name = "";
RequestContext.getCurrentInstance().update(":form");
}
You can also read this post Can I update a JSF component from a JSF backing bean method?.
Solution 3 : update it using an Ajax event
You can also add an ajax event
<p:commandButton value="Save" type="button" >
<p:ajax event="click" listener="#{myBean.saveAction}" update=":form"/>
</p:commandButton>

h:inputText not working with a4j:commandLink

myBean is in request scope.
<h:form id="indexFormID">
<a4j:outputPanel ajaxRendered="true" layout="block">
<h:inputText id="inputForHD" value="#{myBean.inputParam}"></h:inputText>
<a4j:commandLink value="Submit" action="#{myBean.myMethod}" reRender="renderSuccess" process="indexFormID:inputForHD"></a4j:commandLink>
</a4j:outputPanel>
<h:panelGroup id="renderSuccess">
<h:panelGroup rendered="#{myBean.someBoolean}">
//Some other JSF components go here
</h:panelGroup>
</h:panelGroup>
</h:form>
MyBean class definition:
private String inputParam;
//Getters and setters are there
public String myMethod()
{
log.debug("~ Value of inputParam" +this.getInputParam()); //This is printing null value for inputParam
//when commandLink is clicked
return null;
}
Why my inputParam is not getting set with the input parameters?
Ok I found few issues with your approach:
<h:inputText id="inputForHD" value="#{myBean.inputParam}"></h:inputText>
You are already mapping the inputParam attribute with this bean, why have a new Id "inputForHD"
Use the inputParam itself, if you want to use inputForHD, you can pick the same from request Parameter map like.
String inputForHD = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("indexFormID:inputForHD");
Also as I mentioned previously wrap the output panel inside the and a4j panel e.g.
<h:panelGroup id="renderSuccess">
<h:panelGroup rendered="#{helloWorld.someBoolean}">
//Some other JSF components go here
<h:inputText id="inputForHDasdasd" value="#{helloWorld.inputParam}"></h:inputText>
</h:panelGroup>
</h:panelGroup>
This is working fine, let know if any issues.

Using <ui:repeat><h:inputText> on a List<String> doesn't update model values

Here is the scenario (simplified):
There is a bean (call it mrBean) with a member and the appropriate getters/setters:
private List<String> rootContext;
public void addContextItem() {
rootContext.add("");
}
The JSF code:
<h:form id="a_form">
<ui:repeat value="#{mrBean.stringList}" var="stringItem">
<h:inputText value="#{stringItem}" />
</ui:repeat>
<h:commandButton value="Add" action="#{mrBean.addContextItem}">
<f:ajax render="#form" execute="#form"></f:ajax>
</h:commandButton>
</h:form>
The problem is, when clicking the "Add" button, the values that were entered in the <h:inputText/> that represent the Strings in the stringList aren't executed.
Actually, the mrBean.stringList setter (setStringList(List<String> stringList)) is never called.
Any idea why?
Some info -
I'm using MyFaces JSF 2.0 on Tomcat 6.
The String class is immutable and doesn't have a setter for the value. The getter is basically the Object#toString() method.
You need to get/set the value directly on the List instead. You can do that by the list index which is available by <ui:repeat varStatus>.
<ui:repeat value="#{mrBean.stringList}" varStatus="loop">
<h:inputText value="#{mrBean.stringList[loop.index]}" />
</ui:repeat>
You don't need a setter for the stringList either. EL will get the item by List#get(index) and set the item by List#add(index,item).

JSF: what is the "correct" way of doing this?

I've been using JSF for a while but there's something that has always confused me. Hopefully someone can help.
Simple example, there's a page that shows a table of "Person"s and when you click on the "Person" name, it takes you to a page to view the details of the "Person".
Typically, I implement a personSearch.jsf page like this:
<h:dataTable value="#{personHandler.persons}" var="person">
<h:column>
<h:commandLink action="#{personHandler.show( person.id )}" >
<h:outputText value="#{person.name}" />
</h:commandLink>
</h:column>
</h:dataTable>
And I implement a personView.jsf page like this:
<h:panelGrid columns="2">
<h:outputText value="Person ID:" />
<h:outputText value="#{personHandler.selectedPerson.id}" />
<h:outputText value="Person Name:" />
<h:outputText value="#{personHandler.selectedPerson.name}" />
</h:panelGrid>
PersonHandler.show(Integer personId) sets personHandler.selectedPerson and then redirects to the personView page.
This all works fine when PersonHandler is a session bean. But I prefer it to be a request scoped bean because the user may have several windows open and I don't want there to be only one selected person per session.
So my question is, what's the "correct" way to do this JSF? I was once able to get what I wanted using a4j:keepAlive on the personHandler, but that always felt like a kludge. Again, this is something I've never understood about JSF.
Any help is greatly appreciated!
rob
If the view is supposed to be bookmarkable, pass the person ID as a GET request parameter instead of a POST request "parameter".
<h:outputLink value="viewperson.xhtml">
<f:param name="id" value="#{person.id}" />
</h:outputLink>
This way you can use two #RequestScoped beans, one for the list and one for the view. You can preload the selected person as follows:
#ManagedProperty(value="#{param.id}")
private Long id;
#PostConstruct
public void init() {
selectedPerson = personDAO.find(id);
}
If it is not supposed to be bookmarkable, then just create a single view which renders the view state conditionally.
<ui:fragment rendered="#{!personHandler.viewMode}">
<h:form>
<h:dataTable value="#{personHandler.persons}" var="person">
<h:column>
<h:commandLink value="#{person.name}" action="#{personHandler.show(person)}" />
</h:column>
</h:dataTable>
</h:form>
</ui:fragment>
<ui:fragment rendered="#{personHandler.viewMode}">
<h:form>
...
<h:commandLink value="Go back" action="#{personHandler.back}" />
</h:form>
</ui:fragment>
(You can if necessary split out the content of the both framgents to another Facelet files which you include by <ui:include>)
This way you can use a single #ViewScoped bean with action methods returning void or null.
public void show(Person selectedPerson) {
this.selectedPerson = selectedPerson;
}
public void back() {
selectedPerson = null;
}
public boolean isViewMode() {
return selectedPerson != null;
}
You can even wrap the whole view in some
<h:panelGroup id="container">
and nest the following in both command links to let Ajax magic do the work
<f:ajax execute="#form" render=":container" />

Change the properties of an Input within a ui:repeat

I'd like to change the "required" property of an InputText that is located within an ui:repeat, but I'm not able to access to the component from the ManagedBean:
<h:selectManyCheckbox id="required" value="#{test.required}"
layout="lineDirection" converter="javax.faces.Integer">
<f:ajax event="change" listener="#{test.update}" />
<f:selectItems value="#{test.selectable}"></f:selectItems>
</h:selectManyCheckbox>
<ui:repeat value="#{test.names}" var="name" id="repeat">
<h:panelGrid columns="3">
<h:outputLabel id="nameLabel">name:</h:outputLabel>
<h:inputText id="name" value="#{name}"
validator="#{test.validateName}" />
<h:message for="name"></h:message>
</h:panelGrid>
</ui:repeat>
I'm trying to use the findComponent method, but it does not work:
public void update(AjaxBehaviorEvent event) {
for(Integer i: selectable) {
UIViewRoot vr = FacesContext.getCurrentInstance().getViewRoot();
HtmlInputText input = (HtmlInputText)vr.findComponent("form:repeat:"+i+":name");
input.setRequired(required.contains(i));
}
}
The ui:repeat doesn't repeat the components in the view root, it repeats the component's output in the rendered HTML output.
There are several ways to achieve this properly. One of them is to use a value object instead and set the requireness there. E.g. a List<Item> wherein Item has the properties String name and boolean required.
<ui:repeat value="#{test.items}" var="item" id="repeat">
<h:panelGrid columns="3">
<h:outputLabel id="nameLabel">name:</h:outputLabel>
<h:inputText id="name" value="#{item.name}" required="#{item.required}" validator="#{test.validateName}" />
<h:message for="name"></h:message>
</h:panelGrid>
</ui:repeat>
There are more ways, but since the JSF version you're using and the functional requirement is unclear, it's only guessing which way is the most applicable in your case.

Resources