I wonder if i can replace "success" with a Constants value from a JSF library.
Backing Bean Method:
#Override
public String save() {
LOG.info("Called");
return "success";
}
For your issue, you'll find Omnifaces' <o:importConstants /> so useful (that's what I use in my own projects). That way you can import your constants file in your JSF page (I use my master page template for that).
<o:importConstants
type="com.mycompany.NavigationResults" />
This way you can access your NavigationResults values both from Java code and JSF tags (EL scope).
public abstract class NavigationResults {
public static final String SUCCESS = "success";
public static final String HOME = "home";
}
Use it in your managed beans:
public String save() {
LOG.info("Called");
return NavigationResults.SUCCESS;
}
In your buttons or links:
<h:button value="Go home" outcome="#{NavigationResults.HOME}" />
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.
jsp content:
<s:checkboxlist list="list" name="values"/>
action content:
public List<Foo> getList() {
return list;
}
public String[] getValues() {
return values;
}
public void setValues(String[] values) {
this.values = values;
}
class Foo:
private String code;
public String getCode() {
return code;
}
public String toString() {
return code;
}
When I put breakpoint at getValues() method, I clearly see it's being called with some values there. But that values don't appear to be selected on a page.
What am I missing here?
I found a solution. I've added
<s:checkboxlist list="list" name="values" listKey="code" listValue="code />
to jsp and it all started working after that. It generates the same html, but it seems that despite rendering correctly, that properties are required by struts to check what values should be set. And there is no emphasis on that in struts docs.
EDIT:
It seems that only this is actually requited for this thing to work:
<s:checkboxlist list="list" name="values" listKey="code"/>
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?
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
<p:editor value="#{editorBean.value}" widgetVar="editor" width="686"
height="390" language="en" align="center">
</p:editor>
Following is my rich-text editor bean picked up from primefaces
#ManagedBean(name = "editorBean")
#SessionScoped
public class EditorBean {
private static final String MANAGED_BEAN_NAME = "editorBean";
private String value;
public static EditorBean getCurrentInstance() {
return (EditorBean) FacesContext.getCurrentInstance()
.getExternalContext().getRequestMap().get(MANAGED_BEAN_NAME);
}
public void setValue(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
Apart from this I have another bean say A. I have a method inside A that populates a HTML table. What I want is when the user opens the editor, it should be pre-populated with that HTML table data and of course the changes should get reflected into (String: value). Therefore, you can say that I am trying to tie up both the values together. I think it needs to be done with DI but somehow its not working. If someone can guide or quote an example, it would be really helpful.
One way to do it is rewrite your getValue() method to pick up the value from bean A.
And yes, the reference to your A bean should come from DI:
//injecting a reference to A
#ManagedPropery(value="#{A}") //or whatever is the name of your bean
private A beanA;
public void setBeanA(A beanA) {
this.beanA = beanA;
}
Or, with CDI, just:
#Inject private A beanA
Finally, your getValue method
public String getValue() {
return beanA.getValue()
}