Custom component inside <ui:repeat> doesn't find iterated item during encode - jsf

I'm trying to create a custom component for displaying an Entity with a certain form. So I've created my #FacesComponent and he's working but only when he is not inside a loop like <ui:repeat>. When I'm using the following code, my component is displaying null values for price and photo but not for name. Do you have an explaination ?
XHTML code :
<ui:define name="content">
<f:view>
<h:form>
<ui:repeat value="#{dataManagedBean.listNewestCocktails}" var="item" varStatus="status">
<h:outputText value="#{item.price}"/> <!--working very well-->
<t:cocktailVignette idPrefix="newCocktails" name="foo" price="#{item.price}" urlPhoto="#{item.photoURI}"/> <!-- not working the getPrice here -->
</ui:repeat>
<!--<t:cocktailVignette idPrefix="allCocktails" name="OSEF" price="20" urlPhoto="osefdelurl" ></t:cocktailVignette> -->
</h:form>
</f:view>
My component code :
package component;
import java.io.IOException;
import javax.faces.context.FacesContext;
import javax.faces.component.FacesComponent;
import javax.faces.component.UIComponentBase;
import javax.faces.context.ResponseWriter;
#FacesComponent(value = "CocktailVignette")
public class CocktailVignette extends UIComponentBase {
private String idPrefix;
private String name;
private String price;
private String urlPhoto;
public String getIdPrefix() {
return idPrefix;
}
public void setIdPrefix(String idPrefix) {
this.idPrefix = idPrefix;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPrice() {
return price;
}
public void setPrice(String price) {
this.price = price;
}
public String getUrlPhoto() {
return urlPhoto;
}
public void setUrlPhoto(String urlPhoto) {
this.urlPhoto = urlPhoto;
}
#Override
public String getFamily() {
return "CocktailVignette";
}
#Override
public void encodeBegin(FacesContext context) throws IOException {
ResponseWriter writer = context.getResponseWriter();
writer.write("<div id=\""+idPrefix+name+"\" class=\"cocktail-vignette\">");
writer.write("<h2>"+name+"</h2>");
writer.write("<h3>"+price+"</h3>");
writer.write("</div>");
}
}
Thanks a lot :) I'm trying but nothing is working ...

All of component's attributes which are sensitive to changes in state (e.g. the value being dependent on <ui:repeat var>, at least those which is not known during view build time but during view render time only), must delegate the storage of attribute value to the state helper as available by inherited getStateHelper() method.
Kickoff example:
public String getPrice() {
return (String) getStateHelper().eval("price");
}
public void setPrice(String price) {
getStateHelper().put("price", price);
}
Apply the same for all other attributes and get rid of the instance variable declarations. Important note is that the state helper key ("price" in above example) must be exactly the same as attribute name.
See also:
How to save state when extending UIComponentBase

Related

Passing parameters in JSF and PrimeFaces

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}" />

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

Using SelectManyCheckbox with list of objects

Im trying to create a JSF page that lets the user select X amount of ingredients, and then saves those selected ingredients to a list.
Ingredient is an object with two values, String IngredientName and int ingredientPrice.
What I want to do is to create 1 selectItem per Ingredient in an IngredientList (dynamically sized), and then save the selected items to another list of ingredients.
I've tried doing this multiple different ways but either I get classcast exceptions or the checkboxes don't appear at all.
My Bean:
#ManagedBean
#SessionScoped
public class ManagedIngredientsBean {
#EJB
IngredientBean iBean;
private List<Ingredient> ingredientList;
private List<Ingredient> checkedOptions;
private List<SelectItem> selectList;
public ManagedIngredientsBean() {
}
public String createNew(){
ingredientList = iBean.getAllIngredients();
selectList = new ArrayList<SelectItem>(ingredientList.size());
for(Ingredient i : ingredientList){
selectList.add(new SelectItem(i.getIngredientName()));
}
return "createnew.xhtml";
}
public List<SelectItem> getSelectList() {
return selectList;
}
public void setSelectList(List<SelectItem> selectList) {
this.selectList = selectList;
}
public List<Ingredient> getCheckedOptions() {
return checkedOptions;
}
public void setCheckedOptions(List<Ingredient> checkedOptions) {
this.checkedOptions = checkedOptions;
}
public List<Ingredient> getIngredientList() {
return ingredientList;
}
public void setIngredientList(List<Ingredient> ingredientList) {
this.ingredientList = ingredientList;
}
#FacesConverter(value="userConverter")
public static class UserConverter implements Converter {
public Object getAsObject(FacesContext facesContext,
UIComponent component, String value) {
return value;
}
public String getAsString(FacesContext facesContext,
UIComponent component, Object o) {
Ingredient i = (Ingredient) o;
return i.getIngredientName();
}
}
}
IngredientBean used to get the Ingredient items from the persistence database and returning them as a list:
#Stateless(name = "IngredientEJB")
public class IngredientBean {
EntityManagerFactory entFactory;
EntityManager em;
public IngredientBean() {
entFactory = Persistence.createEntityManagerFactory("NewPersistenceUnit");
em = entFactory.createEntityManager();
}
public List<Ingredient> getAllIngredients(){
TypedQuery<Ingredient> ingQuery = em.createQuery("SELECT i FROM Ingredient i", Ingredient.class);
List<Ingredient> iList = ingQuery.getResultList();
return iList;
}
}
My JSF Page:
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core">
<h:head>
<title>Create New Order</title>
</h:head>
<h:body>
<h:form>
<h:selectManyCheckbox value = "#{managedIngredientsBean.checkedOptions}">
<f:converter converterId="userConverter"/>
<f:selectItem value = "#{managedIngredientsBean.selectList}" var = "item" itemLabel = "#{item.getIngredientName()}" itemValue = "#{item}"/>
</h:selectManyCheckbox>
</h:form>
</h:body>
</html>
I'm probably missing something obvious or simply misunderstanding how to use the selectManyCheckbox element but I'm completely stuck on how to fix this. Appreciate any answers on how I should be implementing this. :)
Edit: Forgot to mention, the createNew() method in the managed bean is called in the previous JSF page and redirects to this one.
your converter is broken.
first, it have to be a bean so must not be a static class.
second, it "should" be symmetric:
x.equals(c.getAsObject(ctx, comp, c.getAsString(ctx, component, x))); "should" be true.
#FacesConverter(value="userConverter")
public class UserConverter implements Converter
{
public Object getAsObject(FacesContext facesContext, UIComponent component, String value)
{
return database.loadIngredientByUniqueValue(value);
}
public String getAsString(FacesContext facesContext,UIComponent component, Object o)
{
Ingredient i = (Ingredient) o;
return i.getSomeUniqueValue();
}
}

Getting selection from p:selectOneMenu in PrimeFaces

I want to select a value from a p:selectOneMenu component (a dropdownlist) in Primefaces. I get my data from a Java Bean. I have the following code:
XHTML:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:p="http://primefaces.org/ui">
<h:body>
<h:form>
<p:messages id="errorMessages" style="color:red;margin:8px;" />
<br></br>
<p:panelGrid columns="2" style="margin-bottom:10px" cellpadding="5">
<h:outputText value="Tasks: "/>
<p:selectOneMenu value="#{devTestController.selectedTask}">
<f:selectItems value="#{devTestController.tasks}" var="task" itemLabel="#{task.label}" itemValue="#{task.value}"/>
<f:converter converterId="infoRowBeanConverter" />
</p:selectOneMenu>
</p:panelGrid>
<br/>
<p:commandButton value="Execute Task" update = "errorMessages" action="#{devTestController.executeTask()}"/>
</h:form>
</h:body>
</html>
Java Bean DevTestController.java:
package mypackage;
import java.util.LinkedList;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
import javax.faces.context.FacesContext;
import javax.servlet.http.HttpServletRequest;
#ManagedBean
#RequestScoped
public class DevTestController
{
private InfoRowBean selectedTask;
private static List<InfoRowBean> tasks;
#PostConstruct
public void initList()
{
if (tasks == null)
{
tasks = new LinkedList<>();
tasks.add(new InfoRowBean("Task 1", "Task 1"));
tasks.add(new InfoRowBean("Task 2", "Task 2"));
}
}
public InfoRowBean getSelectedTask()
{
return selectedTask;
}
public void setSelectedTask(InfoRowBean selectedTask)
{
this.selectedTask = selectedTask;
}
public List<InfoRowBean> getTasks()
{
return tasks;
}
public void executeTask()
{
System.out.println("Executing task " + selectedTask.label);
}
}
InfoRowBean.java:
package mypackage;
import java.util.List;
public class InfoRowBean
{
String label = null;
String value = null;
public InfoRowBean(String label, String value)
{
setLabel(label);
setValue(value);
}
public String getLabel()
{
return label;
}
public void setLabel(String label)
{
this.label = label;
}
public String getValue()
{
return value;
}
public void setValue(String value)
{
this.value = value;
}
// This must return true for another InfoRowBean object with same label/id.
public boolean equals(Object other)
{
return other instanceof InfoRowBean && (label != null) ? label.equals(((InfoRowBean) other).label) : (other == this);
}
// This must return the same hashcode for every InfoRowBean object with the same label.
public int hashCode()
{
return label != null ? this.getClass().hashCode() + label.hashCode() : super.hashCode();
}
// Override Object#toString() so that it returns a human readable String representation.
// It is not required by the Converter or so, it just pleases the reading in the logs.
public String toString()
{
return "InfoRowBean[" + label + "," + value + "]";
}
}
Converter InfoRowBeanConverter.java:
package mypackage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.FacesConverter;
#FacesConverter("infoRowBeanConverter")
public class InfoRowBeanConverter implements Converter
{
public Object getAsObject(FacesContext context, UIComponent component, String value)
{
return value;
}
public String getAsString(FacesContext context, UIComponent component, Object value)
{
return value.toString();
}
}
If I press the button nothing happens (no error also). If I remove the parameter "value" from tag (namely leave ), the button works fine, but of course I don't get the selected item. What is the problem here?
The problem is that your converter isn't converting the submitted string value to a concrete InfoRowBean instance in getAsObject() method, but instead returning the raw submitted String value as you have generated in getAsString() method. This doesn't match the type of selectedTask, which is InfoRowBean.
You need to fix your converter accordingly in such way that getAsString() returns the unique string representation of the complex object, usually in flavor of the database identifier (so that it can be used further in text based formats such as HTML output and HTTP request parameters), and that getAsObject() converts exactly that unique string representation back to the concrete complex object instance, usually via a DB call using the unique identifier as key.
An alternative is to use omnifaces.SelectItemsConverter of the JSF utility library OmniFaces, so that you never need to create custom converters for components using <f:selectItem(s)> with complex objects as values.
Another alternative is to change selectedTask to be String instead of InfoRowBean (and get rid of the whole converter as it is completely useless in this construct).
See also:
How to populate options of h:selectOneMenu from database?

Using a Enum with a ActionParam

I am using the following piece of code in my JSF 2.0 with RichFaces 4.0. I have a managed bean that has an enum. Now i want to assign the value of the enum via an ActionParam. How can I do this? Here is the code:
<a4j:commandLink id="pendingTransactions"
action="#{tellerBean.getPendingTransactions}" value="Show Pending"
styleClass="button category-btn">
<a4j:actionparam name="first" value=""
assignTo="" />
</a4j:commandLink>
and my managed bean:
#ManagedBean
#SessionScoped
public class TellerBean implements Serializable{
public enum TransactionType {
PENDING,PROCESSED,ALL
}
private static final long serialVersionUID = -321111;
private String recipientID;
private String recipientName;
private String transactionAmount;
private TransactionType transactionType;
public String getRecipientID() {
return recipientID;
}
public void setRecipientID(String recipientID) {
this.recipientID = recipientID;
}
public String getRecipientName() {
return recipientName;
}
public void setRecipientName(String recipientName) {
this.recipientName = recipientName;
}
public String getTransactionAmount() {
return transactionAmount;
}
public void setTransactionAmount(String transactionAmount) {
this.transactionAmount = transactionAmount;
}
public void searchTransactions() {}
public TransactionType getTransactionType() {
return transactionType;
}
public void setTransactionType(TransactionType transactionType) {
this.transactionType = transactionType;
}
public void getTransactions() {}
}
Now I want to assign the value of the transactionType variable to an Enum value. How can I do this?
I don't know what you want to do with the variable or how you want to display it, so here's a generic example.
First of all, the JSF page must be able to 'iterate' over the enum to discover the possible values. I'm using h:selectOneMenu as an example which is filled using f:selectItems. f:selectItems expects a List<> as input so we need to create a method in the TellerBean:
public List<TransactionType> getTransactionTypes()
{
List<TransactionTypes> tt = new ArrayList<TransactionType>();
for (TransactionType t : TransactionType.values())
{
tt.add(new TransactionType(t, t.toString()))
}
return tt;
}
Then for an example JSF page:
<h:form>
<h:selectOneMenu value="#{tellerBean.transactionType}">
<f:selectItems value="#{tellerBean.transactionTypes}"/>
</h:selectOneMenu>
<h:commandButton value="Submit" action="#{tellerBean.someMethod}"/>
</h:form>
The JSF page should display a drop-down list with the values of the enum. When clicking the button labeled "Submit" it executes someMethod() in TellerBean. Of course this doesn't work because the method doesn't exist, but it's just an example. ;-)

Resources