Get composite component inputs' converted values - jsf

I have a composite component made of two p:calendars:
<cc:interface componentType="testComponent">
<cc:attribute name="value" type="org.primefaces.test.DateRange" />
</cc:interface>
<cc:implementation>
<c:set value="dd-MM-yyyy" var="pattern" />
<p:panelGrid columns="2">
<p:outputLabel for="from" value="From" />
<p:calendar id="from" binding="#{cc.from}" pattern="#{pattern}" />
<p:outputLabel for="to" value="To" />
<p:calendar id="to" binding="#{cc.to}" pattern="#{pattern}" />
</p:panelGrid>
</cc:implementation>
Class DateRange:
public class DateRange {
private Date from;
private Date to;
public DateRange() {
}
public DateRange(Date from, Date to) {
this.from = from;
this.to = to;
}
public Date getFrom() {
return from;
}
public void setFrom(Date from) {
this.from = from;
}
public Date getTo() {
return to;
}
public void setTo(Date to) {
this.to = to;
}
#Override
public String toString() {
return from + " --> " + to;
}
}
Backing class:
#FacesComponent("testComponent")
public class TestComponent extends UIInput implements NamingContainer {
private UIInput from;
private UIInput to;
private static final String DELIMITER = "-->";
#Override
public String getFamily() {
return UINamingContainer.COMPONENT_FAMILY;
}
#Override
public void encodeBegin(FacesContext context) throws IOException {
DateRange range = (DateRange) getValue();
System.out.println(range);
if (range != null) {
from.setValue(range.getFrom());
to.setValue(range.getTo());
}
super.encodeBegin(context);
}
#Override
public Object getSubmittedValue() {
if (from == null || to == null) return null;
String submittedFrom = (String) from.getSubmittedValue();
String submittedTo = (String) to.getSubmittedValue();
if (submittedFrom == null) {
submittedFrom = "";
}
if (submittedTo == null) {
submittedTo = "";
}
return submittedFrom + DELIMITER + submittedTo;
}
#Override
protected Object getConvertedValue(FacesContext context, Object newSubmittedValue) throws ConverterException {
String submitted = (String) newSubmittedValue;
if (submitted == null || submitted.isEmpty()) {
return null;
}
String[] split = submitted.split(DELIMITER);
String fromStr = split.length > 0 ? split[0] : "";
String toStr = split.length > 1 ? split[1] : "";
// TODO: Do I need to perform a manual conversion of the inputs???
return new DateRange((Date) fromObj, (Date) toObj);
}
getConvertedValue() should return an instance of DateRange. To get dates from calendars, I would need to call getConvertedValue() on each but this method is protected and I cannot access it.
Do I need to manually get the pattern from the p:calendar and convert the value myself? If yes, how do I even know how the conversion is really done in Calendaritself? I wonder how is this approached in more complex components where conversion on subcomponents is not that trivial.

Related

Primefaces Datatable converter with filtering

I have the datatable and selectonemenu inside it.
I want to show images in select one menu items.So a have Rating class and converter ratingConverter for Rating class.
I have the next problem here: filtering by this column works just once! When I try to change item, method load(..) from LazyComicsDataModel calls. But after
filtering one time nothing worked! Method load not calls anymore. There are't any exceptions in server.log. When I don't use converter and class Rating (use just String class) it works right any number of times.
I would like to get any ideas why it might be happened.
Code:
Part of xhtml page:
<p:ajax
event="rowSelect"
update=":form:comicsDetail"
oncomplete="PF('comicsDialog').show()"/>
<p:column width="20%"
headerText="#{comicsCatalogueView.columnComicsRating}"
sortBy="#{comics.rating}"
filterBy="#{comics.rating}"
filterMatchMode="exact" >
<f:facet name="filter" >
<p:selectOneMenu
value="#{comicsCatalogueView.rating}"
var="r"
onchange="PF('comicsTable').filter()"
converter="ratingConverter"
>
<f:selectItem
value="#{null}"
itemValue="#{null}" />
<f:selectItems
value="#{comicsFinderManagedBean.ratings}"
var="rating"
itemLabel="#{rating.value} - #{rating.value+1}"
itemValue="#{rating}"
/>
<p:column>
<p:graphicImage value="#{r.image}"/>
</p:column>
</p:selectOneMenu>
</f:facet>
<h:panelGroup>
<pf:rating disabled="true"
totalStars="5"
step="0.1"
value="#{comics.rating}"
>
</pf:rating>
</h:panelGroup>
</p:column>
Rating class:
public class Rating {
public Rating(Integer value, String image) {
this.value = value;
this.image = image;
}
private Integer value;
private String image;
public Integer getValue() {
return value;
}
public void setValue(Integer value) {
this.value = value;
}
public String getImage() {
return image;
}
public void setImage(String image) {
this.image = image;
}
#Override
public int hashCode() {
int hash = 5;
hash = 71 * hash + (this.value != null ? this.value.hashCode() : 0);
hash = 71 * hash + (this.image != null ? this.image.hashCode() : 0);
return hash;
}
#Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Rating other = (Rating) obj;
if (this.value != other.value && (this.value == null || !this.value.equals(other.value))) {
return false;
}
if ((this.image == null) ? (other.image != null) : !this.image.equals(other.image)) {
return false;
}
return true;
}
#Override
public String toString() {
return "Rating{" + "value=" + value + ", image=" + image + '}';
}
Rating converter:
#FacesConverter("ratingConverter")
public class RatingConverter implements Converter {
#Override
public Object getAsObject(FacesContext fc, UIComponent uic, String value) {
if(value != null && value.trim().length() > 0) {
ComicsFinderManagedBean ratingFinderManagedBean = (ComicsFinderManagedBean)
fc.getExternalContext().getApplicationMap().get("comicsFinderManagedBean");
return ratingFinderManagedBean.getRating(Integer.parseInt(value));
}
else {
return null;
}
}
#Override
public String getAsString(FacesContext fc, UIComponent uic, Object object) {
if(object != null) {
return String.valueOf(((Rating) object).getValue());
}
else {
return null;
}
}
}

PrimeFaces auto complete return a null object

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;
}
}

Primefaces p:orderList java backing list does not update

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

JSF Converter Validation Error: Value not valid

I have a rich:pickList, when submit values it still displays error: "Validation Error: Value is not valid". I also set breakpoint to debug (getAsobject), but after system invocation I had nothing.
BalusC said that I would implement the equals() method in my entities, or I have the entities in the web service, and I fill the right side of the picklist with data from this web service.
xHTML file
<h:form>
<rich:panel>
<h:panelGrid columns="2" styleClass="criteresSaisie"
rowClasses="critereLigne" columnClasses="titreColonne,">
<h:outputLabel for="libelleComplement" value=" "
size="20" />
<rich:pickList id="libelleComplement" sourceCaption="Compléments"
targetCaption="Compléments sélectionnés"
value="#{listeCmpltDispoModel.listeCmpltSelect}" size="15"
addText=">" addAllText=">>" removeText="<"
removeAllText="<<" listWidth="270px" listHeight="110px"
orderable="true">
<f:selectItems value="#{listeCmpltDispoModel.listeCmpltDispo}"
var="liste" itemLabel="#{liste.libelleComplement}"
itemValue="#{liste}" />
<f:converter converterId="cmpltsTitresConcerter" />
</rich:pickList>
</h:panelGrid>
<h:panelGroup>
<div align="right">
<h:panelGrid columns="8">
<h:commandButton value="Valider"
action="#{saisieCmpltsTitreCtrl.valider}" />
</h:panelGrid>
</div>
</h:panelGroup>
</rich:panel>
</h:form>
Converter
#FacesConverter(value="cmpltsTitresConcerter")
public class CmpltsTitresConcerter implements Converter
{
public Object getAsObject(FacesContext context, UIComponent component, String value)
{
ComplementsDispoSortieDTO cmpltSelect= new ComplementsDispoSortieDTO();
if(value != null)
{
cmpltSelect.setCdComplement(Long.parseLong(value));
//cmpltSelect.setLibelleComplement("aaa");
}
return cmpltSelect;
}
public String getAsString(FacesContext arg0, UIComponent arg1, Object obj)
{
String result = null;
if(obj != null)
{
result = String.valueOf(((ComplementsDispoSortieDTO) obj).getCdComplement());
}
return result;
}
}
Model
#ManagedBean(name = "listeCmpltDispoModel")
#SessionScoped
public class ListeCmpltDispoModel implements Serializable {
private static final long serialVersionUID = 1L;
private Long cdComplement;
private String libelleComplement;
private int nbCompl;
private List<ComplementsDispoSortieDTO> listeCmpltDispo ;
private List<ComplementsDispoSortieDTO> listeCmpltSelect ;
public ListeCmpltDispoModel() {
}
public Long getCodeComplement() {
return cdComplement;
}
public void setCodeComplement(Long cdComplement) {
this.cdComplement = cdComplement;
}
public String getLibelleComplement1() {
return libelleComplement;
}
public void setLibelleComplement1(String libelleCoplement) {
this.libelleComplement = libelleCoplement;
}
public Long getCdComplement() {
return cdComplement;
}
public void setCdComplement(Long cdComplement) {
this.cdComplement = cdComplement;
}
public String getLibelleComplement() {
return libelleComplement;
}
public void setLibelleComplement(String libelleComplement) {
this.libelleComplement = libelleComplement;
}
public List<ComplementsDispoSortieDTO> getListeCmpltDispo() {
return listeCmpltDispo;
}
public void setListeCmpltDispo(List<ComplementsDispoSortieDTO> listeCmpltDispo) {
this.listeCmpltDispo = listeCmpltDispo;
}
public int getNbCompl() {
return nbCompl;
}
public void setNbCompl(int nbCompl) {
this.nbCompl = nbCompl;
}
public List<ComplementsDispoSortieDTO> getListeCmpltSelect() {
return listeCmpltSelect;
}
public void setListeCmpltSelect(List<ComplementsDispoSortieDTO> listeCmpltSelect) {
this.listeCmpltSelect = listeCmpltSelect;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((cdComplement == null) ? 0 : cdComplement.hashCode());
result = prime
* result
+ ((libelleComplement == null) ? 0 : libelleComplement
.hashCode());
result = prime * result
+ ((listeCmpltDispo == null) ? 0 : listeCmpltDispo.hashCode());
result = prime
* result
+ ((listeCmpltSelect == null) ? 0 : listeCmpltSelect.hashCode());
result = prime * result + nbCompl;
return result;
}
#Override
public boolean equals(Object obj){
if(!(obj instanceof ComplementsDispoSortieDTO)){
return false;
}
return ((ComplementsDispoSortieDTO)obj).getCdComplement()==this.cdComplement;
}
}
The equals() method of your entity is not right. It's not only in the wrong class (the backing bean class instead of the model class), but it's also using == on an Object (the == on an object only tests the reference, not the internal value; as a starter, you should have experienced this mistake on String values already).
The == on a Long would only return true is the Long instance was created by Long#valueOf() or Long#parseLong() and the value is between -128 and 127. Anything else, including new Long(value) and those with values beyond the given "flyweight" range, return false.
As with every other Java object (such as your current one), you need equals() instead. Put it in the right class and implement it as follows:
public class ComplementsDispoSortieDTO {
private Long cdComplement;
// ...
#Override
public boolean equals(Object obj){
if (!(obj instanceof ComplementsDispoSortieDTO)){
return false;
}
return (cdComplement != null)
? cdComplement.equals(((ComplementsDispoSortieDTO) obj).cdComplement)
: (obj == this);
}
}
Note that I also added the missing reflexive obj == this. See also the javadoc for list of requirements.
See also:
Validation Error: Value is not valid
Right way to implement equals contract
I know which could be the solution.
You should store the list and selected element in properties and maintain them in the scope as long as the component is used.

selectOneMenu do not render/display value

Made a post earlier about this topic (Display values in drill-down SelectOneMenus). The problem that have is not connected to the Object converters as I first thought.
I have two selectOneMenu depending on each other, Sector -> Category. When I persist the background business object that holds a Sector and Category, everything works as expected. Even after the current user session terminates and the user log on again and edit the previous saved business object.
The problem occur when and the entity manager flushes the objects and user wants to display the Sector, Category again from the database. The Sector selectOneMenu displays its value as it should, but the Category selectOneMenu value is not displayed. Although the backing bean has the correct value from the database.
What makes the selectOneMenuto not display certain persisted values/objects when they are loaded from database instead from in-memory?
Edit.xhtml
<h:outputLabel value="Sector:" />
<h:selectOneMenu id="sectorSelector" value="#{activityController.selected.category.sector}" title="#{bundle.CreateSectorLabel_sectorName}" valueChangeListener="#{activityController.changeSectorMenu}" immediate="true">
<a4j:ajax event="change" execute="#this categoryMenu" render="categoryMenu"/>
<f:selectItems value="#{sectorController.itemsAvailableSelectOne}"/>
</h:selectOneMenu>
<h:outputLabel value="Category:" />
<h:selectOneMenu id="categoryMenu" value="#{activityController.selected.category}" title="#{bundle.CreateSectorLabel_sectorName}"
binding="#{activityController.categoryMenu}"
required="true" requiredMessage="#{bundle.CreateCategoryRequiredMessage_sector}">
<f:selectItems value="#{activityController.categorySelection}"/>
</h:selectOneMenu>
Controller bean for Category
#ManagedBean(name = "categoryController")
#SessionScoped
public class CategoryController implements Serializable{
....
#FacesConverter(forClass = Category.class)
public static class CategoryControllerConverter implements Converter {
#Override
public Object getAsObject(FacesContext facesContext, UIComponent component, String value) {
if (value == null || value.length() == 0) {
return null;
}
CategoryController controller = (CategoryController) facesContext.getApplication().getELResolver().
getValue(facesContext.getELContext(), null, "categoryController");
return controller.ejbFacade.find(getKey(value));
}
java.lang.Integer getKey(String value) {
java.lang.Integer key;
key = Integer.valueOf(value);
return key;
}
String getStringKey(java.lang.Integer value) {
StringBuffer sb = new StringBuffer();
sb.append(value);
return sb.toString();
}
#Override
public String getAsString(FacesContext facesContext, UIComponent component, Object object) {
if (object == null) {
return null;
}
if (object instanceof Category) {
Category o = (Category) object;
return getStringKey(o.getIdCategory());
}
else {
throw new IllegalArgumentException("object " + object + " is of type " + object.getClass().getName() + "; expected type: " + CategoryController.class.getName());
}
}
}
Part of POJO object
...
public class Category implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "idCategory")
private Integer idCategory;
...
#Override
public boolean equals(Object object) {
if (!(object instanceof Category)) {
return false;
}
Category other = (Category) object;
if ((this.idCategory == null && other.idCategory != null) || (this.idCategory != null && !this.idCategory.equals(other.idCategory))) {
return false;
}
return true;
}
Functon for creating the SelectItem[] array
public static SelectItem[] getSelectItems(List<?> entities, boolean selectOne) {
int size = entities.size();
SelectItem[] items;
int i = 0;
if (selectOne) {
items = new SelectItem[size + 1];
items[0] = new SelectItem("", ResourceBundle.getBundle("/resources/Bundle").getString("Select_item"));
i++;
} else {
items = new SelectItem[size];
}
for (Object x : entities) {
items[i++] = new SelectItem(x, x.toString());
}
return items;
}
The problem was that I didn't have the right collection of SelectItems when the user edited a business object. Plus that I added the <f:converter> to explicitly invoke the converter, where I did some changes in the getAsString() (the else part).
Thank you for your time, and hope the post can be useful to someone else.
SelectMenuOne
public String prepareEditOrganisationActivity() {
current = (Activity) organisationActivityItems.getRowData();
Sector sector = current.getCategory().getSector();
CategoryController categoryBean = JsfUtil.findBean("categoryController", CategoryController.class);
categorySelection = categoryBean.categoryItemsBySector(sector);
return "EditActivity";
}
CategoryControllerConverter
...
#Override
public String getAsString(FacesContext facesContext, UIComponent component, Object object) {
if (object == null) {
return null;
}
if (object instanceof Category) {
Category o = (Category) object;
return getStringKey(o.getIdCategory());
}
else {
Category tmp = new Category();
return getStringKey(tmp.getIdCategory());
}
}

Resources