I have a code like this:
<ui:repeat value="#{myController.fileList}}" var="element" >
<t:inputFileUpload value="#{myController.uploadedFile[element]}" />
</ui:repeat>
but this returns an error : java.lang.NumberFormatException: For input string: "[0]}". UploadedFile is an array of Files. When i wrote code like this
...
<t:inputFileUpload value="#{myController.uploadedFile[0]}" />
...
everything is ok, but i want to have there values between 0-8, not only '0', and this has to be returned from my controller. So how to convert this 'element' to int value inside <t:inputFileUpload value="#{myController.uploadedFile[element]}" />? Thanks for help.
You can use the varStatus attribute to obtain the loop status.
<ui:repeat value="#{myController.fileList}" var="element" varStatus="loop">
<t:inputFileUpload value="#{myController.uploadedFile[loop.index]}" />
</ui:repeat>
(only available in JSF 2.x by the way, in JSF 1.x your best bet might be c:forEach)
But why don't you just use the following?
<ui:repeat value="#{myController.uploadedFile}" var="uploadedFile">
<t:inputFileUpload value="#{uploadedFile}" />
</ui:repeat>
Update: the fileList must be of type List<Integer> or Integer[] or int[] and the uploadedFile must be of type List<SomeObject> or SomeObject[] to get your initial code to work.
Update 2: here's a small reproducible test snippet:
XHTML:
<h:form>
<h:selectManyCheckbox value="#{bean.selectedIndexes}">
<f:selectItems value="#{bean.selectIndexes}" />
</h:selectManyCheckbox>
<h:commandButton value="submit" />
</h:form>
<ui:repeat value="#{bean.selectedIndexes}" var="selectedIndex">
<p><h:outputText value="#{bean.list[selectedIndex]}" /></p>
</ui:repeat>
Bean:
private List<SelectItem> selectIndexes; // +getter
private List<Integer> selectedIndexes; // +getter +setter
private List<String> list; // +getter
public Bean() {
selectIndexes = new ArrayList<SelectItem>();
selectIndexes.add(new SelectItem(0, "one"));
selectIndexes.add(new SelectItem(1, "two"));
selectIndexes.add(new SelectItem(2, "three"));
selectIndexes.add(new SelectItem(3, "four"));
selectedIndexes = new ArrayList<Integer>();
list = Arrays.asList("one", "two", "three", "four");
}
Works fine here on Mojarra 2.0.3 and Apache Tomcat 6.0.29. You only need to substitute selectedIndexes as fileList and list as uploadedFile.
Related
I created a very simple example based on my project in order to illustrate my doubt. Just a way to register a person with a list of telephone numbers.
MainController.java
private String name;
private List<Phone> phoneList;
// Getters and Setters
#PostConstruct
private void init() {
phoneList = new ArrayList<>();
}
public static class Phone implements Serializable {
private String number;
// Getters and Setters
#Override
public String toString() {
return number != null ? number : "null";
}
}
public void add() {
phoneList.add(new Phone());
}
public void save() {
System.out.println("Name: " + name + "; " + phoneList.toString());
}
index.xhtml
<h:form>
<h:inputText value="#{mainController.name}" required="true" />
<ui:repeat var="phone" value="#{mainController.phoneList}" varStatus="status">
<h:inputText value="#{phone.number}" required="true" />
</ui:repeat>
<h:commandButton action="#{mainController.add()}" value="Add Phone" immediate="true" />
<h:commandButton action="#{mainController.save()}" value="Save" />
</h:form>
In my example, note that all phone fields that are added MUST be filled in (required = true).
The problem is: when I type name and click add (to add a phone) the value of the field is maintained. But when I type a first phone and click add, the phone's value is not maintained. This occurs for all fields within the component ui:repeat.
Is there a way to preserve the input values within a after an immediate request, as with the name field?
Extra note: Other strange behavior I noticed is when add at least two phone fields, let the first blank and fills the second, and saves the form. After a failed validation (due to phone blank), click add will make all fields are filled with the value of the second phone.
Wildfly 9.0.2, JSF Api (Jboss) 2.2.12
Thanks to #BalusC comment. The OmniFaces library has two taghandlers that can be used in this case. In both cases input values will be preserved in case of validation failure. Note that h:commandButton should be with <h:commandButton immediate="false" />.
ignoreValidationFailed
In this case all validation failures will be ignored (including converter failures). Note that the h:form have to be changed to o:form. Also, the failures messages will still be displayed, which can be solved putting a proper condition in the rendered attribute. The files will look like this:
index.xhtml
<o:form>
<h:inputText value="#{mainController.name}" required="true" />
<ui:repeat var="phone" value="#{mainController.phoneList}" varStatus="status">
<h:inputText value="#{phone.number}" required="true" />
</ui:repeat>
<h:commandButton action="#{mainController.add()}" value="Add Phone">
<o:ignoreValidationFailed />
</h:commandButton>
<h:commandButton action="#{mainController.save()}" value="Save" />
</o:form>
<h:messages rendered="#{facesContext.validationFailed}" />
skipValidators
In this case only the validation failures will be ignored (the converters will still run). The failures messages will not be displayed, except for the converters. Note that this taghandler is only available since the 2.3 version. The files will look like this:
index.xhtml
<h:form>
<h:inputText value="#{mainController.name}" required="true" />
<ui:repeat var="phone" value="#{mainController.phoneList}" varStatus="status">
<h:inputText value="#{phone.number}" required="true" />
</ui:repeat>
<h:commandButton action="#{mainController.add()}" value="Add Phone">
<o:skipValidators />
</h:commandButton>
<h:commandButton action="#{mainController.save()}" value="Save" />
</h:form>
The solution that I use to this problem is to create an external field to the loop, which stores a JSON containing the values that should be saved. This field, to be outside the loop, properly saves values after each try and restore the missing values when necessary. I use two functions JavaScript and JQuery library.
So the files would look like this:
index.xhtml
<h:outputScript library="jquery" name="jquery.min.js" />
<h:outputScript library="all" name="all.js" />
<h:form>
<h:inputText value="#{mainController.name}" required="true" />
<ui:repeat var="phone" value="#{mainController.phoneList}" varStatus="status">
<h:inputText styleClass="savePhoneNumber" value="#{phone.number}" required="true" onchange="saveUiRepeatInput('#{allPhoneNumber.clientId}', 'savePhoneNumber')" />
</ui:repeat>
<h:inputHidden id="allPhoneNumber" binding="#{allPhoneNumber}" />
<h:outputScript>loadUiRepeatInput('#{allPhoneNumber.clientId}', 'savePhoneNumber')</h:outputScript>
<h:commandButton action="#{mainController.add()}" value="Add Phone" immediate="true" />
<h:commandButton action="#{mainController.save()}" value="Save" />
</h:form>
all.js
function saveUiRepeatInput(inputAll, inputClass) {
document.getElementById(inputAll).value = JSON.stringify($('.' + inputClass).map(function() { return this.value; }).get());
}
function loadUiRepeatInput(inputAll, inputClass) {
var jsonAll = document.getElementById(inputAll).value;
if (jsonAll) {
var array = JSON.parse(jsonAll);
$('.' + inputClass).each(function(i) { if (i < array.length) this.value = array[i]; });
}
}
Although work perfectly (including via ajax, with some minor changes), it looks like a hack, not an ideal solution. So if anyone can help with any solution strictly based on JSF, I will be grateful. Thanks.
I have the below form:
<h:form>
<a4j:repeat var="drillDownSet" value="#{drilldownRequest.itemDetailMap.entrySet().toArray()}">
<rich:select>
<f:selectItems value="#{drillDownSet.value}" var="val"
itemValue="#{drillDownSet.key}" itemLabel="#{val}" />
</rich:select>
</a4j:repeat>
<h:commandButton value="Summit" action="#{drilldownRequest.itemSpecSummit}" />
</h:form>
And the below backing bean:
#Named
#RequestScoped
public class DrillDownRequest {
Map<String, ArrayList> itemDetailMap;
public void itemSpecSummit() {
// How can I retrieve all the selected values from the dropdown menus here?
}
}
How can I retrieve all selected values of the <rich:select> in the action method?
First of all, your <f:selectItems itemValue> is broken. It's the same for all options. This isn't right. Provided that the ArrayList is actually a List<String>, just this should do:
<f:selectItems value="#{drillDownSet.value}" />
See also:
https://stackoverflow.com/tags/selectonemenu/info
Coming back to the concrete question, and continuing with the above fix, you just need to bind the <rich:select value> to another Map<String, String> using #{drillDownSet.key} as map key and the <f:selectItems itemValue> as map value.
private Map<String, String> selectedItems = new HashMap<>(); // +getter (setter not necessary)
<a4j:repeat var="drillDownSet" value="#{drilldownRequest.itemDetailMap.entrySet().toArray()}">
<rich:select value="#{drilldownRequest.selectedItems[drillDownSet.key]}">
<f:selectItems value="#{drillDownSet.value}" />
</rich:select>
</a4j:repeat>
I' have a simple problem with adding values from inputs to the ArrayList.
I have a POJO like this:
public class Person {
private String firstName;
private String lastName;
private List<String> friends=new ArrayList<>();
//getters and setters
then Backing bean:
public class backingBean{
Person p=new Person();
public void addPerson(){
for(String friend:p.getFriends)
System.out.println(friend);
}
}
and the view
<h:form>
<fieldset>
<h:panelGrid columns="2">
<h:outputText value="Name" />
<h:inputText value="{backingBean.person.firstName}"/>
<h:outputText value="LastName" />
<h:inputText value="#{backingBean.person.lastName}"/>
<h:outputText value="Friends" />
<h:inputText value="#{backingBean.person.friends}" />
<h:inputText value="#{backingBean.person.friends}" />
</h:panelGrid>
<h:commandButton value="Add"
action="#{backingBean.addPerson}" />
</fieldset>
</h:form>
When I try to addPerson I get this error:
summary=(Conversion Error setting value...
I don't understand why convert String to String?
You can't bind value of h:inputText to ArrayList (without converter). When you submit form (by clicking button) JSF tries to call setFriends(String) and this is where this Exception occurs. Try to figure out what you are trying to achieve with these two h:inputText elements.
if you want to add 2 friends, just create only 2 different variables in backing bean as :
private String friend1;
private String friend2;
and then add them in addPerson like this:
List<String> friends=new ArrayList<String>();
friends.add(friend1);
friends.add(friend2);
p.setFriends(friends);
Not tested can be some bugs.
EDIT:
And if this not satisfies you, you can look at this #BalusC ANSWER
I want to change the inputTexts' values when I choose another Skin from my selectOneMenu.
Everything is doing well, my Converter gives back the right Object from the menu, but the inputTexts are not updated.
<h:form>
<h:selectOneMenu id="dropdownSkin"
value="#{helloBean.currentSkin}" defaultLabel="Select a skin.."
valueChangeListener="#{helloBean.skinValueChanged}" immediate="true"
onchange="this.form.submit()" converter="SkinConverter" >
<f:selectItems value="#{helloBean.mySkinsSI}" var="c"
itemValue="#{c.value}" />
</h:selectOneMenu>
<br />
<h:inputText id="name" value="#{helloBean.currentSkin.title}"></h:inputText>
<br />
<h:inputText id="tcolor" value="#{helloBean.currentSkin.titleBar.textColor}"></h:inputText>
<br />
<h:inputText id="bcolor" value="#{helloBean.currentSkin.titleBar.backgroundColorStart}"></h:inputText>
</h:form>
Here is what my Bean looks like. I debugged it and the Object currentSkin is set correctly. Now i need to know how to update the textfields content.
#ManagedBean
#SessionScoped
public class HelloBean implements Serializable {
private static final long serialVersionUID = 1L;
private List<ExtendedSkin> mySkins;
private List<SelectItem> mySkinsSI;
private ExtendedSkin currentSkin;
public void skinValueChanged(ValueChangeEvent e) {
currentSkin = (ExtendedSkin) e.getNewValue();
FacesContext.getCurrentInstance().renderResponse();
}
public List<ExtendedSkin> getMySkins() {
mySkins = XMLParser.readExtendedSkins();
return mySkins;
}
public List<SelectItem> getMySkinsSI() {
mySkinsSI = new LinkedList<SelectItem>();
for (ExtendedSkin s : getMySkins()) {
mySkinsSI.add(new SelectItem(s, s.getTitle()));
}
return mySkinsSI;
}
public void setMySkinsSI(List<SelectItem> myItems) {
this.mySkinsSI = myItems;
}
public ExtendedSkin getCurrentSkin() {
if (currentSkin == null) {
currentSkin = getMySkins().get(0);
}
return currentSkin;
}
public void setCurrentSkin(ExtendedSkin currentSkin) {
this.currentSkin = currentSkin;
}
}
The problem here is that the converter is doing its work filling the helloBean.currentSkin object, but the values in the <h:inputText> that are bounded to this helloBean.currentSkin: title, textColor and backgroundColorStart will be send to the server and replace the actual values that were loaded by the converter. In other words:
The converter is executed and builds the helloBean.currentSkin based on the selected value.
The <h:inputText id="name"> empty value is sent to server and will be injected in helloBean.currentSkin.title. Same behavior for the other 2 <h:inputText>s.
The view will be loaded using the selected helloBean.currentSkin and it will load the helloBean.currentSkin.title with the empty value. Same behavior for the other 2 <h:inputText>s.
There are two possible solutions to this problem:
Move the <h:inputText>s outside the form, so the empty values won't be send to the server. When loading the view, it will maintain the values loaded in the converter.
<h:form>
<h:selectOneMenu id="dropdownSkin"
value="#{helloBean.currentSkin}" defaultLabel="Select a skin.."
valueChangeListener="#{helloBean.skinValueChanged}" immediate="true"
onchange="this.form.submit()" converter="SkinConverter" >
<f:selectItems value="#{helloBean.mySkinsSI}" var="c"
itemValue="#{c.value}" />
</h:selectOneMenu>
</h:form>
<br />
<h:inputText id="name" value="#{helloBean.currentSkin.title}"></h:inputText>
<!-- rest of Facelets code... -->
Since you're loading the helloBean.currentSkin while changing the selected value on your dropdownlist, you can add ajax behavior using <f:ajax> tag component inside the <h:selectOneMenu> and update the fields in a cleaner way. I would opt for this solution.
<h:form>
<!-- Note that there's no need of the onchange JavaScript function -->
<h:selectOneMenu id="dropdownSkin"
value="#{helloBean.currentSkin}" defaultLabel="Select a skin.."
valueChangeListener="#{helloBean.skinValueChanged}" immediate="true"
converter="SkinConverter" >
<f:selectItems value="#{helloBean.mySkinsSI}" var="c"
itemValue="#{c.value}" />
<f:ajax process="#this" render="name tcolor bcolor" />
</h:selectOneMenu>
<br />
<h:inputText id="name" value="#{helloBean.currentSkin.title}" />
<h:inputText id="tcolor" value="#{helloBean.currentSkin.titleBar.textColor}" />
<br />
<h:inputText id="bcolor"
value="#{helloBean.currentSkin.titleBar.backgroundColorStart}" />
</h:form>
You can learn more about <f:ajax> in online tutorial like this one.
Since you're going to use an ajax call in your page, you should change your managed bean scope from #SessionScoped to #ViewScoped. More info about this here: Communication in JSF 2
I have a List of strings in my object, and I was hoping to be able to edit them with an a4j:repeat.
Below is my code:
<a4j:repeat value="#{Controller.object.stringList}" var="item" >
<h:panelGrid columns="2">
<h:outputLabel value="ID:" />
<h:inputText value="#{item}" />
</h:panelGrid>
</a4j:repeat>
My problem is that the values of the items never get changed when I submit my form. I've had a look at the livedemo but even after adding the UpdateBean, it still didn't work.
Any ideas?
The objects in your repeat need to follow the bean standard if you want to write back to them. I'm guessing that they are just Strings in your example?
Try this:
public class StringBean {
private String value;
public void setValue(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
along with:
<h:inputText value=#{item.value} />