I'm making a datatable where you can add exrta rows. In this extra rows, there's a inputtext. After putting something in the inputtext, this value should be saved to a list. But it's not. I tried in the method below using Sting as a parameter, but then to a list was saved : "javax.faces.component.html.HtmlInputText#23sdf" and thing like that. When I changed method to javax.faces.component.html.HtmlInputText method below returns null.
here's code of table:
<h:dataTable value="#{correct.newPowody}" var="z" binding="#{table}" >
<h:inputText binding="#{custom}" value="#{z}">
<f:ajax event="blur" listener="#{correct.newPowodyAdder(table.rowIndex, custom)}"
execute="#this" render="#form"/>
</h:inputText>
</h:dataTable>
here is my correct bean code:
public void newPowodyAdder(int rowIndex, javax.faces.component.html.HtmlInputText powod) {
String value = powod.getValue().toString();
this.newPowody.set(rowIndex, value);
}
}
this method just should add data from inputtext into a list. Thank you for help
This code really isn't making any sense. It's an overcomplicated attempt to fix the simple problem of String class being immutable. Don't use <h:inputText value="#{string}". Just use brace notation [] in EL to reference a list item by index like so value="#{bean.strings[index]}":
<h:dataTable binding="#{table}" value="#{correct.newPowody}">
<h:column>
<h:inputText value="#{correct.newPowody[table.rowIndex]}" />
</h:column>
</h:dataTable>
That's all. No ajax listener method mess necessary.
See also:
Using <ui:repeat><h:inputText> on a List<String> doesn't update model values
Related
I want the use to enter one or more value to the JSF inputText components and then put them in a List :
private List<String> stringList= new ArrayList<String>();
I want to do something like this :
<c:forEach id="myData" items="#{documentController.listeColonnes}" var="address" varStatus="loop">
<h:outputLabel value="#{address}" />
<h:inputText value="#{documentController.stringList[loop.index]}"/>
</c:forEach>
ListeColonnes has label values (ID,prenom .... )
I want to enter this values(12,sam,....) in a new arrayList ()
Any idea !
Ok, I got the same problem and I managed to make it work, after several trials and errors. This is was my case:
I wanted to create a dynamic UI that can be configured with JSON, so a took that data and put it inside a class called property.
After that, I need to collect the data given by the user to store it in a DB.
This was my approach:
JSF
<h:form id="forma_po" styleClass="form-body" prependId="false">
<c:forEach items="#{purchaseOrderPrinterBean.properties}" var="property">
<p:outputLabel value="#{message_report[property.label]}:"/>
<br/>
<p:inputTextarea id="#{property.name}" rows="#{property.lines}"
cols="#{property.numberOfCharacters}" required="true"
maxlength="#{property.lines * property.numberOfCharacters}"
autoResize="false" rendered="#{property.type == 'TEXT_AREA'}"
value="#{purchaseOrderPrinterBean.values[property.name]}">
<p:ajax event="change" listener="#{purchaseOrderPrinterBean.fieldValue}"/>
</p:inputTextarea>
<p:calendar id="popup_#{property.name}" rendered="#{property.type == 'DATE'}"
value="#{purchaseOrderPrinterBean.values[property.name]}" required="true"
pattern="#{property.format}" effect="slideDown">
<p:ajax event="dateSelect" listener="#{purchaseOrderPrinterBean.fieldValue}"/>
</p:calendar>
<br/>
</c:forEach>
</h:form>
As you can see, if the user had previously saved any data at all the I show it with the value attribute on both p:inputTextarea and p:calendar, this purchaseOrderPrinterBean.values[property.name] was of course a java.util.HashMap<String, Object> field, a problem I encounter with this was when the user gave or change the information, the values field did not update at all, hence the <p:ajax /> tags inside the p:inputTextarea and p:calendar.
Bean
In the bean I had this:
public void fieldValue(AjaxBehaviorEvent e) {
if (e.getSource() instanceof UIInput) {
UIInput input = (UIInput) e.getSource();
Object value = input.getValue();
String id = input.getId();
if(StringUtils.startsWith(id, "popup_")) {
id = StringUtils.remove(id, "popup_");
}
values.put(id, value);
}
}
This was the only case I found to make it work, a caution case, and I don't know why, but I had to put the prefix 'popup_' to the calendar, otherwise I got an error for duplicated ids.
Hope it helps, and excuse my bad english.
Try this
<ui:repeat value="#{documentController.listeColonnes}" var="address" varStatus="loop">
<h:outputLabel value="#{address}" />
<h:inputText value="#{documentController.stringList.get(loop.index)}"/>
</ui:repeat>
I have a datatable that iterates over a list, lets call it myList. I populate this myList based on some request parameters. Inside the datatable there are commandLinks. If i put a dummy entry into myList during apply request values phase, i can click on the first commandLink, and it works as it should (it is executed during invoke application phase, and by then the correct entries are in myList). If i dont do it, or i click on the second or later commandLink, nothing happens. So im guessing the clientId of the command button is resolved during apply request phase, even thought it is only used during the invoke application phase, which results in the broken commandLinks.
something like this:
<h:selectManyCheckbox styleClass="hidden"
value="#{cc.attrs.selectionList.selected}"
converter="#{cc.attrs.converter}" >
<f:selectItems value="#{cc.attrs.selectionList.all}"
var="item" itemValue="#{item}" itemLabel="" />
</h:selectManyCheckbox>
<h:dataTable value="#{cc.attrs.selectionList.selectedTest}" var="item">
<h:column>
<h:commandLink value="deselect" action="#{cc.attrs.selectionList.deSelect(item)}">
<f:ajax execute=":#{component.parent.parent.parent.clientId}"
render=":#{component.parent.parent.parent.clientId}" />
</h:commandLink>
</h:column>
</h:dataTable>
and the model:
public List<E> getSelected()
{
return myList;
}
public List<E> getSelectedTest()
{
if(FacesContext.getCurrentInstance().getCurrentPhaseId().equals(PhaseId.RESTORE_VIEW) && getSelectedList().isEmpty())
{
return Collections.singletonList(myList.get(0));
}
else if(FacesContext.getCurrentInstance().getCurrentPhaseId().equals(PhaseId.APPLY_REQUEST_VALUES) && getSelectedList().isEmpty())
{
return Collections.nCopies(2, myList.get(0));
}
else if(FacesContext.getCurrentInstance().getCurrentPhaseId().equals(PhaseId.PROCESS_VALIDATIONS) && getSelectedList().isEmpty())
{
return Collections.nCopies(3, myList.get(0));
}
else if(FacesContext.getCurrentInstance().getCurrentPhaseId().equals(PhaseId.UPDATE_MODEL_VALUES) && getSelectedList().isEmpty())
{
return Collections.nCopies(4, myList.get(0));
}
return myList;
}
public void deSelect(E item)
{
myList.remove(item);
}
with this example, the top two commandLinks of the datatable works.
My question is why is this behaviour, and is there any way around without filling myList with dummy entries? I do not want to use any (viewscoped) backing bean to store the data.
During apply request values phase, JSF needs to iterate over the model in order to find the clicked command link. If the model changes incompatibly during the HTTP request wherein the form submit is processed (the postback) as compared to the initial HTTP request wherein the table with the command links is shown, then JSF may not be able to find the clicked command link and thus never queue the desired action, or the object representing the "current row" is not the same as the enduser intented.
If your bean is request scoped, then it should be written in such way that it initializes selectedTest in the constructor or #PostConstruct method based on some request parameter. At least, you should absolutely not perform business logic in getters.
You can pass the parameters necessary for reconstructing the selectedTest as <f:param> in the command link.
<h:commandLink ...>
<f:param name="some" value="#{bean.some}" />
</h:commandLink>
And prepare the model as follows:
#ManagedProperty
private String some;
#PostConstruct
public void init(){
selectedTest = populateItBasedOn(some);
}
// Don't change standard getters/setters!
See also:
commandButton/commandLink/ajax action/listener method not invoked or input value not updated - point 4
I managed to get my way around by binding the selectManyCheckbox itself to my componentBindings HashMap, and using that for the dataTable (with immediate="true" on the selectManyCheckbox):
<h:selectManyCheckbox immediate="true" styleClass="hidden"
binding="#{componentBindings[cc.attrs.selectionList]}"
value="#{cc.attrs.selectionList.selected}"
converter="#{cc.attrs.converter}" >
<f:selectItems value="#{cc.attrs.selectionList.all}" var="item"
itemValue="#{item}" itemLabel="" />
</h:selectManyCheckbox>
<h:dataTable value="#{componentBindings[cc.attrs.selectionList].value}" var="item">
<h:column>
<h:commandLink value="deselect" action="#{cc.attrs.selectionList.deSelect(item)}">
<f:ajax execute=":#{component.parent.parent.parent.clientId}"
render=":#{component.parent.parent.parent.clientId}" />
</h:commandLink>
</h:column>
</h:dataTable>
in faces-config.xml:
<managed-bean>
<description>Holder of all component bindings.</description>
<managed-bean-name>componentBindings</managed-bean-name>
<managed-bean-class>java.util.HashMap</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
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).
I have a dataTable that lists some objects and I want to set a property for those objects using a selectOneListbox. This is my dataTable
<h:dataTable value="#{someHandler.entities}"
binding="#{someHandler.dataTable}" var="entitiy">
<h:column>
<f:facet name="header">
<t:outputText value="Level" />
</f:facet>
<h:selectOneListbox id="level" value="#{entitiy.level}" size="1"
valueChangeListener="#{someHandler.changeLevel}"
onchange="submit()">
<f:selectItem itemValue="-" itemLabel="-" />
<f:selectItem itemValue="ALL" itemLabel="ALL" />
(and so on)
</h:selectOneListbox>
</h:column>
<h:column>
<f:facet name="header">
<t:outputText value="Name" />
</f:facet>
<h:outputText value="#{entitiy.name}" />
</h:column>
</h:dataTable>
The valueChangeListener looks like this:
public void changeLevel(ValueChangeEvent event) {
String newLevel = (String) event.getNewValue();
Logger logger = (Logger) dataTable.getRowData();
logger.setLevel(Level.toLevel(newLevel));
}
(dataTable is an HtmlDataTable object.)
However, the event object is always the same - no matter which row the selectOneListbox was in. (It seems always the logger in the first row). The Logger object I get is also not the one I want.
Any ideas? Thank you!
And anothers questions? Is the entitiy.setLevel() method called even though I have a valueChangeListener? I use entitiy.level because I want to show the chosen level as a default for those entity.
Thank you!
There are two potential problems here:
First, the onchange="submit()" submits the entire form. The valueChangeListener will be invoked on all input elements of which the submitted value differs from the initial value.
You need to preset the value behind #{entitiy.level} with the same value as the default menu option. E.g. in the constructor.
public Entity() {
level = "-";
}
Or, better, make the default value null.
<f:selectItem itemValue="#{null}" itemLabel="-" />
so that the valueChangeListener won't be invoked when the particular menu is not changed.
Or, when you are already on JSF 2.x (please always mention exact JSF impl/version in your JSF questions), you can use <f:ajax> tag for this without the need for a valueChangeListener with a hacky onchange="submit()".
Second, you need to ensure that the datatable value #{someHandler.entities} returns exactly the same list during the submit as it was during the initial request. So, do the data loading in the bean (post)constructor. In JSF 2.x you'd like to put the bean in the view scope as well.
Unrelated to the concrete problem, you can also just use <h:selectOneMenu> instead of a <h:selectOneListbox size="1">.
I have a JSF datatable with a bunch of rows, with each row having a selectOneMenu inside of it like this:
<h:form
<h:dataTable id="myTable"
binding="#{myBean.dataTable}"
value="#{myBean.dataTableRows}" var="row"
first="0" rows="0" dir="LTR" frame="hsides" rules="all">
<h:column>
<f:facet name="header">
<h:outputText value="Sample Name" />
</f:facet>
<h:outputText value="#{row.sampleName}" />
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="Role" />
</f:facet>
<h:selectOneMenu value="#{row.role}"
id="roleInput">
<f:selectItems value="#{myBean.allRoles}" />
</h:selectOneMenu>
</h:column>
</h:dataTable>
<h:commandButton value="Save" action="#{myBean.save}" />
</h:form>
However, I can't seem to figure out how get the selected role out of each row in the save method. In other words, I want to save each row's value. I saw this article, which explains how to save an input text box:
http://balusc.blogspot.com/2006/06/using-datatables.html#EditableDatatable
but it doesn't seem to apply to the h:selectOneMenu case. Does anyone have any example code that does this?
Thanks!
I see your table has binding to your bean. In your bean you can use the getDataTable() method and access it. Java doc says:
public Object getRowData()
Return the data object representing the data for the currently selected row index, if any.
So if you do your code like:
List<String> selectedRowData = (List<String>) getDataTable().getRowData()
You can then access all the fields the user has chosen. Im using this in my own project and its working. The only difference is that Im casting to my own type instead of List<String>
There are no obvious errors in the form - if your save method is not being invoked, try adding a messages tag to your form to help track down the source of the problem. It would help if you posted a sample bean that reproduces the problem and state the JSF implementation and version you are using.