Ajax-update another component inside same composite component - ajax

When using ajax inside a composite component, the ajax feature isn't working. What's wrong with the code?
the composite component:
<composite:interface>
<composite:attribute name="filter" required="true" type="java.lang.String" />
<composite:attribute name="list" required="true" type="java.util.List" />
</composite:interface>
<composite:implementation>
<h:inputText value="#{cc.attrs.filter}">
<f:ajax event="keyup" render="#{cc.clientId}:table#{cc.clientId}" />
</h:inputText>
<h:dataTable id="table#{cc.clientId}" value="#{cc.attrs.list}" var="elem">
<h:column>
<h:outputText value="#{elem}" />
</h:column>
</h:dataTable>
</composite:implementation>
now the bean
#ManagedBean
#SessionScoped
public class Ajaxcc
{
private String filter;
private List<String> list;
public Ajaxcc()
{
list = new ArrayList<String>();
list.add("one");
list.add("two");
list.add("three");
list.add("four");
}
public List<String> getList()
{
List<String> filteredList = new ArrayList<String>();
for (String s : list)
{
if (filter == null || filter.trim().equals(""))
{
filteredList.add(s);
}
else if (s.contains(filter))
{
filteredList.add(s);
}
}
return filteredList;
}
public String getFilter()
{
return filter;
}
public void setFilter(String filter)
{
this.filter = filter;
}
}
now the view:
<h:form>
<myCustomComponent:ajaxcc list="#{ajaxcc.list}" filter="#{ajaxcc.filter}" />
</h:form>
I'm using myfaces 2.1.10, deploying on a tomcat 7.0.39 by maven.
Expected behaviour: the list on the web site should be reduced to one, two and four whenever I press the o - button
Failure: the list isn't reduced.
What could be the solution?
BTW, if I put the content of the composite component into the view, it works correctly:
<h:form>
<h:inputText value="#{ajaxcc.filter}">
<f:ajax event="keyup" render="table" />
</h:inputText>
<h:dataTable id="table" value="#{ajaxcc.list}" var="elem">
<h:column>
<h:outputText value="#{elem}" />
</h:column>
</h:dataTable>
</h:form>
In this case pressing o reduces the list to the expected values. Besides, I found out that the response of the composite component ajax call seems to be "empty" for the data table field values; the response of the direct ajax call contains the new list.

<h:inputText ...>
<f:ajax ...render="#{cc.clientId}:table#{cc.clientId}" />
</h:inputText>
<h:dataTable id="table#{cc.clientId}" ...>
This is unnecessarily clumsy. Both components are in the same naming container (the composite itself!), so the prefix is implicitly already done by the composite itself. The suffix is valid, but just unnecessary. The composite component's own ID already enforces the uniqueness in the context of its parent naming container component. Opening the page in browser, rightclick-view source and observing the IDs in generated HTML output should already have given insights about that.
Just keep it simple:
<h:inputText ...>
<f:ajax ...render="table" />
</h:inputText>
<h:dataTable id="table" ...>
See also:
How to find out client ID of component for ajax update/render? Cannot find component with expression "foo" referenced from "bar"
Is it possible to update non-JSF components (plain HTML) with JSF ajax?

Related

Ajax in JSF with selectManyCheckbox

I have a datatable and two checkboxes. The content of the datatable is rendered considering which of the checkboxes are selected. I have the following functional code which renders the datable content when pressing the submit button:
<h:form>
<h:selectManyCheckbox value="#{myBean.selections}">
<f:selectItem itemValue="expired" itemLabel="Expired" />
<f:selectItem itemValue="active" itemLabel="Active" />
</h:selectManyCheckbox>
<h:commandButton id="submit" value="Select"
action="#{myBean.getList}" />
</h:form>
<h:form>
<h:dataTable ... />
</h:form>
However, I want to replace the submit button with an ajax call using the f:ajax tag. I tried something like:
<h:selectManyCheckbox value="#{myBean.selections}">
<f:selectItem itemValue="expired" itemLabel="Expired" />
<f:selectItem itemValue="active" itemLabel="Active" />
<f:ajax event="click" render="#form" listener="#{myBean.getList}" />
</h:selectManyCheckbox>
<h:form>
<h:dataTable .... />
</h:form>
But the content is not rendered. What am I doing wrong?
Bean:
public String[] selections = { "expired", "active" };
public String[] getSelections() {
return selections;
}
public void setSelections(String[] selections) {
this.selections = selections;
}
public void getList() {
for (String option : selections) {
// add expired
if (option.equals("expired"))
showExpired();
// add active
else if (option.equals("active"))
showActive();
}
//return Arrays.toString(selections);
}

Dynamic input fields (values) reset on remove item/field

I have a dynamically expansible table with an add and remove button:
<h:form>
<h:dataTable id="tblFields" value="#{bean.fields}" var="field">
<h:column>
<h:inputText value="#{field.value}" />
</h:column>
<h:column>
<h:inputText value="#{field.value2}" />
</h:column>
<h:column>
<h:inputText value="#{field.value3}" />
</h:column>
<h:column>
<h:commandButton value="Remove">
<f:ajax listener="#{bean.onButtonRemoveFieldClick(field)}" immediate="true" render="#form" />
</h:commandButton>
</h:column>
</h:dataTable>
<h:commandButton value="Add">
<f:ajax listener="#{bean.onButtonAddFieldClick}" execute="#form" render="tblFields" />
</h:commandButton>
</h:form>
This is the associated backing bean:
#Named
#ViewScoped
public class Bean {
private List<Field> fields;
#PostConstruct
public void init() {
fields = new ArrayList();
fields.add(new Field());
}
public List<Field> getFields() {
return fields;
}
public void setFields(List<Field> fields) {
this.fields = fields;
}
public void onButtonRemoveFieldClick(Field field) {
fields.remove(field);
}
public void onButtonAddFieldClick() {
fields.add(new Field());
}
}
The use case is as follows:
Press the add button multiple times.
Fill out all values.
Press the remove button of one random row.
After that, all values filled so far disappears and show up blank. How can I keep them filled after pressing the remove button?
It's because you're in the remove button ajax-updating the entire form without processing the newly submitted input values. The immediate="true" on ajax listener skips the processing of all input components which do not have immediate="true" set. You need to remove the attribtue. The absence of execute attribute will cause only the current component (#this) to be processed by default. You need to explicitly specify #form.
So, just do the same as in your add button. Replace immediate="true" by execute="#form".
<f:ajax listener="#{bean.onButtonRemoveFieldClick(field)}" execute="#form" render="#form" />

jsf2 form in form with ajax

On complex data input forms it may happen that one needs to edit a form in a form. As this is not possible in JSF2, I wonder what better solution could be found for this problem. Here's an example of what I need:
given: two beans: OuterBean and HobbyBean; a list of HobbyBeans is used in OuterBean
OuterBean is a ManagedBean (in SessionScope)
HobbyBean contains two fields hobby and like
I want to add HobbyBeans on the form of adding a user's name in OuterBean without submitting OuterBean but submitting new values to fill the list. Here's the code example:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html">
<h:head>
</h:head>
<h:body>
<h:form>
<h:panelGrid columns="2">
<h:outputText value="Name" />
<h:inputText value="#{outerBean.name}" required="true" />
<h:outputText value="Hobbies" />
<h:dataTable id="ht" value="#{outerBean.hobbies}" var="h">
<h:column>
<h:outputText value="#{h.hobby}" />
<f:facet name="footer">
<h:inputText value="#{outerBean.hobby}" required="true" />
</f:facet>
</h:column>
<h:column>
<h:outputText value="#{h.like}" />
<f:facet name="footer">
<h:inputText value="#{outerBean.like}" required="true" />
<h:commandButton action="#{outerBean.addHobby}" value="+" immediate="true">
<f:ajax render="ht" />
</h:commandButton>
</f:facet>
</h:column>
</h:dataTable>
</h:panelGrid>
</h:form>
</h:body>
</html>
Yes, there is no command button for the outer form, but that's not the question here. The commandButton is for the inner form and therefore set attribute immediate = true. Doing this, all fields are NOT checked to be not empty (required-Tag is ignored). But also is the content of this fields ignored and not set into the ajax request. How do I avoid this and send the field values of h.hobby and h.like within the ajax request to the OuterBean?
Here the beans:
#ManagedBean
#SessionScoped
public class OuterBean
{
private List<HobbyBean> hobbies;
private String name;
private String hobby;
private Integer like;
public OuterBean()
{
hobbies = new ArrayList<HobbyBean>();
}
public String addHobby()
{
hobbies.add(new HobbyBean(hobby, like));
System.out.println("hobbies: " + hobbies.toString());
return "";
}
public String submit()
{
System.out.println("name is " + name);
return "";
}
// + getter & setter
}
/* ------------------------------------------------------------------------ */
public class HobbyBean
{
private String hobby;
private Integer like;
public HobbyBean(String hobby, Integer like)
{
this.hobby = hobby;
this.like = like;
}
public String toString()
{
return hobby == null ? "" : hobby.concat(",").concat(like == null ? "" : like.toString());
}
// + getter & setter
}
Now what happens when I add a hobby to the bean is that there is no hobby added because the bean fields hobby and like are not set (the list is empty, log is: "hobbies: []"). How can I make it work?
Well your hobbies is empty because you don't tell the f:ajax what to submit. IF you want to do a partial submit (i guess that's what you want) You can try the following:
<h:form>
<h:panelGrid columns="2">
<h:outputText value="Name" />
<h:inputText value="#{outerBean.name}" required="true" />
<h:outputText value="Hobbies" />
<h:dataTable id="ht" value="#{outerBean.hobbies}" var="h">
<h:column>
<h:outputText value="#{h.hobby}" />
<f:facet name="footer">
<h:inputText id="hobby" value="#{outerBean.hobby}" required="true" />
</f:facet>
</h:column>
<h:column>
<h:outputText value="#{h.like}" />
<f:facet name="footer">
<h:inputText id="like" value="#{outerBean.like}" required="true" />
<h:commandButton action="#{outerBean.addHobby()}" value="+" >
<f:ajax execute="hobby like" render="ht" />
</h:commandButton>
</f:facet>
</h:column>
</h:dataTable>
</h:panelGrid>
</h:form>
As you can see in the code you tell f:ajax wich part of the form needs to be submitted when you push the button. If you check your bean now you will see that only the hobby and like are added to the bean. The name part will not be submitted. You say all fields are not checked. This is because you use immediate=true this skips validation. I would suggest not to use it. Look here for more information about immediate= true http://balusc.blogspot.nl/2006/09/debug-jsf-lifecycle.html#WhenShouldIUseTheImmediateAttribute Hopes this help you.

JSF 2: access selected value of selectOneMenu in a form BEFORE submit

VIEW
<h:form id="main_form">
<p:inputText id="title" required="true" label="Title" value="#{myBean.myLink.title}" immediate="true" />
<p:selectOneMenu id="scope" required="true" label="Scope" value="#{myBean.myLink.scope}" immediate="true" >
<f:selectItem itemLabel="Please choose" itemValue="" />
<f:selectItems value="#{myBean.availableScopes}" id="selScope"/>
</p:selectOneMenu>
<p:inputText id="link" required="true" label="URL" value="#{myBean.myLink.link}" immediate="true">
<p:ajax event="blur" update="msgLink" listener="#{myBean.checkUrl}" />
</p:inputText>
... msgLink and other (required) elements
... submit button
</h:form>
Managed Bean
#Component("myBean")
#Scope("session")
public class MyBean implements Serializable {
private Link myLink;
private Map<String, String> availableScopes;
public MyBean() {
this.availableScopes = new HashMap<String, String>();
this.availableScopes.put("Intranet", "Intranet");
this.availableScopes.put("Internet", "Internet");
}
// setter/getters etc.
public void checkUrl() {
System.out.println(myLink.getTitle()); // works
System.out.println(myLink.getScope()); // DOES NOT work
System.out.println(myLink.getLink()); // works
}
}
I want to check the URL depending of the selected scope before submitting the form. But the called method can access the inputText values of the object. Not the value chosen in selectOneMenu.
I have tried it with getExternalContext().getSessionMap().get("scope") but the SessionMap is null at that time.
Any chance to access the selected value of the combo box?
Thanks
Jim
The <p:ajax> (and <f:ajax>) inside an UIInput component executes/processes by default the current UIInput component (#this) only, not others.
If you want to execute/process all those UIInput components when the listener method is to be invoked, then you should specify that as such in <p:ajax process> (or <f:ajax execute>) attribute:
<p:inputText id="title" ... />
<p:selectOneMenu id="scope" ... >
...
</p:selectOneMenu>
<p:inputText id="link" ...>
<p:ajax process="title scope link" ... />
</p:inputText>
Unrelated to the concrete problem, I wonder how all those immediate="true" attributes are useful in this context. Are you certain you need them?

Select a component and display - Ajax

I need to output the text that is selected in the selectOneMenu-list. My code is as follows;
<h:selectOneMenu value="#{DataForm.stationed}" id="globalFilter" onchange="carsTable.filter()" style="width:350px;font-size:13px;" >
<f:selectItems value="#{DataForm.listHotel}" var="user" itemValue="#{user[1]}" itemDisabled="false" itemLabel="#{user[1]}" />
<h:outputText value="#{carsTable[1]}" style="width:350px"/>
</h:selectOneMenu>
How do I code this?
EDIT
This Listbox is within a <datatable>
<h:body>
<h:form id="form1" >
<p:dataTable var="car" value="#{DataForm.listHotels}" widgetVar="carsTable" paginator="true" rows="10" onRowSelectComplete="carDialog.show()" emptyMessage="No hospital found with given criteria" selectionMode="single" onRowSelectUpdate="growl" style="width:1400px;font-size:13px;">
<h:selectOneMenu value="#{DataForm.stationed}" id="globalFilter" onchange="carsTable.filter()" style="width:350px;font-size:13px;" >
<f:selectItems value="#{DataForm.listHotel}" var="user" itemValue="#{user[1]}" itemDisabled="false" itemLabel="#{user[1]}" />
<h:outputText value="#{carsTable[1]}" style="width:350px"/>
</h:selectOneMenu>
</p:dataTable>
<f:ajax render= "#form1" >
<h:selectOneMenu value="#{DataForm.stationed}">
<f:selectItems value="#{DataForm.listHotels}" var="item" itemValue="#{DataForm.listHotels}" itemLabel="#{DataForm.listHotels}" />
</h:selectOneMenu>
</f:ajax>
</h:form>
</h:body>
I think you should remove the nested <h:outputText/> first, it doesn't really make sense at that position. Also, from your question and example code, it doesn't really become clear what PrimeFaces has to do with it.
To output the selected value, simply print the expression #{DataForm.stationed}, since this is the binding that will receive the selected value.
The following example demonstrates this via AJAX, but it would work the same way if you used an action and a regular form submit.
Facelet
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<h:head />
<h:body>
<h:form id="form">
<f:ajax render="form">
<h:selectOneMenu value="#{selectOneMenuBean.value}">
<f:selectItems value="#{selectOneMenuBean.items}" var="item" itemValue="#{item}" itemLabel="#{item}" />
</h:selectOneMenu>
</f:ajax>
Selected value: #{selectOneMenuBean.value}
</h:form>
</h:body>
</html>
Bean
#ViewScoped
#ManagedBean
public class SelectOneMenuBean {
private List<String> items = Arrays.asList("a", "b", "c");
private String value;
public List<String> getItems() {
return items;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
p.s.
In your code example the EL names for your beans start with a capital. This is a little against the typical conventions. Also, you might want to pay some attention to your names and types. The bean returns something called listHotel, but this is assigned to a variable named user, which is then indexed by an integer. I would recommend aligning names (e.g. collection name = users, variable name user) and using properties instead of indexes (e.g. user.name).

Resources