I'm using the PrimeFaces p:autoComplete widget in a search form of my project. The user can choose how many and which form-elements (search parameters) he wants to include so I need to pass an ID to the completeMethod for each of them. I've tried adding onfocus=".." to pass the object to the bean but that only would be activated when the element first is loaded.
My question: How can I pass an attribute to the completeMethod?
XHTML of the element (simple):
<p:autoComplete value="#{filter.value}" label="dynamic search attribute"
completeMethod="#{myBean.complete}" />
The bean (simple):
#Named("myBean")
public class MyController implements Serializable {
public List<String> complete(String query) {
List<String> results = new ArrayList<String>();
// ... code
return results;
}
}
In theory this would seem like the perfect solution:
<p:autoComplete value="#{filter.value}" label="dynamic search attribute"
completeMethod="#{myBean.complete(filter)}" />
And again the bean:
#Named("myBean")
public class MyController implements Serializable {
public List<String> complete(String query, FilterObject o) {
List<String> results = new ArrayList<String>();
// ... database query based on FilterObject o
return results;
}
}
You can set it as an attribute:
<p:autoComplete value="#{filter.value}" label="dynamic search attribute" completeMethod="#{myBean.complete}">
<f:attribute name="filter" value="#{filter}" />
</p:autoComplete>
and get it by UIComponent#getCurrentComponent():
public List<String> complete(String query) {
FacesContext context = FacesContext.getCurrentInstance();
FilterObject o = (FilterObject) UIComponent.getCurrentComponent(context).getAttributes().get("filter");
// ...
}
Alternatively, as that #{filter} appears in your case to be already in the EL scope, you can also leave the <f:attribute> away and get it by evaluating the EL expression programmatically with help of Application#evaluateExpressionGet():
public List<String> complete(String query) {
FacesContext context = FacesContext.getCurrentInstance();
FilterObject o = context.getApplication().evaluateExpressionGet(context, "#{filter}", FilterObject.class);
// ...
}
Or, if it is also a #Named bean, then you can just #Inject it in the parent bean:
#Inject
private FilterObject o;
Related
This is my JSF pages
<h:form>
First Name: <h:inputText value="#{userBean.first}" valueChangeListener="#{userBean.updateLastName}" onblur="submit()"/><br/>
Last Name: <h:inputText value="#{userBean.last}"/><br/>
Last Name: <h:outputText value="#{userBean.last}"/><br/> </h:form>
Following is the managed bean code.
#Named
#RequestScoped
public class UserBean {
private Map<String, String> names;
private String first, last;
public UserBean() {
names = new HashMap<>();
names.put("first1", "last1");
names.put("first2", "last2");
}
public String getFirst() {return first;}
public void setFirst(String first) {this.first = first;}
public String getLast() {return last;}
public void setLast(String last) {this.last = last;}
public void updateLastName(ValueChangeEvent e){
last = names.get(e.getNewValue().toString());
FacesContext.getCurrentInstance().renderResponse();
}
}
Now, when I type "first1" on the FirstName field and tab out, I see "last1" on the outputText field but inputText field remains empty.
I did the debugging and found getter is being called only once, so I wonder why getter is not being called for the inputText? Considering EL Expression is exactly same and based on my knowledge getter should be called during Render Response phase???
Here is my code
Pojo
public class Deal implements Serializable {
private int id;
private String name;
private String description;
private Customer customer;
//getter setter omitted
}
public class Customer implements Serializable {
private int id;
private String name;
private String email;
private String phone;
//getter setter and equal hashcode omitted
}
Managed Bean
#ManagedBean(name="dealBean")
#ViewScoped
public class DealBean implements Serializable {
private List<Customer> customerList;
private List<Deal> dealList;
private Deal deal;
#PostConstruct
public void init() {
deal = new Deal();
dealList = new ArrayList<Deal>();
customerList = new ArrayList<Customer>();
customerList.add(new Customer(1, "MPRL", "mprl#mail.com", "1234455"));
customerList.add(new Customer(2, "Total", "total#mail.com", "3434323"));
customerList.add(new Customer(3, "Petronas", "petronas#mail.com", "8989876"));
}
//getter setter omitted
}
Customer Converter
#FacesConverter("customerConverter")
public class CustomerConverter implements Converter {
#Override
public Object getAsObject(FacesContext arg0, UIComponent arg1, String customerID) {
DealBean dealBean = (DealBean) FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get("dealBean");
if (dealBean != null) {
List<Customer> customerList = dealBean.getCustomerList();
for (Customer customer : customerList) {
if (customerID.equals(String.valueOf(customer.getId()))) {
return customer;
}
}
}
return null;
}
#Override
public String getAsString(FacesContext arg0, UIComponent arg1, Object obj) {
if (obj != null) {
return String.valueOf(((Customer)obj).getId());
}
return null;
}
}
XHTML
Customer : <h:selectOneMenu id="customer" value="#{dealBean.deal.customer}">
<f:converter converterId="customerConverter" />
<f:selectItems value="#{dealBean.customerList}" var="cus"
itemLabel="#{cus.name}" itemValue="#{cus}" />
</h:selectOneMenu>
When the managed bean is in request or session scope, the Customer pojo is set correctly to Deal pojo. The problem is when the managed bean is in View scope, the Customer pojo is set to Deal pojo as NULL.
I am using JSF 2.2.0
Thanks much for the help in advance.
It's not the converter, is the view scoped the one broken:
Since you're using JSF tags, you cannot use #ViewScoped annotation, because it was removed from specification and recovered only for CDI usage. You could use omnifaces view scoped or the components of apache myFaces (I personally recommend omnifaces).
You can confirm this creating a
System.out.print("Creating");
in the constructor and checking how is called each Ajax request, so the bean is not recovered and since is marked as view and is a partial request, the values are not setted again (unless you send all the form, which is not a nice solution), other workaround could be making the bean request and recover all the data each request, making it Session (but will be alive for the session), or the #ConvesationScoped, in which you'll have to destroy and start the conversation manually.
Again, my first recommendation could be change to a Java ee server compliant and use the CDI annotations since JSF are being depreciated and not updated anymore
I'm playing around with genericizing some JSF pages. One of the features that's been requested is making them fieldname-agnostic -- passing in fieldnames as parameters rather than having them bound to specific getters/setters in the backing bean.
So instead of binding a specific field like this:
<custom:editor value="#{backingViewBean.editorValue}" />
... to the back-end methods like these:
public String getEditorValue() ...
public void setEditorValue(String editorValue) ...
... I'm trying to bind it to back-end methods like these:
public String getFieldValue(String fieldName) ...
public void setFieldValue(String fieldName, String fieldValue) ...
Is there some elegant way I can do this with the value attribute mimicking how getters and setters usually behave? Or does this approach require more complexity than that?
For me it looks like you are searching for a ways to set one pair of getter and setter for all your variables inside your bean so if you are to 'lazy' them for alle your data in your managed bean you can implement java.util.Map into the bean then you can overwrite the put and get Methode like this:
public class DataBean implements Serializable, java.util.Map {
public class DataBean implements Serializable, java.util.Map {
private static final long serialVersionUID = 1L;
private final HashMap<String, Object> BeanData;
public DataBean(){
BeanData = new HashMap<String,Object>();
}
#Override
public Object get(final Object key) {
if (key == null) {throw new IllegalArgumentException("Key cannot be null.");}
return BeanData.get(key);
}
#Override
public Object put(final Object key, final Object value) {
if (key == null) {throw new IllegalArgumentException("Key cannot be null.");}
BeanData.put(key.toString(), value);
return null;
}
This will allow you to store values in your bean without creating getter and setter for every var in your bean:
<h:inputText value="#{dataBean.key1}"></h:inputText>
<h:outputLabel value="#{dataBean.key1}"></h:outputLabel>
<h:inputText value="#{dataBean.key2}"></h:inputText>
<h:outputLabel value="#{dataBean.key2}"></h:outputLabel>
<h:selectManyListbox id="sectorsListBox" size="2" multiple="multiple" value="#{Mybean.classificationSelectedItems}">
<f:selectItems id="sectors" value="#{Mybean.classificationSelectItems}"/>
</h:selectManyListbox>
Backing Bean has:
public class Mybean
{
private Map<String,String> classificationSelectItems = new LinkedHashMap<String,String>();
private List<String> classificationSelectedItems = new ArrayList<String>();
//getter and setter for both.
}
init()
{
classificationSelectItems.put("INS","Insurance")
classificationSelectItems.put("HLC","HealthCare")
}
The select many box gets initialized with these 2 values but the problem is only the last selected entry is getting stored in classificationSelectedItems. Why is that so ? And how do I get all the selected entries stored in the list of classificationSelectedItems ?
Adding FYI, the init method is class by Spring.
I have tested with an examle(reference:http://www.mkyong.com/jsf2/jsf-2-multiple-select-listbox-example/), good luck :)
Facelets:
<h:form id="form">
<h:selectManyListbox value="#{user.favFood1}" >
<f:selectItems value="#{user.favFood2Value}" />
</h:selectManyListbox>
<h:commandButton value="test"/>
</h:form>
Bean:
#ManagedBean(name = "user")
#ViewScoped
public class UserBean implements Serializable {
private static final long serialVersionUID = 1L;
public List<String> favFood1;
private Map<String, Object> food2Value;
public UserBean() {
favFood1 = new ArrayList<String>();
food2Value = new LinkedHashMap<String, Object>();
food2Value.put("Food2 - Fry Checken", "Fry Checken1"); //label, value
food2Value.put("Food2 - Tomyam Soup", "Tomyam Soup2");
food2Value.put("Food2 - Mixed Rice", "Mixed Rice3");
}
public List<String> getFavFood1() {
return favFood1;
}
public void setFavFood1(List<String> favFood1) {
this.favFood1 = favFood1;
}
public Map<String, Object> getFavFood2Value() {
return food2Value;
}
}
I noticed exactly this behaviour when I used a Collection in the setter method, like
public void setClassificationSelectedItems(Collection<String> in){
// store it somewhere
}
This setter is called during the restore phase but not during the update phase, so the previously set value will be set, but never the new one. If you use a List, it works as expected:
public void setClassificationSelectedItems(List<String> in){
// store it somewhere
}
Note that you will need to redeploy the application after such a change because the JSP needs to be recompiled but this isn’t done automatically.
I have the following dropdown in a Facelets page:
<h:selectOneMenu value="#{contactBean.selectedContact}" converter="#{contactConverter}">
<f:selectItems value="#{contactsHolder.contacts}" var="contact"
itemLabel="#{contact.firstName}" itemValue="#{contact}" />
</h:selectOneMenu>
The problem is, no matter what I put in for itemLabel (JSF EL expression or just plain text), it doesn't display. Any idea what I'm doing wrong?
Here's ContactConverter:
#ManagedBean(name = "contactConverter")
#RequestScoped
public class ContactConverter implements Converter, Serializable {
#ManagedProperty(value = "#{contactsHolder}")
private ContactsHolder contactsHolder;
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
return contactsHolder.getContacts().get(value);
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
return ((Contact) value).getContactID();
}
//getter & setters...
}
And ContactsHolder:
#ManagedBean
#SessionScoped
public class ContactsHolder implements Serializable {
private Map<String, Contact> contacts;
#PostConstruct
public void init() {
contacts = new LinkedHashMap<String, Contact>();
//get Contacts data and populate map...
}
//getters & setters...
}
You're feeding a Map<String, Contact> to <f:selectItems value>. Each item in var attribute will be a Map.Entry<String, Contact> which has only getKey() and getValue() methods returning the String map key and Contact map value respectively. The Map.Entry class indeed doesn't have a getFirstName() method.
Fix it accordingly:
<f:selectItems value="#{contactsHolder.contacts}" var="contact"
itemLabel="#{contact.value.firstName}" itemValue="#{contact.value}" />
Or, if you target a Servlet 3.0 / EL 2.2 capable container which allows invoking non-getter methods, so that you can use Map#values() to get a Collection<Contact>:
<f:selectItems value="#{contactsHolder.contacts.values()}" var="contact"
itemLabel="#{contact.firstName}" itemValue="#{contact}" />
Or, make the #{contactsHolder.contacts} a List<Contact> instead so that your initial view code will work:
<f:selectItems value="#{contactsHolder.contacts}" var="contact"
itemLabel="#{contact.firstName}" itemValue="#{contact}" />