JSF UIRepeat and PostBack - jsf

I have a simple page where a I use <ui:repeat> and it gets the value from a backing bean.
The initial request will give it an empty list. The postback then will invoke an action that will change the model behind the <ui:repeat> but it is not rendered?!
I debugged through it and I saw that the <ui:repeat> evaluates the value at restore view phase but thats it. When it reaches render response it does not use the latest value from my bean. Is that the expected behavior?
How can I make that work? Do I have to write my own repeat tag?

I can't really tell what could be the problem without some of your code, but these are the basics:
Backing bean:
public class ObjectService{
private DataModel objectDataModel;
private List<Object> objectList;
private Pagination paginationHelper;
private ObjectDao objectDao = new ObjectDao();
private String queryOption;
public void setQueryOption(String queryOption){
this.queryOption = queryOption;
}
public String getQueryOption(){
return this.queryOption;
}
public <E> PaginationHelper getPagination(final List<E> list) {
pagination = new PaginationHelper(10) {
#Override
public int getItemsCount() {
return list.size();
}
#Override
public DataModel createPageDataModel() {
return new ListDataModel(list);
}
};
return pagination;
}
public void setPagination(PaginationHelper pagination) {
this.pagination = pagination;
}
public List<Object> getObjectList(){
this.objectList = objectDao.readObjectsWhere(queryOption);
return this.objectList;
}
public void setObjectList(List<Object> objectList){
this.objectList = objectList;
}
public DataModel getObjectDataModel(){
if (objectDataModel == null) {
objectDataModel = getPagination(getObjectList()).createPageDataModel();
}
return objectDataModel;
}
public void setObjectDataModel(DataModel objectDataModel){
this.objectDataModel = objectDataModel
}
public String changeModel(){
objectDataModel = null;
return null;
}
}
XHTML page:
...
<h:form>
<fieldset>
<label>
<span>Option:</span>
<h:inputText value="#{objectService.queryOption}" />
</label>
<h:commandButton action="#{objectService.changeModel}" value="request data" />
</fieldset>
<ui:repeat value="#{objectService.objectDataModel}" var="objectVar">
<h:outputLabel value="#{objectVar.property1}" />
<h:outputLabel value="#{objectVar.property2}" />
<h:outputLabel value="#{objectVar.property3}" />
</ui:repeat>
</h:form>
...

Related

JSF ui repeat : Array index out of range: 0 [duplicate]

I would like to know if it possible to push a value from inside a <ui:repeat> to a map, a set or a list?
I would like to pass the value of the <h:inputtext> to a set.
Code:
<ui:repeat var="_par" value="#{cmsFilterParameterHandler.normaleSuchParameter()}">
<p:outputLabel value="#{_par.bezeichnung}" />
<p:spacer width="5px" />
<p:inputText id="me" value="#{??? push me to a set ???}"/>
<br /><br />
</ui:repeat>
With a Set, it is not possible as it doesn't allow referencing items by index or key. It's however possible with a List and a Map by just specifying the list index and map key in the input value.
With a List:
private List<String> list; // +getter (no setter necessary)
#PostConstruct
public void init() {
list = createAndFillItSomehow();
}
<ui:repeat value="#{bean.list}" varStatus="loop">
<h:inputText value="#{bean.list[loop.index]}" />
</ui:repeat>
With a Map (only if your environment supports EL 2.2 or JBoss EL):
private Map<String, String> map; // +getter (no setter necessary)
#PostConstruct
public void init() {
map = createAndFillItSomehow();
}
<ui:repeat value="#{bean.map.entrySet().toArray()}" var="entry">
<h:inputText value="#{bean.map[entry.key]}" />
</ui:repeat>
Noted should be that the canonical approach is to use a List of fullworthy javabeans. Let's assume a Javabean class named Par with properties id and value which maps exactly to a par table in DB with columns id and value:
private List<Par> pars; // +getter (no setter necessary)
#PostConstruct
public void init() {
pars = createAndFillItSomehow();
}
<ui:repeat value="#{bean.pars}" var="par">
<h:inputText value="#{par.value}" />
</ui:repeat>
Either way, it works as good when using <p:inputText>, it's in no way related to PrimeFaces, it's in the context of this question merely a jQuery based JSF UI component library. Just replace h: by p: to turn it on.
I'm not sure, if I understood your requirements correctly.
I suppose the following: You need a List of Strings in some backend and an ui:repeat tag to iterate over those strings with input-fields to edit them. Maybe there are some syntax-issues, but my idea should be clear:
public class Backend {
private List<String> myStrings;
public MyStringWrapper getMyStringWrapper(int index) {
return new MyStringWrapper(index);
}
public class MyStringWrapper {
private final int index;
public MyStringWrapper(int index) { this.index = index; }
public String getContent() { return myStrings.get(index); }
public void setContent(String newContent) { myStrings.add(index, newContent); }
}
}
In the frontend you use as follows:
<ui:repeat var="_index" value="#{backend.getIndexSequence()}">
<p:inputText value="#{backend.getMyStringWrapper(_index).content}"/>
</ui:repeat>
Of course, you have to provide a getIndexSequence-method which produces a list of ints ranging from 0 to the size of the strings.
Do you mean like this?
<p:inputText id="me" value="#{_par.input}"/>
in BackBean:
public class Par implements Serializable {
private String inputText;
private String bezeichnung;
public Par()
{
}
public void setInput(String input)
{
this.inputText = input;
}
public String getInput()
{
return this.inputText
}
public void setBezeichnung(String bezeichnung)
{
this.bezeichnung = bezeichnung;
}
public String getBezeichnung()
{
return this.bezeichnung
}
}

Using P:CommandButton in PrimeFaces

Originally I have this form for user input and do a search.
<h:form id="wordForm">
<h:panelGrid columns="4">
<h:inputText id="word" "
value="#{wordController.word}" />
<h:message for="word" />
<h:commandButton id="search" value="search"
action="#{wordController.search}" />
</h:panelGrid>
</h:form>
Now I want to use PrimeFaces for autocomplete feature, and this is my new form with Autocomplete. How can I replace the new form with the above form?
<h:form>
<p:growl id="msgs" showDetail="true" />
<h:panelGrid columns="2" cellpadding="5">
<p:autoComplete id="wordForm" value="#{autoCompleteView.query}"
completeMethod="#{autoCompleteView.completeQuery}" var="query"
itemLabel="#{query.displayName}" itemValue="#{query}"
converter="queryConverter" forceSelection="true" />
<p:commandButton value="search" oncomplete="PF('dlg').show()" **action="#{wordController.search}"** />
</h:panelGrid>
</h:form>
More specifically, I think I still need to somehow use "action="#{wordController.search}" in P:CommandAction button so that I don't need to change anything else in backend. But How do I pass the query parameter to the "wordController.word" variable? Because now "action="#autoCompleteView.query" takes the user input.
How can I modify this without significant change to current bean code? Do I have to unify the original search bean WordController with the new AutocompleteView bean? because now the user input is accepted into AutoCompleteView bean.
AutoCompleteView.java
#ManagedBean
public class AutoCompleteView {
private Query query;
#ManagedProperty("#{queryService}")
private QueryService service;
private List<Query> selectedQueries;
public List<Query> completeQuery(String query) {
System.out.println(query);
List<Query> allQueries = service.getQueries();
List<Query> filteredQueries = new ArrayList<Query>();
for (int i = 0; i < allQueries.size(); i++) {
Query skin = allQueries.get(i);
if(skin.getName().toLowerCase().contains(query)) {
filteredQueries.add(skin);
}
}
return filteredQueries;
}
public void onItemSelect(SelectEvent event) {
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Item Selected", event.getObject().toString()));
}
public Query getQuery() {
return query;
}
public void setQuery(Query query) {
this.query = query;
}
public void setService(QueryService service) {
this.service = service;
}
public List<Query> getSelectedQueries() {
return selectedQueries;
}
public void setSelectedQueries(List<Query> selectedQueries) {
this.selectedQueries = selectedQueries;
}
}
Edited per suggestion:
#Named
#RequestScoped
public class WordController {
private String word;
// For AutoComplete suggestions
private Query selectedQuery;
#Inject
private QueryService service;
#Inject
private Word wordObject;
public void search() {
if (word != null && !word.isEmpty()) {
wordObject.searchWord(word);;
...
}else {
System.out.println("Query can't be null!");
}
}
public List<Query> completeQuery(String query) {
List<Query> allQueries = service.getQueries();
List<Query> filteredQueries = new ArrayList<Query>();
for (int i = 0; i < allQueries.size(); i++) {
Query skin = allQueries.get(i);
if(skin.getName().toLowerCase().contains(query)) {
filteredQueries.add(skin);
}
}
return filteredQueries;
}
public String getWord() {
return word;
}
public void setWord(String word) {
this.word = word;
}
public Query getSelectedQuery() {
return selectedQuery;
}
public void setSelectedQuery(Query selectedQuery) {
this.selectedQuery = selectedQuery;
}
}
Question: Originally, my 'word' is filled through an "h:inputText" in JSF view and search() is called in JSF:
<h:commandButton id="search" value="Search!" action="#{wordController.search}" />
Now, how do I get "selectedQuery" from completeQuery() method, and then use it to fill "word" and then call search() method?

How to set submitted values of <h:inputText> inside <ui:repeat> into map, set or list

I would like to know if it possible to push a value from inside a <ui:repeat> to a map, a set or a list?
I would like to pass the value of the <h:inputtext> to a set.
Code:
<ui:repeat var="_par" value="#{cmsFilterParameterHandler.normaleSuchParameter()}">
<p:outputLabel value="#{_par.bezeichnung}" />
<p:spacer width="5px" />
<p:inputText id="me" value="#{??? push me to a set ???}"/>
<br /><br />
</ui:repeat>
With a Set, it is not possible as it doesn't allow referencing items by index or key. It's however possible with a List and a Map by just specifying the list index and map key in the input value.
With a List:
private List<String> list; // +getter (no setter necessary)
#PostConstruct
public void init() {
list = createAndFillItSomehow();
}
<ui:repeat value="#{bean.list}" varStatus="loop">
<h:inputText value="#{bean.list[loop.index]}" />
</ui:repeat>
With a Map (only if your environment supports EL 2.2 or JBoss EL):
private Map<String, String> map; // +getter (no setter necessary)
#PostConstruct
public void init() {
map = createAndFillItSomehow();
}
<ui:repeat value="#{bean.map.entrySet().toArray()}" var="entry">
<h:inputText value="#{bean.map[entry.key]}" />
</ui:repeat>
Noted should be that the canonical approach is to use a List of fullworthy javabeans. Let's assume a Javabean class named Par with properties id and value which maps exactly to a par table in DB with columns id and value:
private List<Par> pars; // +getter (no setter necessary)
#PostConstruct
public void init() {
pars = createAndFillItSomehow();
}
<ui:repeat value="#{bean.pars}" var="par">
<h:inputText value="#{par.value}" />
</ui:repeat>
Either way, it works as good when using <p:inputText>, it's in no way related to PrimeFaces, it's in the context of this question merely a jQuery based JSF UI component library. Just replace h: by p: to turn it on.
I'm not sure, if I understood your requirements correctly.
I suppose the following: You need a List of Strings in some backend and an ui:repeat tag to iterate over those strings with input-fields to edit them. Maybe there are some syntax-issues, but my idea should be clear:
public class Backend {
private List<String> myStrings;
public MyStringWrapper getMyStringWrapper(int index) {
return new MyStringWrapper(index);
}
public class MyStringWrapper {
private final int index;
public MyStringWrapper(int index) { this.index = index; }
public String getContent() { return myStrings.get(index); }
public void setContent(String newContent) { myStrings.add(index, newContent); }
}
}
In the frontend you use as follows:
<ui:repeat var="_index" value="#{backend.getIndexSequence()}">
<p:inputText value="#{backend.getMyStringWrapper(_index).content}"/>
</ui:repeat>
Of course, you have to provide a getIndexSequence-method which produces a list of ints ranging from 0 to the size of the strings.
Do you mean like this?
<p:inputText id="me" value="#{_par.input}"/>
in BackBean:
public class Par implements Serializable {
private String inputText;
private String bezeichnung;
public Par()
{
}
public void setInput(String input)
{
this.inputText = input;
}
public String getInput()
{
return this.inputText
}
public void setBezeichnung(String bezeichnung)
{
this.bezeichnung = bezeichnung;
}
public String getBezeichnung()
{
return this.bezeichnung
}
}

how can i call setter without calling <f:viewparam> converter?

i am using jsf 2.1.1 and primefaces 3.0.M4. i have a sample jsf page that used to post country comments. i use f:viewparam tag with converter to view country pages. here are the codes:
country.xhtml:
<f:metadata>
<f:viewParam name="country" value="#{countryBean2.selectedCountry}" converter="countryConverter" required="true"/>
</f:metadata>
<h:head>
<title>Country</title>
</h:head>
<h:body>
<h:form id="form">
<h:outputText value="#{countryBean2.selectedCountry.countryName}" />
<br/><br/>
<h:outputText value="Comment:" />
<h:inputText value="#{countryBean2.comment}" />
<br/>
<p:commandButton value="Send" action="#{countryBean2.sendComment}" update="#this" />
</h:form>
</h:body>
CountryBean2.java:
#Named("countryBean2")
#SessionScoped
public class CountryBean2 implements Serializable {
private EntityCountry selectedCountry;
private String comment;
public EntityCountry getSelectedCountry() { return selectedCountry; }
public void setSelectedCountry(EntityCountry newValue) { selectedCountry = newValue; }
public String getComment() { return comment; }
public void setComment(String newValue) { comment = newValue; }
EntityManagerFactory emf = Persistence.createEntityManagerFactory("testPU");
public void sendComment() {
EntityManager em = emf.createEntityManager();
try {
FacesMessage msg = null;
EntityTransaction entr = em.getTransaction();
boolean committed = false;
entr.begin();
try {
EntityCountryComment c = new EntityCountryComment();
c.setCountry(selectedCountry);
c.setComment(comment);
em.persist(c);
committed = true;
msg = new FacesMessage();
msg.setSeverity(FacesMessage.SEVERITY_INFO);
msg.setSummary("Comment was sended");
} finally {
if (!committed) entr.rollback();
}
} finally {
em.close();
}
}
}
CountryConverter.java:
public class CountryConverter implements Converter {
public static EntityCountry country = new EntityCountry();
EntityManagerFactory emf = Persistence.createEntityManagerFactory("testPU");
#Override
public EntityCountry getAsObject(FacesContext context, UIComponent component, String value) {
EntityManager em = emf.createEntityManager();
Query query = em.createQuery("SELECT c FROM EntityCountry c WHERE c.countryName = :countryName")
.setParameter("countryName", value);
country = (EntityCountry) query.getSingleResult();
return country;
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
EntityCountry c = (EntityCountry) value;
return c.getCountryName();
}
}
i want to call "setComment" setter without calling CountryConverter, when i am using commandbutton to post comment. how can i do that ?
Unfortunately, that's by design of the <f:viewParam> component. It will convert the request parameter and set the property on every HTTP request, also on postbacks. In order to change this behaviour, you would need to extend <f:viewParam> with a custom component which doesn't remember the initial request parameter in its state. It's relatiely simple, instead of delegating the setSubmittedValue() and getSubmittedValue() to StateHelper, you just need to make it an instance variable. This is described in detail in this blog.
#FacesComponent("com.my.UIStatelessViewParameter")
public class UIStatelessViewParameter extends UIViewParameter {
private String submittedValue;
#Override
public void setSubmittedValue(Object submittedValue) {
this.submittedValue = (String) submittedValue;
}
#Override
public String getSubmittedValue() {
return submittedValue;
}
}
OmniFaces has an ready-to-use component for this in flavor of <o:viewParam>. Here is the live example.

Invoke ActionListener of Backing Component in Composite Component

try to write a composite component that allows mutltiple text inputs. I read that it is possible to define a backing component for a composite component, so I don't have to write a renderer nor a handler. What I couldn't figure out is how to delegate actions declared in composite's xhtml to the backing component. I guess i did not yet quite understand the concept of this. Does anybody has an Idea?
I am using Tomcat 7, EL 2.2, Spring 3, Mojarra 2.1.7
This is the way i'd like to use the component:
<custom:multiInput value="#{backingBean.inputList}"/>
Where the BackingBean.java holds a list of objects:
#Component
#Scope(value="view")
public class BackingBean {
...
private List<Foo> inputList;
....
}
The composite component multiInput.xhtml looks like this:
<cc:interface componentType="MultiInput">
<cc:attribute name="value" required="true" type="java.util.List" />
</cc:interface>
<cc:implementation>
<div id="#{cc.clientId}">
<h:dataTable value="#{cc.attrs.rows}" var="row">
<h:column>
<!-- here will be a selector component in order to select a foo object -->
</h:column>
<h:column>
<h:commandButton value="Remove Row">
<f:ajax execute=":#{cc.clientId}" render=":#{cc.clientId}" listener="#{cc.removeRow(row)}" />
</h:commandButton>
</h:column>
<h:column>
<h:commandButton value="Add Row" rendered="#{cc.lastRow}">
<f:ajax execute=":#{cc.clientId}" render=":#{cc.clientId}" listener="#{cc.addEmptyRow()}" />
</h:commandButton>
</h:column>
</h:dataTable>
</div>
</cc:implementation>
And here the backing component MultiInput.java:
#FacesComponent(value="MultiInput")
public class MultiInput extends UIInput implements NamingContainer, Serializable{
...
#Override
public String getFamily() {
return "javax.faces.NamingContainer";
}
#Override
public void encodeBegin(FacesContext context) throws IOException {
initRowsFromValueAttribute();
super.encodeBegin(context);
}
public void removeRow(MultiInputRow row) {
// why is this method is never reached when clicking remove button?
}
public void addEmptyRow() {
// why is this method is never reached when clicking add button?
}
public ListDataModel<MultiSelectRow> getRows() {
return (ListDataModel<MultiSelectRow>) getStateHelper().eval(PropertyKeys.rows, null);
}
private void setRows(ListDataModel<MultiSelectRow> rows) {
getStateHelper().put(PropertyKeys.rows, rows);
}
...
}
Now - removeRow and addEmptyRow is never called on MultiInput. An ajax request is triggered but it gets lost somewhere. Why?
I think the method signature for ajax listener methods should include the AjaxBehaviorEvent (unverified):
public void addEmptyRow(AjaxBehaviorEvent event) { ... }
and the f:ajax tag should just look like (without parentheses):
<f:ajax execute=":#{cc.clientId}" render=":#{cc.clientId}" listener="#{cc.addEmptyRow}" />
I'm struggling with the same problem here: using <f:ajax>, action listener methods in the composite component backing component are not executed.
It works partially when using Primefaces <p:commandButton>: the action listener method is correctly called in this case. However, the value of the 'process' attribute seems to be ignored in this case: All form fields are submitted, which causes validation failure in my case. If this is not a problem for you, you could try this.
I have created some test classes that reproduce the problem:
The composite component file testComponent.xhtml:
<html xmlns="http://www.w3c.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:p="http://primefaces.org/xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:composite="http://java.sun.com/jsf/composite">
<composite:interface componentType="testComponent">
</composite:interface>
<composite:implementation>
<div id="#{cc.clientId}">
<h:panelGroup id="addPanel">
<h:inputText id="operand1" value="#{cc.operand1}"/>
<h:outputText value=" + " />
<h:inputText id="operand2" value="#{cc.operand2}"/>
<h:outputText value=" = " />
<h:outputText id="result" value="#{cc.result}" />
<br />
<p:commandButton id="testButton1" value="Primefaces CommandButton"
actionListener="#{cc.add()}" process="addPanel" update="addPanel"/>
<h:commandButton id="testButton2" value="f:ajax CommandButton">
<f:ajax execute="addPanel" render="addPanel" listener="#{cc.add()}" />
</h:commandButton>
</h:panelGroup>
</div>
</composite:implementation>
</html>
The backing component class:
package be.solidfrog.pngwin;
import javax.faces.component.FacesComponent;
import javax.faces.component.UINamingContainer;
import javax.faces.event.ActionEvent;
#FacesComponent("testComponent")
public class TestComponent extends UINamingContainer {
private Integer operand1, operand2, result;
public void add() {
System.err.println("Adding " + operand1 + " and " + operand2);
result = operand1 + operand2;
}
public Integer getOperand1() { return operand1; }
public void setOperand1(Integer operand1) { this.operand1 = operand1; }
public Integer getOperand2() { return operand2; }
public void setOperand2(Integer operand2) { this.operand2 = operand2; }
public Integer getResult() { return result; }
public void setResult(Integer result) { this.result = result; }
}
And the using page test.xhtml:
<!DOCTYPE html>
<html xmlns="http://www.w3c.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html" xmlns:p="http://primefaces.org/ui"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:sf="http://java.sun.com/jsf/composite/solidfrog">
<h:body>
<h:messages />
<h:form id="testForm">
<h:outputLabel for="field1" value="Integer field: "/>
<h:inputText id="field1" value="#{testBean.field1}" />
<hr/>
<sf:testComponent id="testComponent" />
</h:form>
</h:body>
</html>
When clicking the first button and filling in the two operand fields, the result is correctly calculated. However, when a non-numeric value is entered in field1, there is a failed verification.
When using the second button, the action listener method is never calculated. However, the complete form is always submitted, so entering a non-numeric value in field1 triggers the error too.
I also tried p:ajax, which behaved the same as f:ajax.
I really have no idea what is happening here. Hopefully someone with more JSF wisdom can help out.
Although I don't understand everything in detail, I found a way to make it work. Since on each request a new instance of the backing component MultiInput is created, I had to save the state by overwriting saveState and restoreState. This way I could keep the property rows as a simple property. I also removed the encodeBegin method and overwrote getSubmittedValue.
At least this way it is working in Mojarra. When using MyFaces with default settings, I got some serialization exceptions, but I did not get deepter into that since we will stick on Mojarra. Also MyFaces seemed to be more stricked with ajax event listeners. It required "AjaxBehaviorEvent" parameters in listener methods.
Here the complete backing component MultInput:
#FacesComponent(value = "MultiInput")
public class MultiInput extends UIInput implements NamingContainer, Serializable {
ListDataModel<MultiInputRow> rows;
#Override
public String getFamily() {
return "javax.faces.NamingContainer";
}
#Override
public Object getSubmittedValue() {
List<Object> values = new ArrayList<Object>();
List<MultiInputRow> wrappedData = (List<MultiInputRow>) getRows().getWrappedData();
for (MultiInputRow row : wrappedData) {
if (row.getValue() != null) { // only if a valid value was selected
values.add(row.getValue());
}
}
return values;
}
public boolean isLastRow() {
int row = getRows().getRowIndex();
int count = getRows().getRowCount();
return (row + 1) == count;
}
public boolean isFirstRow() {
int row = getRows().getRowIndex();
return 0 == row;
}
public void removeRow(AjaxBehaviorEvent e) {
List<MultiInputRow> wrappedData = (List<MultiInputRow>) getRows().getWrappedData();
wrappedData.remove(rows.getRowIndex());
addRowIfEmptyList();
}
public void addEmptyRow(AjaxBehaviorEvent e) {
List<MultiInputRow> wrappedData = (List<MultiInputRow>) getRows().getWrappedData();
wrappedData.add(new MultiInputRow(null));
}
public ListDataModel<MultiInputRow> getRows() {
if (rows == null) {
rows = createRows();
addRowIfEmptyList();
}
return rows;
}
public List<Object> getValues() {
return (List<Object>) super.getValue();
}
private ListDataModel<MultiInputRow> createRows() {
List<MultiInputRow> wrappedData = new ArrayList<MultiInputRow>();
List<Object> values = getValues();
if (values != null) {
for (Object value : values) {
wrappedData.add(new MultiInputRow(value));
}
}
return new ListDataModel<MultiInputRow>(wrappedData);
}
private void addRowIfEmptyList() {
List<MultiInputRow> wrappedData = (List<MultiInputRow>) rows.getWrappedData();
if (wrappedData.size() == 0) {
wrappedData.add(new MultiInputRow(null));
}
}
#Override
public Object saveState(FacesContext context) {
if (context == null) {
throw new NullPointerException();
}
Object[] values = new Object[2];
values[0] = super.saveState(context);
values[1] = rows != null ? rows.getWrappedData() : null;
return (values);
}
#Override
public void restoreState(FacesContext context, Object state) {
if (context == null) {
throw new NullPointerException();
}
if (state == null) {
return;
}
Object[] values = (Object[]) state;
super.restoreState(context, values[0]);
rows = values[1] != null ? new ListDataModel<MultiInputRow>((List<MultiInputRow>) values[1]) : null;
}
/**
* Represents an editable row that holds a value that can be edited.
*/
public class MultiInputRow {
private Object value;
MultiInputRow(Object value) {
this.value = value;
}
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
}
}

Resources