I am currently implementing a orderable list using PrimeFaces' component, embedded inside a . I was able to get the list to appear properly with my items. However, when I saved the list and submitted it back to the server, the rearranged items did not get reflected in the backing bean for some reason. Since the Primefaces showcase was able to see the changes, what am I doing wrong?
XHTML Snippet:
<h:form id="confirmDialogForm">
<p:confirmDialog id="arrangeProjDialog" widgetVar="arrangeDlg" width="600"
header="Meeting Order"
appendToBody="true" message="Drag and drop to rearrange meeting order">
<p:orderList id="arrangeProjDialogList"
value="#{adminMeetingListBean.orderProjList}"
converter="#{adminMeetingListBean.rowConverter}"
var="po"
controlsLocation="left"
styleClass="wideList"
itemLabel="#{po.projectTitle}"
itemValue="#{po}"
>
<f:facet name="caption">Proposals</f:facet>
</p:orderList>
<p:commandButton value="Save" ajax="true" process="arrangeProjDialogList #this"
actionListener="#{adminMeetingListBean.updateProposalMeetingOrder}" onclick="arrangeDlg.hide();">
</p:commandButton>
<p:button value="Cancel" onclick="arrangeDlg.hide(); return false;" />
</p:confirmDialog>
</h:form>
Backing Bean:
public void updateProposalMeetingOrder() {
if (selectedMeeting != null) {
orderProjTitles.get(0);
meetingService.updateMeetingProjSequence(orderProjList, selectedMeeting.getMeetingId());
}
}
The List is a list of POJO "ProposalOrderRow" objects. This has the definition:
public class ProposalOrderRow implements Serializable {
private static final long serialVersionUID = -5012155654584965160L;
private int dispSeq;
private int appId;
private int assignmentId;
private String refNo;
private String projectTitle;
public int getDispSeq() {
return dispSeq;
}
public void setDispSeq(int dispSeq) {
this.dispSeq = dispSeq;
}
public int getAppId() {
return appId;
}
public void setAppId(int appId) {
this.appId = appId;
}
public String getRefNo() {
return refNo;
}
public void setRefNo(String refNo) {
this.refNo = refNo;
}
public String getProjectTitle() {
return projectTitle;
}
public void setProjectTitle(String projectTitle) {
this.projectTitle = projectTitle;
}
public int getAssignmentId() {
return assignmentId;
}
public void setAssignmentId(int assignmentId) {
this.assignmentId = assignmentId;
}
}
Converter:
#FacesConverter("proposalOrderRowConverter")
public class ProposalOrderRowConverter implements Converter {
private List<ProposalOrderRow> orderRows;
#Override
public Object getAsObject(FacesContext context, UIComponent component, String newValue) {
if (newValue.isEmpty()) {
return null;
}
for (ProposalOrderRow item : orderRows) {
String refNo = item.getRefNo();
if (refNo.equals(newValue)) {
return item;
}
}
return null;
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
if (value == null) {
return "";
}
ProposalOrderRow row = (ProposalOrderRow) value;
String output = row.getRefNo();
return output;
}
public List<ProposalOrderRow> getOrderRows() {
return orderRows;
}
public void setOrderRows(List<ProposalOrderRow> orderRows) {
this.orderRows = orderRows;
}
}
This problem is caused by appendToBody="true" in the confirm dialog. Setting it to false solved the problem.
See link here: link
Related
How do I define a jsf composite component properly such that its value gets bean-validated correctly in the case it contains a collection?
We have an entity that references a collection of details. Both are annotated with bean-validation-constraints. Please note the annotations at the details-property.
public class Entity implements Serializable {
#NotEmpty
private String name;
#NotEmpty
#UniqueCategory(message="category must be unique")
private List<#Valid Detail> details;
/* getters/setters */
}
public class Detail implements Serializable {
#Pattern(regexp="^[A-Z]+$")
private String text;
#NotNull
private Category category;
/* getters/setters */
}
public class Category implements Serializable {
private final int id;
private final String description;
Category(int id, String description) {
this.id = id;
this.description = description;
}
/* getters/setters */
}
public class MyConstraints {
#Target({ ElementType.TYPE, ElementType.FIELD })
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy = UniqueCategoryValidator.class)
#Documented
public static #interface UniqueCategory {
String message();
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public static class UniqueCategoryValidator implements ConstraintValidator<UniqueCategory, Collection<Detail>> {
#Override
public boolean isValid(Collection<Detail> collection, ConstraintValidatorContext context) {
if ( collection==null || collection.isEmpty() ) {
return true;
}
Set<Category> set = new HashSet<>();
collection.forEach( d-> set.add( d.getCategory() ));
return set.size() == collection.size();
}
public void initialize(UniqueCategory constraintAnnotation) {
// intentionally empty
}
}
private MyConstraints() {
// only static stuff
}
}
The entity can be edited in a jsf-form, where all the tasks concerning the details are encapsulated in a composite component, eg
<h:form id="entityForm">
<h:panelGrid columns="3">
<p:outputLabel for="#next" value="name"/>
<p:inputText id="name" value="#{entityUiController.entity.name}"/>
<p:message for="name"/>
<p:outputLabel for="#next" value="details"/>
<my:detailsComponent id="details" details="#{entityUiController.entity.details}"
addAction="#{entityUiController.addAction}"/>
<p:message for="details"/>
<f:facet name="footer">
<p:commandButton id="saveBtn" value="save"
action="#{entityUiController.saveAction}"
update="#form"/>
</f:facet>
</h:panelGrid>
</h:form>
where my:detailsComponent is defined as
<ui:component xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:cc="http://java.sun.com/jsf/composite"
xmlns:p="http://primefaces.org/ui"
>
<cc:interface>
<cc:attribute name="details" required="true" type="java.lang.Iterable"/>
<cc:attribute name="addAction" required="true" method-signature="void action()"/>
</cc:interface>
<cc:implementation>
<p:outputPanel id="detailsPanel">
<ui:repeat id="detailsContainer" var="detail" value="#{cc.attrs.details}">
<p:inputText id="text" value="#{detail.text}" />
<p:message for="text"/>
<p:selectOneMenu id="category" value="#{detail.category}"
converter="#{entityUiController.categoriesConverter}"
placeholder="please select" >
<f:selectItem noSelectionOption="true" />
<f:selectItems value="#{entityUiController.categoryItems}"/>
</p:selectOneMenu>
<p:message for="category"/>
</ui:repeat>
</p:outputPanel>
<p:commandButton id="addDetailBtn" value="add" action="#{cc.attrs.addAction}"
update="detailsPanel" partialSubmit="true" process="#this detailsPanel"/>
</cc:implementation>
</ui:component>
and the EntityUiController is
#Named
#ViewScoped
public class EntityUiController implements Serializable {
private static final Logger LOG = Logger.getLogger( EntityUiController.class.getName() );
#Inject
private CategoriesBoundary categoriesBoundary;
#Valid
private Entity entity;
#PostConstruct
public void init() {
this.entity = new Entity();
}
public Entity getEntity() {
return entity;
}
public void saveAction() {
LOG.log(Level.INFO, "saved entity: {0}", this.entity );
}
public void addAction() {
this.entity.getDetails().add( new Detail() );
}
public List<SelectItem> getCategoryItems() {
return categoriesBoundary.getCategories().stream()
.map( cat -> new SelectItem( cat, cat.getDescription() ) )
.collect( Collectors.toList() );
}
public Converter<Category> getCategoriesConverter() {
return new Converter<Category>() {
#Override
public String getAsString(FacesContext context, UIComponent component, Category value) {
return value==null ? null : Integer.toString( value.getId() );
}
#Override
public Category getAsObject(FacesContext context, UIComponent component, String value) {
if ( value==null || value.isEmpty() ) {
return null;
}
try {
return categoriesBoundary.findById( Integer.valueOf(value).intValue() );
} catch (NumberFormatException e) {
throw new ConverterException(e);
}
}
};
}
}
When we now press the save-button in the above h:form, the name-inputText is validated correctly but the #NotEmpty- and #UniqueCategory-constraint on the details-property are ignored.
What am I missing?
We are on java-ee-7, payara 4.
After diving into this a bit we endet up with a solution using a backing component ValidateListComponent. It was inspired by UIValidateWholeBean and WholeBeanValidator.
That component extends from UIInput and overrides the validation-Methods to operate on a clone of the above details-collection which gets populated with the already validated values of the children-UIInput. Seems to work for now.
<ui:component ...>
<cc:interface componentType="validatedListComponent">
<cc:attribute name="addAction" required="true" method-signature="void action()"/>
</cc:interface>
<cc:implementation>
... see above ...
</cc:implementation>
</ui:component>
with the backing component defined as
#FacesComponent(value = "validatedListComponent")
#SuppressWarnings("unchecked")
public class ValidatedListComponent extends UIInput implements NamingContainer {
#Override
public String getFamily() {
return "javax.faces.NamingContainer";
}
/**
* Override {#link UIInput#processValidators(FacesContext)} to switch the order of
* validation. First validate this components children, then validate this itself.
*/
#Override
public void processValidators(FacesContext context) {
// Skip processing if our rendered flag is false
if (!isRendered()) {
return;
}
pushComponentToEL(context, this);
for (Iterator<UIComponent> i = getFacetsAndChildren(); i.hasNext(); ) {
i.next().processValidators(context);
}
if (!isImmediate()) {
Application application = context.getApplication();
application.publishEvent(context, PreValidateEvent.class, this);
executeValidate(context);
application.publishEvent(context, PostValidateEvent.class, this);
}
popComponentFromEL(context);
}
/**
* Override {#link UIInput#validate(FacesContext)} to validate a cloned collection
* instead of the submitted value.
*
* Inspired by {#link UIValidateWholeBean} and {#link WholeBeanValidator}.
*/
#Override
public void validate(FacesContext context) {
AreDetailsValidCallback callback = new AreDetailsValidCallback();
visitTree( VisitContext.createVisitContext(context)
, callback
);
if ( callback.isDetailsValid() ) {
Collection<?> clonedValue = cloneCollectionAndSetDetailValues( context );
validateValue(context, clonedValue);
}
}
/**
* private method copied from {#link UIInput#executeValidate(FacesContext)}.
* #param context
*/
private void executeValidate(FacesContext context) {
try {
validate(context);
} catch (RuntimeException e) {
context.renderResponse();
throw e;
}
if (!isValid()) {
context.validationFailed();
context.renderResponse();
}
}
private Collection<Object> cloneCollectionAndSetDetailValues(FacesContext context) {
ValueExpression collectionVE = getValueExpression("value");
Collection<?> baseCollection = (Collection<?>) collectionVE.getValue(context.getELContext());
if ( baseCollection==null ) {
return null;
}
// Visit all the components children to find their already validated values.
FindDetailValuesCallback callback = new FindDetailValuesCallback(context);
this.visitTree( VisitContext.createVisitContext(context)
, callback
);
// Clone this components value and put in all cloned details with validated values set.
try {
Collection<Object> clonedCollection = baseCollection.getClass().newInstance();
for( Entry<Object,Map<String,Object>> entry : callback.getDetailSubmittedData().entrySet() ) {
Object clonedDetail = cloneDetailAndSetValues( entry.getKey(), entry.getValue() );
clonedCollection.add( clonedDetail );
}
return clonedCollection;
} catch ( Exception e ) {
throw new ConverterException(e);
}
}
private <T> T cloneDetailAndSetValues(T detail, Map<String, Object> propertyMap) throws Exception {
T clonedDetail = clone(detail);
// check the properties we have in the detail
Map<String, PropertyDescriptor> availableProperties = new HashMap<>();
for (PropertyDescriptor propertyDescriptor : getBeanInfo(detail.getClass()).getPropertyDescriptors()) {
availableProperties.put(propertyDescriptor.getName(), propertyDescriptor);
}
// put their value (or local value) into our clone
for (Map.Entry<String, Object> propertyToSet : propertyMap.entrySet()) {
availableProperties.get(propertyToSet.getKey()).getWriteMethod().invoke(clonedDetail,
propertyToSet.getValue());
}
return clonedDetail;
}
private static <T> T clone(T object) throws Exception {
// clone an object using serialization.
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(byteArrayOutputStream);
out.writeObject(object);
byte[] bytes = byteArrayOutputStream.toByteArray();
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
ObjectInputStream in = new ObjectInputStream(byteArrayInputStream);
return (T) in.readObject();
}
private class FindDetailValuesCallback implements VisitCallback {
private final FacesContext context;
private final Map<Object, Map<String, Object>> detailSubmittedData = new HashMap<>();
public FindDetailValuesCallback(final FacesContext context) {
this.context = context;
}
final Map<Object, Map<String, Object>> getDetailSubmittedData() {
return detailSubmittedData;
}
#Override
public VisitResult visit(VisitContext visitContext, UIComponent component) {
if ( isVisitorTarget(component) ) {
ValueExpression ve = component.getValueExpression("value");
Object value = ((EditableValueHolder)component).getValue();
if (ve != null) {
ValueReference vr = ve.getValueReference(context.getELContext());
String prop = (String)vr.getProperty();
Object base = vr.getBase();
Map<String, Object> propertyMap
= Optional.ofNullable( detailSubmittedData.get(base) )
.orElseGet( HashMap::new );
propertyMap.put(prop, value );
detailSubmittedData.putIfAbsent( base, propertyMap);
}
}
return ACCEPT;
}
}
private class AreDetailsValidCallback implements VisitCallback {
private boolean detailsValid;
public AreDetailsValidCallback() {
this.detailsValid = true;
}
public boolean isDetailsValid() {
return detailsValid;
}
#Override
public VisitResult visit(VisitContext visitContext, UIComponent component) {
if ( isVisitorTarget(component) ) {
if ( !((EditableValueHolder)component).isValid() ) {
this.detailsValid = false;
}
}
return ACCEPT;
}
}
private boolean isVisitorTarget( UIComponent component ) {
return component instanceof EditableValueHolder && component.isRendered()
&& component!=ValidatedListComponent.this;
}
}
UPDATE: sometimes it is a problem to obtain the ValueReference in FindDetailValuesCallback#visit. The answer given by Michele here solved that problem.
I am studying a PrimeFaces AutoComplete demo. I shortenied it from the full showcase demo. http://www.primefaces.org/showcase/ui/input/autoComplete.xhtml
AutoCompleteBean.java
#ManagedBean
public class AutoCompleteBean {
private Query query;
private List<Query> queries = new ArrayList<Query>();
#PostConstruct
public void init() {
queries.add(new Query(0, "Afterdark", "afterdark"));
queries.add(new Query(1, "Afternoon", "afternoon"));
queries.add(new Query(2, "Afterwork", "afterwork"));
queries.add(new Query(3, "Aristo", "aristo"));
}
public List<Query> completeQuery(String query) {
List<Query> filteredQueries = new ArrayList<Query>();
for (int i = 0; i < queries.size(); i++) {
Query skin = queries.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;
}
}
Query.java
public class Query {
private int id;
private String displayName;
private String name;
public Query() {}
public Query(int id, String displayName, String name) {
this.id = id;
this.displayName = displayName;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getDisplayName() {
return displayName;
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public String toString() {
return name;
}
}
I omitted a Convert class, which I think is not that relevant.
search.xhtml
<h:form>
<p:growl id="msgs" showDetail="true" />
<h:panelGrid columns="2" cellpadding="5">
<p:autoComplete id="queryPojo" 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()"/>
</h:panelGrid>
</h:form>
I have three questions for this:
1) completeMethod="#{autoCompleteView.completeQuery}": completeQuery method is called without passing a parameter, but it's defined as completeQuery(String query). How does this work?
2) value="#{autoCompleteView.query}". Query is an object defined in AutoCompleteBean. How can this Query object take user input string as its value? Usually InputText's value is good for taking user's input, which is a String value.
3) Can I still add an attribute "action=..." to the p:autoComplete componenet?
The converter class that you omitted here plays the real game.... Lets see your questions
As you see converter class overrides 2 methods
getAsString and getAsObject
1)the value
completeMethod="#{autoCompleteView.completeQuery}
gets refactred to
autoCompleteView.completeQuery(autoCompleteView.query);
as you can find to string method in Query class.
2).as converter is defined for autocomplete it calls getAsString method to render on screen. when selected getAsObject method is called to convert string value to object(Query).
3)you can use ajax select event
<p:ajax event="select" listener="#{autoCompleteView.someMethod}">
or call a remoteCommand by onSelect attribute in p:autoComplete
<p:autoComplete id="queryPojo" value="#{autoCompleteView.query}" onSelect="someRemoteCommand();"
completeMethod="#{autoCompleteView.completeQuery}" var="query"
itemLabel="#{query.displayName}" itemValue="#{query}"
converter="queryConverter" forceSelection="true" />
<p:remoteCommand name="someRemoteCommand" update="queryPojo" actionListener="#{autoCompleteView.execute}" />
I tried to make same list as in the below link:
http://www.primefaces.org/showcase/ui/input/listbox.xhtml
Unfortunately I get validation error:
Select Box: Validation Error: Value is not valid
I don't know where I've made mistake. I read a lot about this issue (in most cases it was solved by BalusC) however still can't find the issue.
If you can help I really appreciate.
StoreHouse.java
#Entity
public class StoreHouse implements Serializable {
#Id
#GeneratedValue
private Integer id;
#OneToOne
private Supply supply;
#Column(nullable = false)
private Integer amount;
//geters setters namedqueryies
Supply.java
#Entity
public class Supply implements Serializable {
#Id
#GeneratedValue
private Integer id;
#Column(unique = true, nullable = false, length = 32)
private String name;
#Column(length = 1024)
private String description;
#Column
private Double price;
#Enumerated(EnumType.STRING)
#NotNull
private SupplyType supplyType;
//geters setters namedqueryies
StoreHouseController.java
#ManagedBean
public class StoreHouseController implements Serializable {
#Inject
private StoreHouseBean storeHouseBean; // DAO for storeHouse
#ManagedProperty("#{supplyController}")
private SupplyController supplyController; //Manged bean for Supply
private StoreHouse storeHouse = new StoreHouse();
private List<Supply> allSupplies;
#PostConstruct
public void init() {
allSupplies = supplyController.findAll();
}
public void check() {
System.out.println("storeHousetheme" + storeHouse.toString()); // Function just to check if the supply was set
}
SupplyConverter.java
#FacesConverter("supplyConverter")
public class SupplyConverter implements Converter {
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
if (value != null && value.trim().length() > 0) {
try {
SupplyController supplyController = (SupplyController) context.getExternalContext().getApplicationMap().get("supplyController");
Supply supply = supplyController.findById(Integer.parseInt(value));
System.out.println("CONVERTER:" + supply.toString());
return supply;
} catch (NumberFormatException e) {
throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Conversion error", "ERROR."));
}
} else {
return null;
}
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object object) {
if (object != null) {
return String.valueOf(((Supply) object).getId());
} else {
return null;
}
}
}
view.xhtml
<h:form>
<p:messages autoUpdate="true" />
<h:panelGrid columns="2">
<h:outputLabel value="Nazwa" />
<p:selectOneListbox id="supplies" value="#{storeHouseController.storeHouse.supply}" converter="supplyConverter" var="s" filter="true" label="Select Box">
<f:selectItems value="#{storeHouseController.allSupplies}" var="supply" itemLabel="#{supply.name}" itemValue="#{supply}"/>
<p:column>
<h:outputText value="#{s.name}" />
</p:column>
</p:selectOneListbox>
<p:commandButton action="#{storeHouseController.check}" type="submit" value="submit" />
</h:panelGrid>
If you need any other file/class please just add comment.
Your SelectItem-value is your object, so the toString()-method is called and you get something like "Supply#44de6bae".
Then, your controller tries to parse it to int and fails. Try...
<f:selectItems value="#{...}" var="..." itemLabel="..." itemValue="#{supply.id}"/>
If the error occures while loading the page, this might help as well as at least some components don't like "none-basic-type-or-enum" values. I hope this helped, Greez.
After few more days of searching and googling I've found the solution. I'm a bit angry that this is not posted anywhere on primefaces because I've lost many hours to fix the issue.
It came out that I did not overwrite equals method of Supply class which is required.
#Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Supply other = (Supply) obj;
if (!Objects.equals(this.id, other.id)) {
return false;
}
if (!Objects.equals(this.name, other.name)) {
return false;
}
if (this.supplyType != other.supplyType) {
return false;
}
return true;
}
I believe that this topic will help someone in the future...
this is my xhtml page
<h:form id="order_search" prependId="flase">
<p:growl id="growl" showDetail="true" autoUpdate="true"
sticky="false" />
<h:panelGrid columns="5" style="margin-bottom:10px" cellpadding="5">
<p:outputLabel value="Customer Name : " for="ac_order" />
<p:autoComplete id="ac_order" value="#{orderSearchController.orderFromAutoC}"
completeMethod="#{orderSearchController.autoCompleteOrder}" var="order"
itemLabel="#{order.customerName}" itemValue="#{order}"
converter="#{orderConverter}" forceSelection="true" />
<p:commandButton id="selected" value="print" action="#{orderSearchController.printOrder}" />
</h:panelGrid>
</h:form>
and this is my backing bean
#Component
#ManagedBean
#ViewScoped
public class OrderSearchController implements Serializable{
private static final long serialVersionUID = 1L;
#ManagedProperty(value = "#{orderService}")
public OrderService orderService;
public List<Order> allOrders;
public List<Order> acFilterdOrders;
public Order orderFromAutoC;
#PostConstruct
public void Init() {
System.out.println("init gets called");
// allOrders = new ArrayList<>();
// orderFromAutoC = new Order();
allOrders = orderService.getAllOrders();
System.out.println("After sssssss ");
}
public List<Order> autoCompleteOrder(String query) {
acFilterdOrders = new ArrayList<Order>();
for (int i = 0; i < allOrders.size(); i++) {
if (allOrders.get(i).getCustomerName().toLowerCase().startsWith(query)) {
acFilterdOrders.add(allOrders.get(i));
}
}
return acFilterdOrders;
}
public String printOrder() {
System.out.println("Inside print");
System.out.println("Inside print : "+orderFromAutoC);
return null;
}
//Getters and Setters
}
and this is my converter code
#ManagedBean(name = "orderConverter")
#RequestScoped
public class OrderConverter implements Converter {
#ManagedProperty(value = "#{orderService}")
private OrderService orderService;
#Override
public Object getAsObject(FacesContext arg0, UIComponent arg1, String value) {
if (value != null && value.trim().length() > 0) {
return orderService.getOrderById(Integer.parseInt(value));
} else {
return null;
}
}
#Override
public String getAsString(FacesContext arg0, UIComponent arg1, Object arg2) {
// TODO Auto-generated method stub
return null;
}
public OrderService getOrderService() {
return orderService;
}
public void setOrderService(OrderService orderService) {
this.orderService = orderService;
}
}
the auto-complete component works fine but but when i tried to get the selected value from it in the backing bean it always return a null
The getAsString method is not implemented correctly as it is just returning the NULL value. The return value of getAsString method is passed as value in the getAsObject method based on which it get the value from the list. Here is an example for your reference . Value in the getAsObject method is the Id that is returned from getAsString method.
#Override
public Object getAsObject(FacesContext fc, UIComponent uic, String value) {
if(value != null && value.trim().length() > 0) {
MessageLogBean messageLogBean = (MessageLogBean) SessionUtility.getManagedBean("messageLogBean");
DeviceResponseDTO deviceResponseDTO = new DeviceResponseDTO();
deviceResponseDTO.setId(Integer.parseInt(value));
List<DeviceResponseDTO> deviceResponseDTOs = messageLogBean.getDeviceResponseDTOs();
int index = deviceResponseDTOs.indexOf(deviceResponseDTO);
if(null != deviceResponseDTOs && !deviceResponseDTOs.isEmpty()){
return deviceResponseDTOs.get(index);
}
return null;
}
else {
return null;
}
}
#Override
public String getAsString(FacesContext fc, UIComponent uic, Object object) {
if(object != null) {
return String.valueOf(((DeviceResponseDTO) object).getId());
}
else {
return null;
}
}
hey I am using the following code to create the number of text fields as the user wants
<h:form>
<p>Number Of News <h:inputText value="#{news.noOfFields}" /></p>
<ui:repeat value="#{news.values}" var="item">
<hr/>
News #{item.no}
<h:inputText value="#{item.news}" /><br/>
</ui:repeat>
<hr/>
<h:commandButton styleClass="btn btn-blue" action="#{news.submit}" value="Save" />
</h:form>
The managed bean news has a class News as
#ManagedBean
#SessionScoped
public class News
{
private String noOfFields;
private List<NewsVO> values;
public News()
{
this.values = new ArrayList<NewsVO>();
}
public String submit() {
for(NewsVO newsVO : this.values)
{
System.out.println(newsVO.getNews());
System.out.println(newsVO.getNo());
}
return null;
// save values in database
}
public String getNoOfFields() {
return noOfFields;
}
public List<NewsVO> getValues() {
return values;
}
public void setValues(List<NewsVO> values) {
this.values = values;
}
public void setNoOfFields(String noOfFields) {
this.values = new ArrayList<NewsVO>();
try {
for(int i=0;i<Integer.valueOf(noOfFields);i++)
{
NewsVO newsVO = new NewsVO();
newsVO.setNo(i+1);
this.values.add(newsVO);
}
this.noOfFields = noOfFields;
}
catch(NumberFormatException ex) {
/*values = new String[1];*/
noOfFields = "1";
}
}
}
The NewsVO is just a javaBean class as follows
public class NewsVO
{
public int no;
public String news;
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public String getNews() {
return news;
}
public void setNews(String news) {
this.news = news;
}
}
The problem is the values inside the input Text doesn't get reflected on pressing the save button. It gives me null, even though, I have written something inside all the textfields.
<h:inputText value="#{item.news}" />
Everytime you push the submit button, all setters in the bean are called (including setNoOfFields()). In this setter you are resetting your list, that's why you loose your values. Since you only need to modify your list if there is a size change, here is a simple way doing it :
#ManagedBean
#SessionScoped
public class News
{
private int noOfFields;
private List<NewsVO> values;
public News()
{
this.values = new ArrayList<NewsVO>();
}
public String submit()
{
for(NewsVO newsVO : this.values)
{
System.out.println(newsVO.getNews());
System.out.println(newsVO.getNo());
}
return null;
// save values in database
}
public int getNoOfFields()
{
return noOfFields;
}
public List<NewsVO> getValues()
{
return values;
}
public void setValues(List<NewsVO> values)
{
this.values = values;
}
public void setNoOfFields(int noOfFields)
{
if(noOfFields < this.noOfFields)
{
for(int i = this.noOfFields - 1;i >= noOfFields;i--)
{
getValues().remove(i);
}
}
else if(noOfFields > this.noOfFields)
{
for(int i = this.noOfFields;i < noOfFields;i++)
{
NewsVO newsVO = new NewsVO();
newsVO.setNo(i+1);
getValues().add(newsVO);
}
}
}
}
Note : I've also changed your noOfFields getter/setter for simple int, JSF will do the conversion for you.