I want to pass some IDs in a URL that are later going to be read by a backing bean. For each ID a particular operation will follow.
F.g. URL: localhost:8585/monit/accepted.jsf?acceptMsg=948&acceptMsg=764
The bean:
#ManagedBean
#RequestScoped
public class AcceptedBean {
#EJB
private MessageService messageService;
#ManagedProperty("#{paramValues.acceptMsg}")
private String[] acceptMsg;
public String[] getAcceptMsg() {
return acceptMsg;
}
public List<Message> getMessages() {
return messageService.getAcceptedMessages();
}
public void setAcceptMsg(String[] acceptMsgs) {
this.acceptMsg = acceptMsgs;
if(acceptMsg != null) {
try {
for (String accept: acceptMsg) {
final Message o = messageService.find(Integer.parseInt(accept));
messageService.accept(o);
FacesContext.getCurrentInstance().
addMessage(null,
new FacesMessage(FacesMessage.SEVERITY_INFO,
"Message was accepted:", o.getSubject()));
}
} catch (Exception e) {
FacesContext.getCurrentInstance().
addMessage(null,
new FacesMessage(FacesMessage.SEVERITY_ERROR,
"Error:", e.getMessage()));
}
}
}
}
acceped.xhtml:
....
<f:metadata>
<f:viewParam name="acceptMsg" value="#{acceptedBean.acceptMsg}" />
</f:metadata>
....
When I tried replacing value with binding in the .xhtml I got
Cannot convert javax.faces.component.UIViewParameter#ee2549 of type class javax.faces.component.UIViewParameter to class [Ljava.lang.String;
Any ideas on how to solve the conversion error would be appreciated!
The <f:viewParam> doesn't support multiple parameter values. Remove it. You don't need it in this particular case. You already have a #ManagedProperty("#{paramValues.acceptMsg}") which already sets them.
See also:
f:viewParam with multiple values
Unrelated to the concrete problem, I'd only replace the business job in the setter by a #PostConstruct method. The getters/setters should in principle not be doing anything else than getting or setting a property, that would indicate bad design as they are merely access points and can be invoked multiple times on a per-request basis.
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?
The situation is that I have a ViewScoped page that uses ui:repeat for laying out some components. I also on this page have some h:commandLinks that have action method specified. The action method performs some conditional logic to determine what navigation outcome to return. Upon returning an outcome and including "faces-redirect=true" to perform a redirect, the current view's bean is reinstantiated before the redirect occurs.
Removing the ui:repeat causes the issue to stop, I know longer see the constructor of the bean being invoked. Also, I cannot use h:links in this case, because I want to be able to calculate the outcome at the moment the link is clicked.
Is this a known bug with ui:repeat? I have searched around and could not find why this is happening.
Here is some sample code that I can reproduce this with using Mojarra JSF 2.1.6:
example.xhtml
<h:form>
<h:outputText value="Bean property value: #{exampleBean.property}"/>
<ui:repeat value="#{exampleBean.list}" var="item">
<h:outputText value="#{item}"/>
</ui:repeat>
<br/><h:commandLink value="Action" action="#{exampleBean.someAction()}"/>
<br/><h:commandLink value="Action w/Redirect" action="#{exampleBean.someRedirectAction()}"/>
</h:form>
ExampleBean.java
#ManagedBean(name="exampleBean")
#ViewScoped
public class ExampleBean {
private String property;
private List<String> list;
public ExampleBean() {
System.err.println("Constructor invoked");
list = new ArrayList<String>();
list.add("list item 1");
list.add("list item 2");
}
public String getProperty() {
return property;
}
public void setProperty(String property) {
this.property = property;
}
public List<String> getList() {
return list;
}
public void setList(List<String> list) {
this.list = list;
}
public String someAction() {
System.err.println("someAction() invoked");
if (calculateSomeCondition()) {
return "otherView1";
} else {
return "otherView2";
}
}
public String someRedirectAction() {
System.err.println("someRedirectAction() invoked");
if (calculateSomeCondition()) {
return "otherView1?faces-redirect=true";
} else {
return "otherView2?faces-redirect=true";
}
}
private boolean calculateSomeCondition() {
// some logic to calc true/false
}
}
Note that otherView1.xhtml and otherView2.xhtml are simple files sitting in the same directory as example.xhtml.
The output seen in the log upon loading the page and clicking the "Action" link:
Constructor invoked
someAction() invoked
The output seen in the log upon loading the page and clicking the "Action w/Redirect" link:
Constructor invoked
someRedirectAction() invoked
Constructor invoked
I have uncovered some strange behavior trying to set a value with a view parameter. The code below does NOT work...upon submission of the form with the view parameter, the id value is NOT set. However, if the comments are removed from the getId and setId methods, the id value IS properly set. Is there some issue with JSF EL when accessing generics, or am I doing something terribly wrong? Can anyone explain this behavior?
Interestingly, I receive no exceptions...the value is simply not set. In fact, the setId method in the abstract class is not even called.
public abstract class AbstractBusinessObjectAction<E, ID extends Serializable> {
protected abstract BusinessObjectManager<E, ID> getBusinessObjectManager();
public ID getId() {
ID id = getBusinessObjectManager().getId();
return id;
}
public void setId(final ID id) {
getBusinessObjectManager().setId(id);
}
}
#Named("configuration")
#ConversationScoped
public class ConfigurationAction extends AbstractBusinessObjectAction<ConfigurationParameter, Long> {
#Inject
private ConfigurationParameterManager manager;
protected BusinessObjectManager<ConfigurationParameter, Long> getBusinessObjectManager() {
return manager;
}
// public Long getId() {
// Long id = super.getId();
// return id;
// }
// public void setId(Long id) {
// super.setId(id);
// }
}
Following is an except from the .xhtml:
<f:metadata>
<f:viewParam id="entityIdParam" name="entityId" value="#{configuration.id}" />
</f:metadata>
Adding a converter solved the problem.
<f:viewParam id="entityIdParam" name="entityId" value="#{configuration.id}" >
<f:converter converterId="javax.faces.Long" />
</f:viewParam>
To the best of my knowledge, this is the reason:
Because the id property is of type ID extends Serializable in the abstract class, JSF tries to install a non-existant Serializable converter. The reason the code works when I uncomment the methods indicated, is that JSF properly detects that it needs a javax.faces.Long converter based on the explicit declaration of Long on the property methods for the "id" property.
Thanks to #Damian for the tip!
<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()
}