I have a form where I have a button, when I click on that button, a Dialog opens to select a value and then my dialog close.
But, doing this my Bean (#ViewScoped) is called again (re-instantiated) and I lose my information that I previous entered on page.
I searched and I found a similar behaviour (http://forum.primefaces.org/viewtopic.php?f=3&t=38235) but no answer.
I initialize my bean with constructor but I don't know if this is the problem.
#ManagedBean(name="exameBean")
#ViewScoped
public class ExameBean implements Serializable {
public ExameBean(){
exame = new Exame();
exames = new ArrayList<Exame>();
}
public void selecionaPaciente() {
RequestContext.getCurrentInstance().openDialog("/pages/SelecionaPaciente");
}
public void retornaPaciente(Paciente paciente) {
RequestContext.getCurrentInstance().closeDialog(paciente);
}
public void pacienteSelecionado(SelectEvent event) {
exame.setPaciente((Paciente) event.getObject());
}
}
SelecionaPaciente.xhtml
<p:column headerText="Selecionar">
<p:commandButton icon="ui-icon-search" actionListener="#{exameBean.retornaPaciente(lista)}" />
</p:column>
May you need to use #SessionScoped, when you use #ViewScoped and show a dialog, you lose the actual instance of you MB. I had the same problem today and i solved using a init method annoted with #PostConstruct (To Load all my data ) and annotate with #SessionScoped to use and manage my data loaded from init().
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.
My JavaScript:
function setJson(value) {
document.getElementById("json").value = value;
}
My XHTML:
<h:inputHidden id="json" value="#{indexManaged.json}" valueChangeListener="#{indexManaged.goTo('datatable')}" />
My ManagedBean:
#ManagedBean
#ViewScoped
public class IndexManaged implements Serializable {
private String json;
public String getJson() { return json; }
public void setJson(String json) { this.json = json; }
public String goTo(String page) {
Flash flash = FacesContext.getCurrentInstance().getExternalContext().getFlash();
flash.put("json", json);
return page + "?faces-redirect=true";
}
}
The scenario:
I have a Java Applet that fires the function setJson(value). But when the applet sets a new value to my inputHidden, isn't the valueChangeListener suposed to fire my ManagedBean method? What am I doing wrong?
The valueChangeListener isn't a client side listener. It's a server side listener which runs when the form is submitted. So, you need to submit the form as well.
Wrap it in a form
<h:form id="form">
and submit it as follows
document.getElementById("form").submit();
Don't forget to fix the client ID of the hidden input accordingly:
document.getElementById("form:json").value = value;
See also:
When to use valueChangeListener or f:ajax listener?
Unrelated to the concrete problem, there are cleaner ways to achieve this. Have a look at PrimeFaces <p:remoteCommand> and OmniFaces <o:commandScript>.
Not sure about the <h:inputHidden
But you can use the following thing:
<h:inputText id="json" value="#{indexManaged.json}" style="display:none">
<f:ajax listener="#{myBean.myListener}"/>
</h:inputText>
and trigger it with jquery like this:
$("#json").change();// instead of `json` you might be needed to provide
//full id like this, $("#myform\\:json").change()
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.
I have problems with SelectOneMenu. I write this:
<h:selectOneMenu id="listaEstados"
styleClass="comboboxStyle"
value="#{detalleSistemaBean.sistema.indEstado}"
immediate="true">
<f:selectItems value="#{detalleSistemaBean.indEstados}" />
</h:selectOneMenu>
<h:commandButton id ="SubmitModificar"
value="Modificar"
styleClass="botonPeque"
action="#{detalleSistemaBean.modificaSistema}">
</h:commandButton>
But when I choose one value from the list "indEstados" and I submit the form, the bean "sistema.indEstado" doesn't change. I have seen that the bean property changes just before the method modificaSistema, but inside this method (where I have a database connection and a sql sentence), "sistema.indEstado" returns to its original value. Why this happens? I have tried to save the value using valueChangeListener, and that works, but I guess that is not a neat solution.
That can happen when you're doing data loading inside the getter method instead of inside the (post)constructor of the bean class.
Fix your managed bean code to not do anything else inside getter methods than just returning the property.
I.e. do not do
public Sistema getSistema() {
return sistemaService.find(someSistemaId);
}
but rather do
private Sistema sistema;
#PostConstruct
public void init() {
sistema = sistemaService.find(someSistemaId);
}
public Sistema getSistema() {
return sistema;
}
Can you try without setting
immediate="true"
JSF commandButton with immediate="true"
I cant seem to get the view scoped managed bean to work with setPropertyActionListener:
<h:commandButton value="Edit" action="edit-company.xhtml">
<f:setPropertyActionListener target="#{companyHolder.item}" value="#{company}"/>
</h:commandButton>
This works fine if companyHolder is session or request scoped but doesnt work if its view scoped. Is this normal?
A brand new view scoped bean is been created when a new view is created. The target view holds a different instance of the view scoped bean than where the property is been set by the action method on the initial view with the form.
This is at first sight indeed unintuitive, but that's how the view scope works. A view scoped bean lives as long as the view lives. It makes sense after all.
Your best bet is using <f:param> instead of <f:setPropertyActionListener> and let the target view set it by <f:viewParam>.
E.g.
<h:commandButton value="Edit" action="edit-company.xhtml">
<f:param name="companyId" value="#{company.id}"/>
</h:commandButton>
with
<f:metadata>
<f:viewParam name="companyId" value="#{bean.company}" required="true" />
</f:metadata>
and
#ManagedBean
#ViewScoped
public class Bean {
private Company company;
// ...
}
and
#FacesConverter(forClass=Company.class)
public class CompanyConverter implements Converter {
#Override
public void getAsObject(FacesContext context, UIComponent component, Object value) throws ConverterException {
try {
return companyService.find(Long.valueOf(value));
} catch (Exception e) {
throw new ConverterException(new FacesMessage(
String.format("Cannot convert %s to Company", value)), e);
}
}
// ...
}
As a completely different alternative, you can also just navigate back to the same view by returning void or null and render the include conditionally.
<ui:include src="#{bean.editmode ? 'edit' : 'view'}.xhtml" />
This however doesn't work if you require to support GET instead of POST (for which you would need to replace <h:commandButton> by <h:button> by the way).