Hi I have a problem about dynamically displaying the value of a HtmlSelectOneMenu. Below is a small application that describes my problem.
I have a list of cars List<Car> carList = new ArrayList<Car>() in my backing bean.
Car is a abstract class and Toyota and Ford extends Car.
Now I need to display different message in the selectonemenu based on the class type. If it is Toyota then I would display something else. Maybe its clearer for the codes to tell the story.
Backing Bean:
#ManagedBean(name="myBean")
#SessionScoped
public class MyCarBackingBean implements PhaseListener {
private List<Car> carList = new ArrayList<Car>();
private HtmlSelectOneMenu hsom;
Car myCar;
#PostConstruct
public void init() {
carList.add(new Ford());
carList.add(new Toyota());
}
#Override
public void beforePhase(PhaseEvent event) {
//hsom becomes null here. Im pretty sure the setHsom was called before and the variable was set.
if(hsom != null) {
switch((Integer)hsom.getValue()){
case 1: hsom.setValue("This is a Ford car"); break;
case 2: hsom.setValue("This is a Toyota car");
}
}
//The rest of the world...
}
And i bind the selectonemenu to the component in my page:
<h:form>
<h:selectOneMenu binding="#{myBean.hsom}">
<f:selectItems value="#{myBean.carList}" var="car" itemValue="#{car.id}" itemLabel="#{car.id}" />
</h:selectOneMenu>
<h:commandButton value="Submit" action="#{myBean.mySubmit()}"/>
</h:form>
And finally the model classes:
public abstract class Car {
protected int id;
//Getters and Setters
}
public class Toyota extends Car {
public Toyota(){
this.id = 2; //in case of ford car, id is 1.
}
}
And I'm thinking using a phase listener to change the display, cos I read some posts saying that it is bad to change the getters and setters and put business logic in them. Nor do I want to wrap those cars in other objects and make use of itemLabel and itemValue.
But when I was debugging it I found that hsom is null when the execution reaches beforePhase but it is not null in the rest part of the code.
So my questions are : is it a good way to use a phase listener for this? And why is the component object null in beforePhase?
Add a different attribute (say description) to your class. Implement it as you like, and refer to it in the selectItems tag. V.g.
<f:selectItems value="#{myBean.carList}" var="car" itemValue="#{car.id}" itemLabel="#{car.description}" />
Alternatively, replace myBean.carList with a method that returns a List<SelectItem>, and create the selectItems as you wish.
As a rule of thumb, try to keep the .xhtml as "logic-free" as possible.
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 try to set the values of a selectManyCheckbox to my testBean backing bean.
If I use a property of type List<String> instead of Attributed<List<String>> it works perfectly. It is the workaround I'm using currently.
But on my backing bean I have a generic object which contains the List.
The javax.el.BeanELResolver resolve this to an Object. Which is correct due to Type erasure.
I tried to implement a custom ElResolver. But I should know to which type to convert the object to. It isn't obviously always a List. I have the information in the xhtml pages. So I hoped I could pass some child element which would contain the information, but could not find a way to access the child element from the ElResolver.
A Custom converted does not work either as it converts selectItems, not the List.
Here is the simplest form
<h:form>
<p:selectManyCheckbox value="#{testBean.attributed.value}" >
<f:selectItems value="#{testBean.selection}" />
</p:selectManyCheckbox>
<p:commandButton action="#{testBean.execute}" value="do it" />
</h:form>
and the bean
private Attributed<List<String>> attributed = new Attributed<>();
public Map<String, String> getSelection() {
return ImmutableMap.<String, String> of("key1", "value1", "key2", "value2");
}
public static class Attributed<T> {
private T value;
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
}
public Attributed<List<String>> getAttributed() {
return attributed;
}
public void setAttributed(Attributed<List<String>> attributed) {
this.attributed = attributed;
}
So the question is:
Is there a way to set the value to testBean.attributed.value directly and with the correct type.
Is it possible by defining a custom ElResolver, or are there other way to do it?
I am trying to do some "custom lightweight" JSF component binding. In my bean (controlling some JSF page) i'm declaring a HahsMap where keys ranging over my h:inputText ids (appearing in the page) map these ids to custom HInputText<T> objects (T in the example given below is Long). Then i am using HInputText<T> objects to hold a subset of corresponding h:inputText attributes : value supposed to be of type T, rendered, required, etc. In this direction fields from the HInputText<T> objects give values to the h:inputText attributes.
My problem is that when using such an h:inputText inside a h:form, JSF validation does not take place : i can type alphanumeric chars in the h:inputText (supposed to hold a Long value) and my form submits without showing any errors. Note that required and rendered attributes are managed correctly (since required is set to true, i have an error when i leave the h:inputText field empty) and that when i try to display back the value of the h:inputText in the page using some h:outputText the alphanumeric chars are displayed.
Is there some trick to make JSF validation work without having to explicitly define a custom Validator for each h:inputText?
HInputText.class:
public class HInputText<T>
{
private String id;
private boolean rendered;
private boolean required;
private T value;
private Class<T> myClass;
// getters and setters for all fields
public HInputText(Class<T> myClass, String id)
{
this.myClass = myClass;
this.id = id;
this.rendered = true;
this.required = true;
}
}
Code snippet from my Managed Bean :
#ManagedBean(name="saisieController")
#SessionScoped
public class SaisieController
{
...
private HashMap<String,HInputText<Long>> htagLongInputTexts;
public HashMap<String, HInputText<Long>> getHtagLongInputTexts()
{
return htagLongInputTexts;
}
public void setHtagLongInputTexts(HashMap<String, HInputText<Long>> hLongInputTexts)
{
this.htagLongInputTexts = hLongInputTexts;
}
public void addHtagLongInputText(HInputText<Long> hLongInputText)
{
getHtagLongInputTexts().put(hLongInputText.getId(), hLongInputText);
}
public HInputText<Long> getHtagLongInputText(String hLongInputTextId)
{
return(getHtagLongInputTexts().get(hLongInputTextId));
}
#PostConstruct
public void init()
{
setHtagLongInputTexts(new HashMap<String, HInputText<Long>>());
addHtagLongInputText(new HInputText<Long>(Long.class, "HIT_LongTestHIT"));
}
public String doNothing()
{
return null;
}
}
and finally a snippet from my jsf page:
<h:form>
<h:inputText
id = "HIT_LongTestHIT"
value = "#{saisieController.htagLongInputTexts['HIT_LongTestHIT'].value}"
rendered = "#{saisieController.htagLongInputTexts['HIT_LongTestHIT'].rendered}"
required = "#{saisieController.htagLongInputTexts['HIT_LongTestHIT'].required}"
/>
<h:message for = "HIT_LongTestHIT" styleClass = "error-text" />
<h:commandButton value = "submit" action = "#{saisieController.doNothing()}" />
<h:outputText value = "#{saisieController.htagLongInputTexts['HIT_LongTestHIT'].value}" />
</h:form>
Is there some trick to make JSF validation work without having to explicitly define a custom Validator for each h:inputText?
No, that's not possible. Due to type erasure the generic type information is not available during runtime. JSF/EL has no idea that T is actually a Long, let alone that there's actually some T. You really need to explicitly specify a converter which is in this particular example the LongConverter with a converter ID of javax.faces.Long.
<h:inputText ... converter="javax.faces.Long">
or, dynamically so you want
<h:inputText ...>
<f:converter converterId="#{saisieController.htagLongInputTexts['HIT_LongTestHIT'].converterId}" />
</h:inputText>
You can find an overview of all available standard JSF converters in the javax.faces.convert package summary. You can find out their converter IDs by navigating to the Constant field values link on the public constant CONVERTER_ID.
How to populate ddropdown list in a web page in struts2 from database. The items of the drop down will be taken from database.
For creating list in struts2, we can use implement Preparable.
public class CountryListAction extends ActionSupport implements Preparable {
private List<String> country;
getters and setters
public String prepareCountryList() throws Exception{
country=new ArrayList<String>();
country=fill country from database;
}
public String countryList() throws Exception{
return SUCCESS;
}
}
For jsp, use:
<s:select label="Select Country"
name="country"
headerValue="Select Country"
list="%{country}"
/>
Preparable interface will call prepareCountryList method before calling countryList and jsp will be prepopulated. You can populate as many drop down list using single method in struts2.
You need to create a List in you action class with getters and setters and all you have to do is to populate the list inside your Action's execute method.
Action Class
public class MyListAction extends ActionSupport{
private List<String> country;
getters and setters
public String execute() throws Exception{
country=new ArrayList<String>();
countr=fill country from database;
return SUCCESS;
}
}
Now all you need to have a S2 select tag with following entry
<s:select label="Select Country"
name="country"
headerKey="-1" headerValue="Select Country"
list="%{country}"
/>
here list is an Iterable source to populate from. If the list is a Map (key, value), the Map key will become the option 'value' parameter and the Map value will become the option body.
So list=%{country} will be evaluated by OGNL as getCountry() method in your action class and will fetch the required list from value-stack to populate the drop down
You can use the below code:
<s:select name="someName" id="someId"
list="someMap_or_List" onchange="someFunction();"/>
here someMap_or_List is the List or Map that you can use to populate your Drop Down List.
This is a pattern that I would use over and over again if I get it to work. I have an enum name Log.LogKey that I want to user to pick out instances of. So the facelet has this:
<h:form id="testForm" >
<h:selectManyCheckbox value="#{test.selectedKeys}" >
<f:selectItems value="#{test.allKeys}"
var="lk"
itemLabel="#{lk.display}"
itemValue="#{lk}" />
</h:selectManyCheckbox>
<h:commandButton value="Do It" action="#{test.doNothng}" />
</h:form>
The enum has a getter called getDisplay(). The selectItems attribute calls that correctly because that's the string that gets rendered to the user. And the backing bean has this:
public class Test implements Serializable {
private List<Log.LogKey> selectedKeys = null;
public List<Log.LogKey> getAllKeys() {
return Arrays.asList(Log.LogKey.values());
}
public List<Log.LogKey> getSelectedKeys() { return selectedKeys; }
public void setSelectedKeys(List selected) {
System.out.println("getSelecgedKeus() got " + selected.size());
int i = 0;
for (Object obj : selected) {
System.out.println(i++ + " is " + obj.getClass() + ":" + obj);
}
}
public String doNothng() { return null; }
}
So on the form submit, the array setSelectedKeys(selected) gets called with a List of Strings, not a List of Log.LogKey. The reference to #{lk} in the selectItems tag is converting the object to a string. What would be the right way to do this?
You need to specify a converter. JSF EL is not aware about the generic List type because that's lost during runtime. When you do not explicitly specify a converter, JSF will not convert the submitted String values and plain fill the list with them.
In your particular case, you can make use of the JSF builtin EnumConverter, you just have to super() the enum type in the constructor:
package com.example;
import javax.faces.convert.EnumConverter;
import javax.faces.convert.FacesConverter;
#FacesConverter(value="logKeyConverter")
public class LogKeyConverter extends EnumConverter {
public LogKeyConverter() {
super(Log.LogKey.class);
}
}
To use it, just declare it as follows:
<h:selectManyCheckbox value="#{test.selectedKeys}" converter="logKeyConverter">
...
</h:selectManyCheckbox>
See also:
Use enum in h:selectManyCheckbox