Using SelectManyCheckbox with list of objects - jsf

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

Related

How use primefaces autocomplete into composite with preselected values?

I have a problem using autocomplete primefaces component inside of an UIInput composite. My goal is to init the application with preselected value in autocomplete field, showing a label accordingly. Below I show a test code
Page testPage.xhtml
<f:view id="view" locale="#{webSession.currentLanguage.locale}">
<h:head>
<title>...</title>
</h:head>
<h:body>
<h:form>
<utils:element/>
<p:autoComplete
value="#{testPage.attr}"
completeMethod="#{testPage.completeMethod}"
var="item"
itemLabel="#{item}"
itemValue="#{item}" />
</h:form>
</h:body>
</f:view>
Managed Bean TestPage.xhtml
#ManagedBean(name = "testPage")
#ViewScoped
public class TestPage {
private String attr;
#PostConstruct
public void init(){
attr = "value 1";
}
public String getAttr() {
return attr;
}
public void setAttr(String attr) {
this.attr = attr;
}
public List<String> completeMethod(String query) {
return Arrays.asList(new String[]{"1111", "2222", "3333"});
}
}
This approach works fine using the autocomplete directly on testPage.xhtml. However, I want to wrap this autocomplete in a element composite, as showed in following code
element.xhtml composite page
<ui:component xmlns="http://www.w3.org/1999/xhtml"
xmlns:p="http://primefaces.org/ui"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:cc="http://java.sun.com/jsf/composite">
<cc:interface componentType="elementComponent">
</cc:interface>
<cc:implementation>
<p:autoComplete
value="#{cc.attr}"
completeMethod="#{cc.completeMethod}"
var="item"
itemLabel="#{item}"
itemValue="#{item}" />
</cc:implementation>
</ui:component>
ElementComponent composite backing
#FacesComponent("elementComponent")
#ViewScoped
public class ElementComponent extends UIInput implements NamingContainer{
private String attr;
#Override
public String getFamily() {
return UINamingContainer.COMPONENT_FAMILY;
}
public List<String> completeMethod(String query) {
return Arrays.asList(new String[]{"value 1", "value 2", "value 3"});
}
#Override
public void encodeBegin(FacesContext context) throws IOException {
attr = "value 1";
}
public String getAttr() {
return attr;
}
public void setAttr(String attr) {
this.attr = attr;
}
}
But when I include the element composite in testPage.xhtml, the autocomplete does not show the preselected value (unlike the direct implementation). Is there any way to solve this? Maybe any method or attribute is missing in the implementation of FacesComponent? I tend to think this is a bug between the implementation of primefaces and the implementation of composite, but I am not sure.
The problem was the method encodeBegin(). This implementation require the encode of the component class, and the encode of the parent (UIInput).
Incorrect
#Override
public void encodeBegin(FacesContext context) throws IOException {
attr = "value 1";
}
Correct
#Override
public void encodeBegin(FacesContext context) throws IOException {
attr = "value 1";
super.encodeBegin();
}

Retrieve class values from selectCheckboxMenu

I have a <p:selectCheckboxMenu> and I want to get the selected values back in bean. But the value I receive
when I select an item from the menu it's a string, representing the type field from the CategorizationBean.
I just want when I select an item from the table, to get the whole CategorizationBean structure in the bean.
This is the snippet from the xhtml page:
<p:selectCheckboxMenu label="Categorization"
value="#alertMB.selectedCategories}"
converter="com.converter.CategoryConverter">
<f:selectItems value="#{alertMB.categoryDomainEntry}"
var="category"
itemLabel="#{category.type}"
itemValue="#{category}"/>
</p:selectCheckboxMenu>
Snippet from bean:
public List<CategorizationBean> getSelectedCategories() {
return selectedCategories;
}
public void setSelectedCategories(List<CategorizationBean> selectedCategories) {
this.selectedCategories = selectedCategories;
}
public class CategorizationBean implements Serializable{
private String type;
private long id;
I think that you have missed by using a list of beans, I use this example and it works:
<p:selectCheckboxMenu id="slctRdBtn"
value="#{yourBean.compLovDtgrid}"
converter="compLovDtgridConverter">
<f:selectItems
value="#{yourBean.listCompLovDtgrid}"
var="rdbtn" itemLabel="#{rdbtn.vjlrLibelleRep}"
itemValue="#{rdbtn}" />
</p:selectCheckboxMenu>
and for the converter:
#FacesConverter(forClass=CompLovDtgrid.class , value="compLovDtgridConverter")
public class CompLovDtgridConverter implements Converter{
#Override
public String getAsString(FacesContext context, UIComponent component, Object value)
{
return (value instanceof CompLovDtgrid) ? ((CompLovDtgrid) value).getVjlrCodeRep() : null;
}
#Override
public Object getAsObject(FacesContext context, UIComponent component,String value)
{
if(value == null)
return null;
YourBean data = context.getApplication().evaluateExpressionGet(context, "#{yourBean}", YourBean.class);
for(CompLovDtgrid compLovDtgrid : data.getListCompLovDtgrid())
{
if(compLovDtgrid.getVjlrCodeRep().equals(value))
return compLovDtgrid;
}
throw new ConverterException(new FacesMessage(String.format("Cannot convert %s to CompLovDtgrid", value)));
}
}
and for the list, I use:
public List<CompLovDtgrid> getListCompLovDtgrid()
{
return listCompLovDtgrid;
}
public void setListCompLovDtgrid(List<CompLovDtgrid> listCompLovDtgrid) {
this.listCompLovDtgrid = listCompLovDtgrid;
}

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

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

picklist PrimeFaces - How to get data from target-list?

I've created a picklist via PrimeFaces. Now i want to handle the selected items which are listed in the target list when i click the commandButton.
I want to pass the data through the controller and store them in my database. But everytime i call the function duallist.getTarget() it's empty.
I've crated a foreach-Loop where i want to select all items in the target list:
Controller (Bean):
private List<DTOAktivitaet> source = new ArrayList<DTOAktivitaet>();
private List<DTOAktivitaet> target = new ArrayList<DTOAktivitaet>();
private List<DTOAktivitaet> zwischen = new ArrayList<DTOAktivitaet>();
public void speicherAktiZug() {
DTOAktivitaet aktivitaet_vorgaenger = null;
for (DTOAktivitaet item : controller.getAktivitaeten()) {
if (item.toString().equals(selected)) {
aktivitaet_vorgaenger = item;
}
}
for (DTOAktivitaet aktivitaet : zwischen) {
try {
dao.aktiZugAkt(aktivitaet_vorgaenger, aktivitaet);
} catch (SQLException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
public AktiListController() {
for (DTOAktivitaet ak : controller.getAktivitaeten()) {
source.add(ak);
}
aktis = new DualListModel<DTOAktivitaet>(source, target);
zwischen = aktis.getTarget();
}
JSF:
<h:form id="form" name="formular">
<h:outputText id="aktivitaet"
value="#{aktiListController.selected}" />
<p:pickList id="pickList" value="#{aktiListController.aktis}"
var="aktivitaet" itemValue="#{aktivitaet}"
itemLabel="#{aktivitaet}" converter="aktivitaetsConverter"
showSourceControls="true" showTargetControls="true" />
<h:commandButton
action="#{aktiListController.speicherAktiZug}"
value="Aktivität-Abhängigkeit anlegen" class="commandButton">
</h:commandButton>
</h:form>
Converter:
#EJB
public class AktiListConverter implements Converter {
private InitialisierungController controller = InitialisierungController
.getInstance();
DTOAktivitaet aktivitaet = new DTOAktivitaet();
String name = "";
#Override
public Object getAsObject(FacesContext arg0, UIComponent arg1, String arg2) {
for (DTOAktivitaet item : controller.getAktivitaeten()) {
if (item.toString().equalsIgnoreCase(arg2)) {
this.aktivitaet = item;
System.out.println(aktivitaet);
return aktivitaet;
}
}
return null;
}
#Override
public String getAsString(FacesContext arg0, UIComponent arg1, Object arg2) {
this.aktivitaet = (DTOAktivitaet) arg2;
return this.name = aktivitaet.getTeambezeichnung();
}
}
My Problem: The target-List is empty before i want to store the items in my database.
I don't fully understand your code as it is not written in English but as far as I can see your Converter is written badly. As far as I can see you do a toString() and a fromString() basically. This is quite error prone and the way you did it, heavy in performance. It is a better idea to use unique ID's (business or database).
Example:
#FacesConverter(value = "aktiListConverter")
public class AktiListConverter implements Converter
{
private InitialisierungController controller = InitialisierungController.getInstance();
#Override
public Object getAsObject(FacesContext arg0, UIComponent arg1, String arg2)
{
//Get object by it's unique ID
return controller.getById(Long.parseLong(arg2));
}
#Override
public String getAsString(FacesContext arg0, UIComponent arg1, Object arg2)
{
//Return object's unique ID
return ((DTOAktivitaet) arg2).getId();
}
}
In stead of using the object as itemLabel (which performs a toString()) use something that generates a nice label like getName() for a person.
itemLabel="#{aktivitaet.nameOrSomething}"
The speicherAktiZug() method doesn't really make sense to me so I came this far:
public class AktiListController
{
private List<DTOAktivitaet> source;
private List<DTOAktivitaet> target = new ArrayList<DTOAktivitaet>();
private DualListModel<DTOAktivitaet> aktis;
public AktiListController()
{
source = controller.getAktivitaeten();
aktis = new DualListModel<DTOAktivitaet>(source, target);
}
//Getters and setters
public void speicherAktiZug()
{
target = aktis.getTarget();
//target should contain the picked items here.
}
}
I see you are also using aktiListController.selected but I cannot see what it's used for.
Names of Conterter between (XHTML) and (Class Converter) is not equal.
converter="aktivitaetsConverter"
public class AktiListConverter implements Converter {...}

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.

Resources