I am creating a web application, where you have to read a list of objects / entities from a DB and populate it in a JSF <h:selectOneMenu>. I am unable to code this. Can someone show me how to do it?
I know how to get a List<User> from the DB. What I need to know is, how to populate this list in a <h:selectOneMenu>.
<h:selectOneMenu value="#{bean.name}">
...?
</h:selectOneMenu>
Based on your question history, you're using JSF 2.x. So, here's a JSF 2.x targeted answer. In JSF 1.x you would be forced to wrap item values/labels in ugly SelectItem instances. This is fortunately not needed anymore in JSF 2.x.
Basic example
To answer your question directly, just use <f:selectItems> whose value points to a List<T> property which you preserve from the DB during bean's (post)construction. Here's a basic kickoff example assuming that T actually represents a String.
<h:selectOneMenu value="#{bean.name}">
<f:selectItems value="#{bean.names}" />
</h:selectOneMenu>
with
#ManagedBean
#RequestScoped
public class Bean {
private String name;
private List<String> names;
#EJB
private NameService nameService;
#PostConstruct
public void init() {
names = nameService.list();
}
// ... (getters, setters, etc)
}
Simple as that. Actually, the T's toString() will be used to represent both the dropdown item label and value. So, when you're instead of List<String> using a list of complex objects like List<SomeEntity> and you haven't overridden the class' toString() method, then you would see com.example.SomeEntity#hashcode as item values. See next section how to solve it properly.
Also note that the bean for <f:selectItems> value does not necessarily need to be the same bean as the bean for <h:selectOneMenu> value. This is useful whenever the values are actually applicationwide constants which you just have to load only once during application's startup. You could then just make it a property of an application scoped bean.
<h:selectOneMenu value="#{bean.name}">
<f:selectItems value="#{data.names}" />
</h:selectOneMenu>
Complex objects as available items
Whenever T concerns a complex object (a javabean), such as User which has a String property of name, then you could use the var attribute to get hold of the iteration variable which you in turn can use in itemValue and/or itemLabel attribtues (if you omit the itemLabel, then the label becomes the same as the value).
Example #1:
<h:selectOneMenu value="#{bean.userName}">
<f:selectItems value="#{bean.users}" var="user" itemValue="#{user.name}" />
</h:selectOneMenu>
with
private String userName;
private List<User> users;
#EJB
private UserService userService;
#PostConstruct
public void init() {
users = userService.list();
}
// ... (getters, setters, etc)
Or when it has a Long property id which you would rather like to set as item value:
Example #2:
<h:selectOneMenu value="#{bean.userId}">
<f:selectItems value="#{bean.users}" var="user" itemValue="#{user.id}" itemLabel="#{user.name}" />
</h:selectOneMenu>
with
private Long userId;
private List<User> users;
// ... (the same as in previous bean example)
Complex object as selected item
Whenever you would like to set it to a T property in the bean as well and T represents an User, then you would need to bake a custom Converter which converts between User and an unique string representation (which can be the id property). Do note that the itemValue must represent the complex object itself, exactly the type which needs to be set as selection component's value.
<h:selectOneMenu value="#{bean.user}" converter="#{userConverter}">
<f:selectItems value="#{bean.users}" var="user" itemValue="#{user}" itemLabel="#{user.name}" />
</h:selectOneMenu>
with
private User user;
private List<User> users;
// ... (the same as in previous bean example)
and
#ManagedBean
#RequestScoped
public class UserConverter implements Converter {
#EJB
private UserService userService;
#Override
public Object getAsObject(FacesContext context, UIComponent component, String submittedValue) {
if (submittedValue == null || submittedValue.isEmpty()) {
return null;
}
try {
return userService.find(Long.valueOf(submittedValue));
} catch (NumberFormatException e) {
throw new ConverterException(new FacesMessage(String.format("%s is not a valid User ID", submittedValue)), e);
}
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object modelValue) {
if (modelValue == null) {
return "";
}
if (modelValue instanceof User) {
return String.valueOf(((User) modelValue).getId());
} else {
throw new ConverterException(new FacesMessage(String.format("%s is not a valid User", modelValue)), e);
}
}
}
(please note that the Converter is a bit hacky in order to be able to inject an #EJB in a JSF converter; normally one would have annotated it as #FacesConverter(forClass=User.class), but that unfortunately doesn't allow #EJB injections)
Don't forget to make sure that the complex object class has equals() and hashCode() properly implemented, otherwise JSF will during render fail to show preselected item(s), and you'll on submit face Validation Error: Value is not valid.
public class User {
private Long id;
#Override
public boolean equals(Object other) {
return (other != null && getClass() == other.getClass() && id != null)
? id.equals(((User) other).id)
: (other == this);
}
#Override
public int hashCode() {
return (id != null)
? (getClass().hashCode() + id.hashCode())
: super.hashCode();
}
}
Complex objects with a generic converter
Head to this answer: Implement converters for entities with Java Generics.
Complex objects without a custom converter
The JSF utility library OmniFaces offers a special converter out the box which allows you to use complex objects in <h:selectOneMenu> without the need to create a custom converter. The SelectItemsConverter will simply do the conversion based on readily available items in <f:selectItem(s)>.
<h:selectOneMenu value="#{bean.user}" converter="omnifaces.SelectItemsConverter">
<f:selectItems value="#{bean.users}" var="user" itemValue="#{user}" itemLabel="#{user.name}" />
</h:selectOneMenu>
See also:
Our <h:selectOneMenu> wiki page
View-Page
<h:selectOneMenu id="selectOneCB" value="#{page.selectedName}">
<f:selectItems value="#{page.names}"/>
</h:selectOneMenu>
Backing-Bean
List<SelectItem> names = new ArrayList<SelectItem>();
//-- Populate list from database
names.add(new SelectItem(valueObject,"label"));
//-- setter/getter accessor methods for list
To display particular selected record, it must be one of the values in the list.
Roll-your-own generic converter for complex objects as selected item
The Balusc gives a very useful overview answer on this subject. But there is one alternative he does not present: The Roll-your-own generic converter that handles complex objects as the selected item. This is very complex to do if you want to handle all cases, but pretty simple for simple cases.
The code below contains an example of such a converter. It works in the same spirit as the OmniFaces SelectItemsConverter as it looks through the children of a component for UISelectItem(s) containing objects. The difference is that it only handles bindings to either simple collections of entity objects, or to strings. It does not handle item groups, collections of SelectItems, arrays and probably a lot of other things.
The entities that the component binds to must implement the IdObject interface. (This could be solved in other way, such as using toString.)
Note that the entities must implement equals in such a way that two entities with the same ID compares equal.
The only thing that you need to do to use it is to specify it as converter on the select component, bind to an entity property and a list of possible entities:
<h:selectOneMenu value="#{bean.user}" converter="selectListConverter">
<f:selectItem itemValue="unselected" itemLabel="Select user..."/>
<f:selectItem itemValue="empty" itemLabel="No user"/>
<f:selectItems value="#{bean.users}" var="user" itemValue="#{user}" itemLabel="#{user.name}" />
</h:selectOneMenu>
Converter:
/**
* A converter for select components (those that have select items as children).
*
* It convertes the selected value string into one of its element entities, thus allowing
* binding to complex objects.
*
* It only handles simple uses of select components, in which the value is a simple list of
* entities. No ItemGroups, arrays or other kinds of values.
*
* Items it binds to can be strings or implementations of the {#link IdObject} interface.
*/
#FacesConverter("selectListConverter")
public class SelectListConverter implements Converter {
public static interface IdObject {
public String getDisplayId();
}
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
if (value == null || value.isEmpty()) {
return null;
}
return component.getChildren().stream()
.flatMap(child -> getEntriesOfItem(child))
.filter(o -> value.equals(o instanceof IdObject ? ((IdObject) o).getDisplayId() : o))
.findAny().orElse(null);
}
/**
* Gets the values stored in a {#link UISelectItem} or a {#link UISelectItems}.
* For other components returns an empty stream.
*/
private Stream<?> getEntriesOfItem(UIComponent child) {
if (child instanceof UISelectItem) {
UISelectItem item = (UISelectItem) child;
if (!item.isNoSelectionOption()) {
return Stream.of(item.getValue());
}
} else if (child instanceof UISelectItems) {
Object value = ((UISelectItems) child).getValue();
if (value instanceof Collection) {
return ((Collection<?>) value).stream();
} else {
throw new IllegalStateException("Unsupported value of UISelectItems: " + value);
}
}
return Stream.empty();
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
if (value == null) return null;
if (value instanceof String) return (String) value;
if (value instanceof IdObject) return ((IdObject) value).getDisplayId();
throw new IllegalArgumentException("Unexpected value type");
}
}
I'm doing it like this:
Models are ViewScoped
converter:
#Named
#ViewScoped
public class ViewScopedFacesConverter implements Converter, Serializable
{
private static final long serialVersionUID = 1L;
private Map<String, Object> converterMap;
#PostConstruct
void postConstruct(){
converterMap = new HashMap<>();
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object object) {
String selectItemValue = String.valueOf( object.hashCode() );
converterMap.put( selectItemValue, object );
return selectItemValue;
}
#Override
public Object getAsObject(FacesContext context, UIComponent component, String selectItemValue){
return converterMap.get(selectItemValue);
}
}
and bind to component with:
<f:converter binding="#{viewScopedFacesConverter}" />
If you will use entity id rather than hashCode you can hit a collision- if you have few lists on one page for different entities (classes) with the same id
Call me lazy but coding a Converter seems like a lot of unnecessary work. I'm using Primefaces and, not having used a plain vanilla JSF2 listbox or dropdown menu before, I just assumed (being lazy) that the widget could handle complex objects, i.e. pass the selected object as is to its corresponding getter/setter like so many other widgets do. I was disappointed to find (after hours of head scratching) that this capability does not exist for this widget type without a Converter. In fact if you supply a setter for the complex object rather than for a String, it fails silently (simply doesn't call the setter, no Exception, no JS error), and I spent a ton of time going through BalusC's excellent troubleshooting tool to find the cause, to no avail since none of those suggestions applied. My conclusion: listbox/menu widget needs adapting that other JSF2 widgets do not. This seems misleading and prone to leading the uninformed developer like myself down a rabbit hole.
In the end I resisted coding a Converter and found through trial and error that if you set the widget value to a complex object, e.g.:
<p:selectOneListbox id="adminEvents" value="#{testBean.selectedEvent}">
... when the user selects an item, the widget can call a String setter for that object, e.g. setSelectedThing(String thingString) {...}, and the String passed is a JSON String representing the Thing object. I can parse it to determine which object was selected. This feels a little like a hack, but less of a hack than a Converter.
Related
I have started learning JSF, but sadly most tutorials out there present only a log in or a register section.
Can you point me to some more in depth examples? One thing I'm interested in is a page presenting a list of products. I'm on page home and I press on page products so that I can see the latest products added. And every time I visit the page, the product list will be created from the latest entries in the database. How can I handle this?
One way to solve this would be to create a session scoped managed bean in which I would place different entities updated through other managed beans. I found this kind of approach in some tutorials, but it seems quite difficult and clumsy.
Which would be the best approach to solve a thing like this? What is the correct usage of session scope in two-page master-detail user interface?
What is the correct usage of session scope
Use it for session scoped data only, nothing else. For example, the logged-in user, its settings, the chosen language, etcetera.
See also:
How to choose the right bean scope?
And every time I visit the page, the product list will be created from the latest entries in the database. How can I handle this?
Typically you use the request or view scope for it. Loading of the list should happen in a #PostConstruct method. If the page doesn't contain any <h:form>, then the request scope is fine. A view scoped bean would behave like a request scoped when there's no <h:form> anyway.
All "view product" and "edit product" links/buttons which just retrieve information (i.e. idempotent) whould be just plain GET <h:link> / <h:button> wherein you pass the entity identifier as a request parameter by <f:param>.
All "delete product" and "save product" links/buttons which will manipulate information (i.e. non-idempotent) should perform POST by <h:commandLink>/<h:commandButton> (you don't want them to be bookmarkable/searchbot-indexable!). This in turn requires a <h:form>. In order to preserve the data for validations and ajax requests (so that you don't need to reload/preinitialize the entity on every request), the bean should preferably be view scoped.
Note that you should basically have a separate bean for each view and also note that those beans doesn't necessarily need to reference each other.
So, given this "product" entity:
#Entity
public class Product {
#Id
private Long id;
private String name;
private String description;
// ...
}
And this "product service" EJB:
#Stateless
public class ProductService {
#PersistenceContext
private EntityManager em;
public Product find(Long id) {
return em.find(Product.class, id);
}
public List<Product> list() {
return em.createQuery("SELECT p FROM Product p", Product.class).getResultList();
}
public void create(Product product) {
em.persist(product);
}
public void update(Product product) {
em.merge(product);
}
public void delete(Product product) {
em.remove(em.contains(product) ? product : em.merge(product));
}
// ...
}
You can have this "view products" on /products.xhtml:
<h:dataTable value="#{viewProducts.products}" var="product">
<h:column>#{product.id}</h:column>
<h:column>#{product.name}</h:column>
<h:column>#{product.description}</h:column>
<h:column>
<h:link value="Edit" outcome="/products/edit">
<f:param name="id" value="#{product.id}" />
</h:link>
</h:column>
</h:dataTable>
#Named
#RequestScoped
public class ViewProducts {
private List<Product> products; // +getter
#EJB
private ProductService productService;
#PostConstruct
public void init() {
products = productService.list();
}
// ...
}
And you can have this "edit product" on /products/edit.xhtml:
<f:metadata>
<f:viewParam name="id" value="#{editProduct.product}"
converter="#{productConverter}" converterMessage="Unknown product, please use a link from within the system."
required="true" requiredMessage="Bad request, please use a link from within the system."
/>
</f:metadata>
<h:messages />
<h:form rendered="#{not empty editProduct.product}>
<h:inputText value="#{editProduct.product.name}" />
<h:inputTextarea value="#{editProduct.product.description}" />
...
<h:commandButton value="save" action="#{editProduct.save}" />
</h:form>
#Named
#ViewScoped
public class EditProduct {
private Product product; // +getter +setter
#EJB
private ProductService productService;
public String save() {
productService.update(product);
return "/products?faces-redirect=true";
}
// ...
}
And this converter for <f:viewParam> of "edit product":
#Named
#RequestScoped
public class ProductConverter implements Converter {
#EJB
private ProductService productService;
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
if (value == null || value.isEmpty()) {
return null;
}
try {
Long id = Long.valueOf(value);
return productService.find(id);
} catch (NumberFormatException e) {
throw new ConverterException("The value is not a valid Product ID: " + value, e);
}
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
if (value == null) {
return "";
}
if (value instanceof Product) {
Long id = ((Product) value).getId();
return (id != null) ? String.valueOf(id) : null;
} else {
throw new ConverterException("The value is not a valid Product instance: " + value);
}
}
}
You can even use a generic converter, this is explained in Implement converters for entities with Java Generics.
See also:
How to navigate in JSF? How to make URL reflect current page (and not previous one)
JSF Controller, Service and DAO
JSF Service Layer
How to inject #EJB, #PersistenceContext, #Inject, #Autowired, etc in #FacesConverter?
Communication in JSF 2.0 - Contains several examples/hints
As a small improvement to what BalusC recommended, sometimes you can remove the required / requiredMessage part from the <f:viewParam> of your "details" screen and instead use the conditional rendering of the editing form (as BalusC did) with a reverse condition for recommending a specific link for the "list/master" screen or, even use a viewAction that would test the param and force a redirect to that list.
I want to refresh a XPages' control, that displays data based on input of a ListBox. I use a Backing Bean that holds the data. The Backing Bean is connected by EL Value Binding.
For a Computed Text Control, the Backing Bean returns the first selected value of the listData property.
public class BackingBean implements Serializable {
private static final long serialVersionUID = 1L;
private List listData;
private String displayData;
public BackingBean() {}
public List getListData() {
System.out.println("getListData()");
return listData;
}
public void setListData(List listData) {
System.out.println("setListData()");
this.listData = listData;
}
public String getDisplayData() {
System.out.println("getDisplayData()");
if(listData != null && listData.size() > 0) {
return (String) listData.get(0);
}
return "NO LIST DATA";
}
}
Here is my XPage code:
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core"
xmlns:xe="http://www.ibm.com/xsp/coreex">
<xp:div id="refreshMe">
<xp:listBox id="listBox1">
<xp:this.value><![CDATA[#{backingBean.listData}]]></xp:this.value>
<xp:selectItem itemLabel="Value 1"></xp:selectItem>
<xp:selectItem itemLabel="Value 2"></xp:selectItem>
<xp:eventHandler event="onchange" submit="true" refreshMode="partial" refreshId="refreshMe">
</xp:eventHandler>
</xp:listBox>
<xp:text escape="true" id="computedField1" value="#{backingBean.displayData}"></xp:text>
</xp:div>
</xp:view>
The problem is, on partial refresh, the data will be sent to the server, but the setListData setter is never called.
The computed text only shows "NO LIST DATA".
view:_id1:listBox1:Value 1 // <-- selected option is sent to the server
$$viewid:!e9x9cl6xse!
$$xspsubmitid:view:_id1:_id298
$$xspexecid:
$$xspsubmitvalue:
$$xspsubmitscroll:0|0
view:_id1:view:_id1
How can I do a partial refresh off a control, whose value relies on value bound data of another control?
Found cause and solution!
When I set disableValidators to true the setListData setter will be called and the Backing Bean gets updated.
<xp:listBox id="listBox1">
<xp:this.value><![CDATA[#{controller.listData}]]></xp:this.value>
<xp:selectItem itemLabel="Value 1"></xp:selectItem>
<xp:selectItem itemLabel="Value 2"></xp:selectItem>
<xp:eventHandler event="onchange" submit="true" refreshMode="partial" refreshId="refreshMe" disableValidators="true"></xp:eventHandler>
</xp:listBox>
To be honest, I also want to update data by using XSP.partialRefreshPost. When I try this, I'm not able to disable validation out of the box.
But Sven Hasselbach posted a solution for this:
https://stackoverflow.com/a/21931796/4735030
(In short: Implement a PhaseListener that disables Validation on demand.)
The event handler, that uses XSP.partialRefreshPost looks like this:
<xp:eventHandler event="onchange" submit="false">
<xp:this.script><![CDATA[
XSP.partialRefreshPost(#{id:refreshMe}', {'params': {'disableValidation':true}});
]]></xp:this.script>
</xp:eventHandler>
Try adding a setListData(String listData) method as well. "1" is not a List, so I wouldn't expect it to call your current method. I'm also not sure whether an xp:listBox maps to a List or a Vector. I would expect it to map to a Vector.
I am building a JSF form which includes a primefaces p:autoComplete component.
The following is an extract from my xhtml page, showing the relvant information about the autoComplete component.
<p:autoComplete
value="#{curAttribute.value}"
completeMethod="#{newBacking.lookupActivated}"
var="curEntry"
itemLabel="#{curEntry.classname}"
itemValue="#{curEntry.id}"
emptyMessage="Start typing..."/>
Please notice that curAttribute is an instance of the CosmoAttribute class, and that the CosmoAttribute.value is a String (of course, CosmoAttribute has all the getters and setters for its fields).
The method newBacking.lookupActivated() returns a List<CosmoCard>.
CosmoCard.classname and CosmoCard.id are both Strings.
I know that I'm woirking with POJOs, but since all my values are String fields from a POJO, I don't think that I need a converter. Anyway, my autoComplete field works fine, but when i select an item, i get the following exception:
SEVERE: Error Rendering View[/test.xhtml]
javax.el.PropertyNotFoundException: /test.xhtml #98,68 itemLabel="#{curEntry.id}": The class 'java.lang.String' does not have the property 'id'.
at com.sun.faces.facelets.el.TagValueExpression.getValue(TagValueExpression.java:111)
at javax.faces.component.ComponentStateHelper.eval(ComponentStateHelper.java:194)
at org.primefaces.component.autocomplete.AutoComplete.getItemLabel(AutoComplete.java:148)
.
.
Caused by: javax.el.PropertyNotFoundException: The class 'java.lang.String' does not have the property 'id'.
at javax.el.BeanELResolver.getBeanProperty(BeanELResolver.java:730)
at javax.el.BeanELResolver.getValue(BeanELResolver.java:351)
at com.sun.faces.el.DemuxCompositeELResolver._getValue(DemuxCompositeELResolver.java:176)
.
.
Has anyone a clue of what am I doing wrong? You can find a very similar question here, but it was unfortunately unanswered. I am willing to provide more details about my code.
UPDATE:
Actually, everything is working fine: I see the correct value in the dropdown of the p:autocomplete. As i select a value, the data in the backing bean (newBacking) is updated accordingly. I just can't get rid of the exception, which, however, does not have any effect on the execution of my page.
I am respecting the constraints of the tag, i.e. the value attribute and the itemValue are of the same type (both Strings). The only thing that is not properly ok is that the system tries to convert a List in a List, I don't know why, or when, but the failure in the conversion (and the subsequent exception) does not have any effect on the behaviour of my page.
UPDATE:
Here is the link to a very simplified version of the project (netbeans). The relevant files of the project are also listed below.
test.xhtml
<h:body>
<h:form id="form">
<p:dataTable var="curAttribute" value="#{newBacking.card.attributes}">
<p:column >
THE CURSED FIELD <br /><br />
<p:autoComplete
value="#{curAttribute.value}"
completeMethod="#{newBacking.lookupActivated}"
var="curEntry"
itemLabel="#{curEntry.code}"
itemValue="#{curEntry.id}">
</p:autoComplete>
</p:column>
</p:dataTable>
</h:form>
newBacking.java
#Named()
#SessionScoped
public class NewBacking implements Serializable {
private CosmoCard card;
private String currentCardClassname = "";
#PostConstruct
public void init() {
Random randomGenerator = new Random();
card = new CosmoCard();
card.setId("ID" + randomGenerator.nextInt(1000));
CosmoAttribute myLA = new CosmoAttribute();
myLA.setLabel("LookupAttributeLabel");
myLA.setValue("LookupAttributeValue");
card.getAttributes().add(myLA);
}
public CosmoCard getCard() {
return card;
}
public String getCurrentCardClassname() {
return currentCardClassname;
}
public void setCurrentCardClassname(String currentCardClassname) {
this.currentCardClassname = currentCardClassname;
}
public List<CosmoCard> lookupActivated(String tgtQuery) {
Logger.getLogger(NewBacking.class.getName()).info("[NewBacking.lookupActivated()] Query: " + tgtQuery);
return CosmoCardList.generateCardList(10).getCards();
}
}
Your complete method is returning an instance of a complex class and thereby you need a converter. Without the existense of a converter, your component is thinking that its working with a string and trying to give reference to it when getting value and label attributes. Try using this and see if it works but I strongly recommend creating your own converter based on your needs:
#FacesConverter("anySelectConverter")
public class AnySelectConverter implements Converter{
private static Map<Object, String> entities = new ConcurrentHashMap<Object, String>();
#Override
public String getAsString(FacesContext context, UIComponent component, Object entity) {
// TODO : Fix
if(entity == null)
return "";
synchronized (entities) {
if (!entities.containsKey(entity)) {
String uuid = UUID.randomUUID().toString();
entities.put(entity, uuid);
return uuid;
} else {
return entities.get(entity);
}
}
}
#Override
public Object getAsObject(FacesContext context, UIComponent component, String uuid) {
for (Entry<Object, String> entry : entities.entrySet()) {
if (entry.getValue().equals(uuid)) {
return entry.getKey();
}
}
return null;
}
}
I am using JSF 2.0 with Primefaces 3.4.2
I have the following in JSF page
<p:selectOneMenu value="#{mb.employee}">
<f:selectItems value="#{mb.employeeList}" var="emp"
itemLabel="#{emp.employeeName}" itemValue="#{emp.employeeCode}"/>
<p:ajax listener="#{mb.changeMethod}" />
</p:selectOneMenu>
Problem is when I select a value in selectOneMenu, I am getting null in changeMethod of ManagedBean, for this System.out.println("val "+employee.getEmployeeName());
What could be the reason for this? How can I resolve this problem?
Any hep is highly appreciable.
ManagedBean Code
#Named("mb")
#ViewAccessScoped
public class MyBean implements Serializable {
private Employee employee;
private List<Employee> employeeList;
#Inject
EmployeeService employeeService;
#PostConstruct
public void loadEmployees() {
employeeList = employeeService.getEmployees();
}
public void changeMethod() {
System.out.println("val "+employee.getEmployeeName());
}
/* getters and setters for employee and employeeList */
.... methods
/* */
You are not specifying on the <p:ajax> component which form elements to process, so the ajax request may be submitting multiple values that could be conflicting with other field validation. Remmeber that if a value is submitted and it fails validation, then none of the request values get set to the model. When you reach the Application(Event) phase, the model values will not reflect any submitted request values.
Try this:
<p:selectOneMenu value="#{mb.employee}">
<f:selectItems value="#{mb.employeeList}" var="emp"
itemLabel="#{emp.employeeName}" itemValue="#{emp.employeeCode}"/>
<p:ajax process="#this" partialSubmit="true" listener="#{mb.changeMethod}" />
</p:selectOneMenu>
Above you will be submitting just the current component request value to be applied to the model.
EDIT:
Actually it probably isn't a validation issue so much as there are no request values being submitted on the ajax event.
According to the Primefaces Manual:
process null String Component(s) to process in partial request.
update null String Component(s) to update with ajax.
The second column is Default Value. In the standard <f:ajax> the execute attribute defaults to #this however this is not the case for <p:ajax>. If you want to submit the current component then you must specify this in the process attribute.
You need to add converter to p:selectOneMenu to let it know that you are dealing with employee object. Create the converter class and use it with converter attribute. Here is the sample converter.
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.FacesConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
#FacesConverter("employeeConverter")
#Configurable
public class EmployeeConverter implements Converter {
#Autowired
EmployeeService employeeService;
public Object getAsObject(FacesContext context, UIComponent component,
String value) {
if (value == null || value.length() == 0) {
return null;
}
Long id = Long.parseLong(value);
return employeeService.findUser(id);
}
public String getAsString(FacesContext context, UIComponent component,
Object value) {
return value instanceof Employee ? ((Employee) value).getId().toString() : "";
}
}
I have a JSF page where users can enter their car into my database. The form has three input fields:
Manufacturer
Model
Registration
I am using PrimeFaces 3.0.M2 and both the Manufacturer and Model field are autocomplete input fields:
<p:autoComplete id="manufacturer"
minQueryLength="3"
completeMethod="#{carController.completeManufacturer}"
forceSelection="true"
value="#{carController.manufacturer}" />
The field for the model looks the same, with slightly different values obviously.
The managed bean looks as follows (slightly abbreviated):
private String manufacturer;
private String model;
private String registration;
public List<String> completeManufacturer(String query) {
List<String> ms = new ArrayList<String>();
for (Manufacturer m : manufacturerFacade.findAllByName(query)) {
ms.add(m.getLongName());
}
return ms;
}
public List<String> completeModel(String query) {
List<String> ms = new ArrayList<String>();
for (Model m :
modelFacade.findAllByManufacturer(manufacturerFacade.findByName(manufacturer))) {
ms.add(m.getShortName());
}
return ms;
}
The problem lies in completing the model field. I need this field to only display autocompletion results based on the selected manufacturer, but the manufacturer String in the managed bean does not get populated until the entire form is submitted, so I cannot look the models up that are associated with the selected manufacturer.
How would I go about submitting only the manufacturer field, without submitting the entire form, so I can look the models of the selected manufacturer up?
Thanks!
Just add a selectListener, like so:
<p:autoComplete id="manufacturer"
minQueryLength="3"
completeMethod="#{carController.completeManufacturer}"
forceSelection="true"
selectListener="#{carController.manufacturerSelected}"
value="#{carController.manufacturer}" />
and then in the controller:
public void manufacturerSelected(SelectEvent vce) {
String nameOfSelected = vce.getObject().toString();
// whatever logic comes to your mind
}
You could add an extra ajax handler to the manafucturer input field and then handle the onchange event. In the server-side handler, simply remember the new value in your backing bean.
If you then put your backing bean in the view scope, the ajax requests originating from the model input field will get the same instance and you have direct access to the manufacturer field that you previously remembered.