I am having trouble with implementing two SelectOneMenu controls, where data in the second one depends on the selection made in the first. This example on the primeFaces showcase is almost the same as what I want to implement: http://www.primefaces.org/showcase-labs/ui/pprSelect.jsf
except that I have to get the data from a database.
The above example is working correctly in the same project. I am using NetBeans 7.0 with GlassFish 3.1 and PrimeFaces 3.0.M2, the latest drop (20th June 2011).
The source code of the JSF page and the managed bean is attached.
<?xml version='1.0' encoding='UTF-8' ?>
<!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:h="http://java.sun.com/jsf/html"
xmlns:p="http://primefaces.prime.com.tr/ui"
xmlns:f="http://java.sun.com/jsf/core">
<h:head><title>Facelet Title</title></h:head>
<h:body>
<p:log />
<center>
<h:form>
<h:outputText value="State: "/>
<p:selectOneMenu id="selectState" value="#{stateCityBean.selectedStateArray}">
<f:selectItem itemLabel="Select Any" itemValue="Empty String"/>
<p:ajax update="selectCity" listener="#{stateCityBean.updateCityMap}"/>
<f:selectItems value="#{stateCityBean.stateMap}" />
</p:selectOneMenu>
<p></p>
<h:outputText value="City: "/>
<p:selectOneMenu id="selectCity" value="#{stateCityBean.selectedCityArray}">
<f:selectItem itemLabel="Select Any" itemValue="Empty String"/>
<f:selectItems value="#{stateCityBean.cityMap}"/>
</p:selectOneMenu>
</h:form>
</center>
</h:body>
StateCityBean.java
package com.xyz.mbeans;
import com.iwizability.priceinfo.dao.*;
import com.iwizability.priceinfo.pojo.*;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.context.FacesContext;
import javax.faces.context.Flash;
import javax.faces.event.ValueChangeEvent;
#ManagedBean
#SessionScoped
public class StateCityBean {
private String selectedStateArray;
private Map<String, State> StateMap;
private Map<String, City> CityMap;
private String selectedCityArray;
public StateCityBean() {
System.out.println("Inside.............. ");
StateMap = new LinkedHashMap<String, State>();
CityMap = new LinkedHashMap<String, City>();
}
public String getSelectedStateArray() {return selectedStateArray;}
public void setSelectedStateArray(String selectedStateArray) {this.selectedStateArray = selectedStateArray;}
public Map<String, State> getStateMap() {
StateDaoImpl stateObj = new StateDaoImpl();
StateMap = stateObj.getState();
return StateMap;
}
public void setStateMap(Map<String, State> stateArray) {this.StateMap = stateArray;}
public String getSelectedCityArray() {return selectedCityArray;}
public void setSelectedCityArray(String selectedCityArray) {this.selectedCityArray = selectedCityArray;}
public Map<String, City> getCityMap() {
CityDaoImpl cityObj = new CityDaoImpl();
int stateId = 0;
if (selectedStateArray != null && !selectedStateArray.equals("")) {
stateId = StateMap.get(selectedStateArray).getId();
}
CityMap = cityObj.getCity(stateId);
return CityMap;
}
public void setCityMap(Map<String, City> CityArray) {
this.CityMap = CityArray;
}
public void updateCityMap() {
CityDaoImpl cityObj = new CityDaoImpl();
int stateId = 0;
if (selectedStateArray != null && !selectedStateArray.equals("")) {
stateId = StateMap.get(selectedStateArray).getId();
this.CityMap = cityObj.getCity(stateId);
}
}
}
On debugging, I can see that the updateCityMap method is invoked but the SelectedStateArray variable is null. Even force changing the value of bound CityMap variable does not update the selectCity drop down.
As you would have guessed, I am new to JSF, but the problem is compounded by the fact that I am using a still in development version of the tag library...
I created a demo for the exact same situation you describe in your project. I have a state and city <p:selectOneMenu/> elements on my page. You select a state, and the cities update. If a different state is selected, the city is erased since it may not exist in the state.
The difference is that I use <p:ajax event="change" update="cities, cs"/> to update the elements, and an actionListener to update the city if the state is different.
<p:selectOneMenu id="states" value="#{dataBean.selectedState}"
valueChangeListener="#{dataBean.stateChangeListener(event)}"
style="width: 150px;">
<f:selectItem itemLabel="" itemValue=""/>
<f:selectItems value="#{dataBean.states}"/>
<p:ajax event="change" update="cities, cs"/>
</p:selectOneMenu>
<h:outputLabel value="City:" for="cities"/>
<p:selectOneMenu id="cities"
value="#{dataBean.selectedCity}"
style="width: 150px;">
<f:selectItem itemLabel="" itemValue=""/>
<f:selectItems value="#{dataBean.cities}"/>
<p:ajax event="change" update="cs" />
</p:selectOneMenu>
The whole project and demo code can be found on my blog. I saw this post and decided to post my project. [blog]: http://javaevangelist.blogspot.com/2012/07/primefaces-ajax-enabled.html
Primefaces is trying something diffent. I dont know why. First of all you must know these releases are not stable.
When you analys code with firebug you will shove this.
Lets assume two combo who has ids countries and cities
when you changed the first combo cities update correcty but cities combo' id change to cities_input they add _input prefix. When I analys primefaces source code. Thereare codes something like traverse tree if visited change id by adding _input or _panel. So if you change the combo in second time. Everything work perfect except you said update cities but there is no component who has id cities becouse it has new id cities_input. So your ajax does not work correctly. But they correct this bug in 3.0m4 or after releases.
This is the problem. Another example of this problem it is bug someone open jira for this.
İf you are using login with spring security j_username, j_password change to j_username_input j_password_input. So this breaks the standart and code does not work in second ajax requests.
Hope this helps..
pay attention to lionhearts words. Primefaces namespaces changed in 3.m4 in pages use this.
xmlns:p="http://primefaces.org/ui"
I did a state -> city select in my jsf project the same way you did. The only differences I found are:
my p:ajax has a change event: <p:ajax event="change" update="city" listener="#{contatoMB.filterCities}" />.
my p:ajax comes last after the f:selectItems but this shouldn't be the problem.
my f:selectItems list in the city select is List<javax.faces.model.SelectItem> and not a Map<String, City>, have you tried using SelectItems instead of your map?
my form has an id and prependId false <h:form id="contact-form" prependId="false">, again, this shouldn't be the problem.
my h:selectOneMenu is inside a p:panel, some PrimeFaces components behave in a strange way when inside or outside some other components.
If none of this works maybe the problem is the PrimeFaces version you're using. My PrimeFaces version is 2.2.1.
I use PrimeFaces 3.0.M4 with namespaces:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:p="http://primefaces.org/ui">
It seems to work fine.
Maybe its the primefaces version but your update method in your bean looks pretty complicated.
Why not retrieve the Object of the selection from the ajax event?, this way you don't need to define that variable.
public void update(AjaxBehaviorEvent event)
{
Object selectOneMenuObject = ((UIOutput)event.getSource()).getValue();
//String selectedStateArray = (String)((UIOutput)event...
//update temporary collection of second SelectOneMenu
}
Don't know if that will help you, but thats just the way I do it.
you should just add
event="change"
to p:ajax
Related
I have an OmniFaces <o:validateMultiple> set to two <p:selectOneMenu>. The one dropdown box is for the home, the other for the away team (of a sports game).
The validator checks, if the combination of the two teams already exists in the schedule, if so, the validator method return false. (see table above in the example: combination already exists... the single item in the list)
Now the problem:
The two select boxes are reset to their null values, but in the background, they seem to keep the value.
The UI tells the user "selection has been cleared", but this is not what I or the users expect.
Example
Before: validation:
After validation:
QUESTION:
How can I restore the select boxes' values after validation fail or rather how do I simply keep the values?
I guess resetting the inputs is just JSF specified behavior?? 🤷♂️
Is it a problem with PrimeFaces (I guess not)?
Can it be done? If so, how?
PS: I don't think posting the JSF code for the select boxes could help here, but if it could, please leave a comment
JSF should preserve the state of the component tree as defined by the life cycle of the framework.
However, depending on how your JSF page functions you may choose to for example only partially process a page whenever an event occurs. In this case only the processed components will retain their state.
Let's take your use case as an example and first define a view:
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core" xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:p="http://primefaces.org/ui" xmlns:o="http://omnifaces.org/ui"
xmlns:of="http://omnifaces.org/functions">
<h:head>
<title>SelectOneMenu Validation</title>
</h:head>
<h:body>
<h:form>
<o:validateMultiple id="validateMatch" components="home away"
validator="#{selectBackingBean.onValidateMatch}"
message="The combination already exists or is invalid!" />
<p:dataList value="#{selectBackingBean.matches}" var="match">
<f:facet name="header">Home / Away</f:facet>
#{match.home} vs #{match.away}
</p:dataList>
<br/><br/>
<p:selectOneMenu id="home" label="Please select home..." value="#{selectBackingBean.selectedHome}">
<f:selectItems value="#{selectBackingBean.teams}" var="team"
itemValue="#{team}" itemLabel="#{team}" />
</p:selectOneMenu>
<p:selectOneMenu id="away" label="Please select away..." value="#{selectBackingBean.selectedAway}">
<f:selectItems value="#{selectBackingBean.teams}" var="team"
itemValue="#{team}" itemLabel="#{team}" />
</p:selectOneMenu>
<h:panelGroup>
<br/>
<h:message for="validateMatch" />
<h:outputText value="OK!" rendered="#{facesContext.postback and not facesContext.validationFailed}" />
</h:panelGroup>
<br/><br/>
<p:commandButton value="Save" action="#{selectBackingBean.onSave}" update="#form" />
</h:form>
</h:body>
</html>
Let's also define the backing bean holding the view state and the model:
#Data
#Named
#ViewScoped
public class SelectBackingBean implements Serializable {
private List<String> teams;
private List<Match> matches;
private String selectedHome;
private String selectedAway;
#Data
#AllArgsConstructor
public class Match {
private String home;
private String away;
}
#PostConstruct
private void init() {
teams = Arrays.asList("Malmö FF", "GAIS", "IFK Göteborg", "AIK");
matches = new ArrayList<>(Arrays.asList(new Match("Malmö FF", "AIK")));
}
public boolean onValidateMatch(FacesContext context, List<UIInput> components,
List<String> values) {
return values.get(0) != values.get(1) && !matches.contains(new Match(values.get(0), values.get(1)));
}
public void onSave() {
matches.add(new Match(selectedHome, selectedAway));
}
}
If we now run this we get the following results:
If we keep going and eventually run into a validation error we get the following:
Notice how it constantly retains the state of all components throghout. There are components in OmniFaces and JSF that allow you to control the state of selected parts of the component tree to get away from this behavior.
I'm guessing there is something going on in your code or that you are somehow resetting the backing bean values connected to the components.
I have now been banging my head for a while over a seemingly simple case. I have a Primefaces <p:inputText> (as a cell inside a <p:dataTable>) for which, when the value changes, I would like to get the value in my controller.
I cannot reveal the real code, only showing the vital excerpts of it:
<p:column headerText="Ratkaisu">
<p:inputText name="inpres" id="inpres" value="#{entity.resolution}">
<p:ajax event="change" immediate="true" execute="#this inpres" render="#this" listener="#{mainViewController.updateEntity}"/>
<f:param value="#{entity}" name="entity"/>
</p:inputText>
</p:column>
The "backing bean" is my mainViewController that is #ViewScoped
The method receiving the ajax data inside the controller:
public void updateEntity(AjaxBehaviorEvent e) {
// I can get the entity via the parameter list
Entity entity = (Entity)FacesContext.getCurrentInstance()
.getExternalContext().getRequestMap().get(entity);
// The value below is always null
Object value = ((UIInput)e.getSource()).getValue();
....
}
Every time the text changes (enter of focus out of the text field), I get an ajax request to my method. Every time, I find the correct Entity from the request map, so the <f:param>-part seems to work.
So my question (or maybe questions) is, that is it possible for me to get read the newly entered value of the InputText in the controller, via the AjaxBehaviorEvent or do I have to try something else (maybe setting the value of the inputText to the f:param)? Also checked that I did not find any values in the requestMap at the server side either...
I also checked what Chrome inspector is posting via ajax requests. I did not find any evidence that the text that I enter in the <p:inputText> would be sent to the server.
Could anyone enlight or give me some pointers on this issue?
EDIT:
As #melloware pointed out, I made the client side example as minimal as possible, still fully running:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:p="http://primefaces.org/ui">
<f:view>
<h:head>
</h:head>
<h:body>
<h:form id="someid">
</h:form>
<p:inputText id="inpresx">
<p:ajax event="change" process="#this"
listener="#{mainViewController.doStuff}" />
</p:inputText>
</h:body>
</f:view>
</html>
for the MainViewController, the doStuff part:
public void doStuff(AjaxBehaviorEvent e) {
Object val = ((UIInput)e.getSource()).getValue();
// val is still null!
}
Even if the program now is very minimal, the value for the inputtext does not seem to be sent over to the server side. A strange thing IMO, is that i need those h:form-tags in order for the example to fire ajax requests.
Here is what the xhr POST request params look like when i feed TestingTesting to the inputtext and press enter (using Chrome Inspector):
javax.faces.partial.ajax: true
javax.faces.source: inpresx
javax.faces.partial.execute: inpresx
javax.faces.behavior.event: change
javax.faces.partial.event: change
someid: someid
javax.faces.ViewState: -4351418361364308383:4652862949343904732
From the above data being sent, there seems like no text data from the inputtext is communicated over to the server side. What may I have done wrong?
EDIT 2:
tldr;
The actual solution to the problem for me was to wrap my table inside a form. The ajax tag won't work if there is no form tag that WRAPS it at some level.
Here is my fully working example. Whatever I type in the inputtext like "hello" comes out the in System.out.println.
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:p="http://primefaces.org/ui"
xmlns:h="http://java.sun.com/jsf/html">
<h:head>
<title>PrimeFaces Test</title>
</h:head>
<h:body>
<h:form id="frmTest">
<p:inputText id="inpresx" value="#{testView.testString}" >
<p:ajax event="change" process="#this" listener="#{testView.doStuff}" />
</p:inputText>
</h:form>
</h:body>
</html>
Java:
package org.primefaces.test;
import javax.faces.event.AjaxBehaviorEvent;
import javax.faces.component.UIInput;
import java.io.Serializable;
import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
#ManagedBean(name = "testView")
#ViewScoped
public class TestView implements Serializable {
private String testString;
public void doStuff(AjaxBehaviorEvent e) {
Object val = ((UIInput)e.getSource()).getValue();
System.out.println("Value = " + val);
}
public String getTestString() {
return testString;
}
public void setTestString(String testString) {
this.testString = testString;
}
}
And my Chrome Output:
javax.faces.partial.ajax: true
javax.faces.source: frmTest:inpresx
javax.faces.partial.execute: frmTest:inpresx
javax.faces.behavior.event: change
javax.faces.partial.event: change
frmTest: frmTest
frmTest:inpresx: stackoverflow
javax.faces.ViewState: 9209410033241115450:-5298602114910206430
I have trouble understanding the operation of the #ApplicationScope when the request is made by Ajax.
I created a small example to facilitate understanding. Where I have:
A slider changes a variable called sliderValue in a ManagedBean of #ApplicationScope, the value is set by Ajax.
A poll which is updating the panelGrid to always get the updated value of sliderValue, the update is done by Ajax.
Theoretically all users from accessing this page, should have the same value for sliderValue, and if a user changes the value of the slider, all others should receive the change, right?
But this does not occur. Apparently, when the update is made via Ajax, he is behaving like a ManagedBean of #SessionScope.
When I change the value of the sliderValue, it is being changed in ManagedBean correctly, but other users do not receive the update via the update performed bypoll.
I can only update the sliderValuevalue if I give a REFRESH in the browser and do a full REFRESH the page.
Has anyone experienced similar problem?
index.xhtml
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:p="http://primefaces.org/ui">
<h:head>
<title>Menu</title>
</h:head>
<h:body>
<h:form prependId="false" id="form1" >
<h:panelGrid id="panelGrid1" columns="1" style="margin-bottom: 10px">
<p:inputText id="txt1" value="#{menuManagedBean.sliderValue}" />
<p:slider id="slider1" for="txt1" >
<p:ajax event="slideEnd" process="txt1" />
</p:slider>
</h:panelGrid>
<p:poll id="poll1" widgetVar="varPool1" async="true" autoStart="true" interval="2" update="panelGrid1" />
</h:form>
</h:body>
</html>
MenuManagedBean.java
import java.io.Serializable;
import java.util.Date;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Named;
#ApplicationScoped
#Named(value = "menuManagedBean")
public class MenuManagedBean implements Serializable {
private int sliderValue;
public MenuManagedBean() {
}
public int getSliderValue() {
System.out.println(new Date() + " - get: " + sliderValue);
return sliderValue;
}
public void setSliderValue(int sliderValue) {
this.sliderValue = sliderValue;
System.out.println(new Date() + " - set: " + sliderValue);
}
}
The <p:poll> submits/processes by default the entire form as in <p:poll process="#form">. Including the current slider value. You should have noticed it by an unnecessary set method call. Every open view submits its own current slider value during the poll. That's why every open view only gets to see its own slider value (leaving race conditions outside consideration when there are "many" open views).
Tell <p:poll> to only process itself, not the entire form.
<p:poll process="#this" ... />
Unrelated to the concrete problem: don't use prependId="false" ever. Get rid of it.
I'm trying to use a p:ajax tag and then in that listener, i'm setting a value called "periodRendered". then i'm trying to update an h:outputLabel tag via an update from the p:ajax tag. It's not updating ajaxily and i'm thinking it's because a primefaces ajax tag can't update a standard jsf outputLabel tag.
Is my assumption correct and is there a more appropriate tag i should be using instead of h:outputLabel ?
<h:outputLabel for="addProgramTo" value="Add Program To" />
<p:selectOneMenu value="#{ppBacker.grantProgram.grant_project_id}" id="addProgramTo" size="1" styleClass="listBoxMedium">
<p:ajax process=":addProgram:addProgramTo" update=":addProgram:periodGrid, :addProgram:periodLabel" event="change" listener="#{ppBacker.addProgramListener}" />
<f:selectItems value="#{ppBacker.grantProjectDropDownList}" />
</p:selectOneMenu>
<h:outputLabel for="period" value="Period" id="periodLabel" rendered="#{ppBacker.periodRendered}">
You cannot update elements which are not rendered , rendered=false "is a JSF way to" to remove elements from the DOM Tree ,
its not like css display:none or visibility:hidden <- this two will keep the elements in the DOM tree but hidden , while the JSF rendered=false wont even render (keep) the element in the DOM tree (you wont even see it in the "view source" of the page)
So in you case you need to wrap the outputLabel with `panelGroup' and update the id of the wrapper
<h:panelGroup id="periodLabelWrapper">
<h:outputLabel for="period" value="Period" id="periodLabel" rendered="#{ppBacker.periodRendered}">
</h:panelGroup>
and refer to the wrapper (which always be in the DOM tree) id in the <p:ajax update attribute, like this:
<p:ajax process=":addProgram:addProgramTo" update=":addProgram:periodGrid, :addProgram:periodLabelWrapper" event="change" listener="#{ppBacker.addProgramListener}" />
Another solution would be the update the entire form like this <p:ajax update="#form" ... that way you don't need the wrap the outputLabel
regarding your comment question
how does #form update the un-rendered elements, but targeting them directly through id's does not?
You can't target an element in update which is not present in the page (rendered=false) "its not there"
But when you use update="#form" or update="someWrapperID" the form/wrapper "re evaluates" all its inner elements , including the ones with rendered="#{someBean.someCondition}" and the condition gets "re evaluated" so if it result to true the elemet will be displayed...
I'm not sure the reason for this behavior, but I'm putting money on the fact that p:ajax is going to require an active target for the update to work. Here's an example of it working/not working:
<h:body styleClass="center">
<f:view contentType="text/html">
<h:form id="test">
<h:outputLabel for="addProgramTo" value="Add Program To: " />
<h:selectOneMenu id="addProgramTo" value="#{testScope.target}">
<p:ajax update=":test:periodLabel, :test:wrapper" event="change" process="#form">
</p:ajax>
<f:selectItems value="#{testScope.values}"/>
</h:selectOneMenu>
<hr />
<p:outputPanel id="wrapper">
<h:outputLabel value="Works, has a target" rendered="#{testScope.timeToRender}"/>
</p:outputPanel>
<hr />
<h:outputLabel value="does not work" id="periodLabel" rendered="#{testScope.timeToRender}" />
</h:form>
</f:view>
</h:body>
//Bean...
#ViewScoped
#ManagedBean(name = "testScope")
public class TestScope
{
private String[] values = { "False", "What?", "True" };
private String target = "Nothing";
/**
* #return the target
*/
public String getTarget()
{
return target;
}
/**
* #return the values
*/
public String[] getValues()
{
System.out.println(values);
return values;
}
public boolean isTimeToRender()
{
return "True".equals(target);
}
/**
* #param target the target to set
*/
public void setTarget(String target)
{
this.target = target;
}
}
I don't have the time to dig into this one, but from a quick look (again QUICK) in the debugger it looks like it doesn't find the ID (it isn't rendered) so it doesn't get the update. Why? I'm not sure--the easy workaround is to place it inside something you can update (p:outputPanel) and render it as a span, then your code should work. Is this acceptable? Not really...
If you can deal with a full form update the following will work as well ->
<p:ajax update=":test" event="change" process="#form">
Since you're targeting the enclosing form it updates all the children. If I was doing this, that is the workaround I'd use. It isn't a lot of data and it is a heck of a lot cleaner. I'm not sure if this a bug or a feature. It feels buggy though.
Short answer: yes you can target a label. Long answer: you can't target one that isn't rendered. I really hope this helps.
I've got a JSF page with a ui:repeater tag that simply displays a list of strings and some controls to add a string to a list. When adding a string I use ajax to update the repeater tag and have the new string be shown immediately without the page refresh. Here's how my page looks like:
<h:body>
<h:form>
<p:inputText id="name" value="#{testController.newString}"/>
<p:commandButton value="Add" actionListener="#{testController.addString}" update="strings" />
</h:form>
<h:panelGroup id="strings">
<ui:repeat var="str" value="#{stringModel.strings}" varStatus="stringData">
<div>
<h:outputText value="#{str}" />
<h:inputText value="#{str}" />
</div>
</ui:repeat>
</h:panelGroup>
</h:body>
Everything works except the inputText component. After ui-repeater is updated with Ajax is still displays the text from the previous string. For example, assume that initially i have a list with 2 strings, "val1" and "val2". I enter a new string called "val3" and submit the form. List is updated correctly on the server side and the repeater is updated, it now has 3 elements. However, while the h:outputText in the newly added element will correctly show "val3", the inputText will be displayed with "val2" as a value. So i end up with something looking like this:
output tag input tag
val1 val1
val2 val2
val3 val2 (???)
The backing beans are very simple:
A view scoped model bean
#Component
#Scope("view")
public class StringModel {
private List<String> strings = Lists.newArrayList("Value 1");
public List<String> getStrings() {
return strings;
}
public void setStrings(List<String> strings) {
this.strings = strings;
}
}
And a request scoped controller bean:
#Component
#Scope("request")
public class TestController {
private String newString;
#Autowired private StringModel model;
public void addString() {
model.getStrings().add(newString);
}
public String getNewString() {
return newString;
}
public void setNewString(String newString) {
this.newString = newString;
}
}
I did some testing and this actually works the same way for any input component, be that textInput, textArea, etc. Any help would be highly appreciated.
I can't tell in detail exactly why it displays the wrong value after update (it'll be that the internal loop index of <ui:repeat> is broken — try a newer Mojarra version), but just referencing the string item by index from varStatus works. It'll also immediately fix the future problem of being unable to submit the edited string value when you put this list in a form, because the String class is immutable and doesn't have a setter.
<ui:repeat value="#{stringModel.strings}" var="str" varStatus="loop">
<div>
<h:outputText value="#{str}" />
<h:inputText value="#{stringModel.strings[loop.index]}" />
</div>
</ui:repeat>
EditableValueHolders inside ui:repeat are broken (by design) in the current version o JSF specs. It will not work, there is no way to fix it. Maybe new versions will make ui:repeat a proper component with support for saving states of its children. Maybe not.
If you change ui:repeat to h:dataTable, things should work (if not, then your problem is somewhere else and I was wrong).
Frankly, there is no workaround apart from using repeaters from some other libraries - you should find working repeaters in Tomahawk, Trinidad and many other places. Primefaces, AFAIR, does not have a pure repeater.
I also had exactly the same problem before. I solved it by putting the inputText in a form. I also copied your codes and put the h:inputText inside a h:form and it worked as well.
<h:form>
<ui:repeat value="#{stringModel.strings}" var="str" varStatus="loop">
<div>
<h:outputText value="#{str}" />
<h:inputText value="#{str}" />
</div>
</ui:repeat>
</h:form>