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).