f:validateWholeBean in JSF 2.3 - jsf

I want to implement f:validateWholeBean with JSF 2.3.
I tried to implement this example with Mojarra 2.3.0-m05 and Tomcat 8:
<h:form>
<h:panelGroup>
<h:inputSecret id="passwd" value="#{bean.dataList['passwd']}">
<f:ajax event="blur" render="passwdvalidator" />
</h:inputSecret>
<h:message id="passwdvalidator" for="passwd" />
</h:panelGroup>
<h:panelGroup>Confirm Password</h:panelGroup>
<h:panelGroup>
<h:inputSecret id="confurmpasswd" value="#{bean.dataList['passwd']}">
<f:ajax event="blur" render="confurmpasswdvalidator" />
</h:inputSecret>
<h:message id="confurmpasswdvalidator" for="confurmpasswd" />
</h:panelGroup>
<h:commandButton action="#{bean.submit}">
<f:ajax render="#form" execute="#form"></f:ajax>
</h:commandButton>
<f:validateWholeBean value="#{contactBean}" validationGroups="validateBean.ContactGroup" />
</h:form>
Custom Validator
#Named
#ViewScoped
public class NewAccountValidator implements Validator, Serializable
{
#Override
public void validate(FacesContext fc, UIComponent uic, Object o) throws ValidatorException
{
// not used
}
public void validatePasswords(FacesContext context, UIComponent component, Object value)
{
String l;
String s = value.toString().trim();
if (s != null)
{
// compare passwords
}
else
{
throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_INFO,
s.isEmpty() ? " This field cannot be empty!" : " '" + s + "' is not a number!", null));
}
}
}
What is the proper way to implement solution with f:validateWholeBean and custom JSF validator?

You shouldn't implement a "standard" validator, but a ConstraintValidator.
You can find an example on Arjan Tijms Weblog:
<h:form>
<h:inputText value="#{indexBean.foo}">
<f:validateBean validationGroups="javax.validation.groups.Default,java.util.RandomAccess"/>
</h:inputText>
<h:inputText value="#{indexBean.bar}">
<f:validateBean validationGroups="javax.validation.groups.Default,java.util.RandomAccess"/>
</h:inputText>
<f:validateWholeBean value="#{indexBean}" validationGroups="java.util.RandomAccess"/>
<h:commandButton value="submit"/>
</h:form>
with backing bean:
#Named
#RequestScoped
#ValidIndexBean(groups = java.util.RandomAccess.class)
public class IndexBean implements ConstraintValidator<ValidIndexBean, IndexBean> {
#Constraint(validatedBy = IndexBean.class)
#Documented
#Target(TYPE)
#Retention(RUNTIME)
public #interface ValidIndexBean {
String message() default "Invalid Bean";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
#Inject // #EJB
private PersistenceService service;
#NotNull
private String foo;
#NotNull
private String bar;
#Override
public void initialize(ValidIndexBean constraintAnnotation) {
//
}
#Override
public boolean isValid(IndexBean other, ConstraintValidatorContext context) {
// return other.getFoo().equals(other.getBar());
return service.query("select count(p) from Person p where p.foo like ?1 and p.bar like ?2", other.getFoo(), other.getBar()) == 0;
}
...
}
answer for comments:
this is a regular bean, so yes, it can be #ViewScoped.
then you should create multiple validators: it's a bad practice to make a single validator perform multiple logics.
unrelated:
As I can see from the code you posted, you are misunderstanding the use of "classic" validator, making it a ManagedBean (CDI flavoured), but this is not the "plain" use of JSF Validators/Converters.
I suppose you are not using a validator, but a validation method instead.
A "classic" Validator should look like (see here):
#FacesValidator("usernameValidator")
public class UsernameValidator implements Validator, Serializable
{
#Override
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException
{
// you should use THIS method to validate a single Component's Value
if(query("select count(*) from user where username = '?'", String.valueOf(value)) > 0)
{
throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "invalid username"));
}
}
}
and should be used like:
<h:inputText value="#{someBean.username}" validator="usernameValidator" />
so:
"classic" Faces Validators are tought for validating one component's value
they shouldn't be #ManagedBean or #Named
they should be referenced by name (validator="usernameValidator" without using EL expressions validator="#{usernameValidator}")
However, it's a best practice for Validators/Converters to be "specialized": they should perform a single validation logic.
If you need to validate a component value, i.e. a Date, that must be non-null and greater than 01/01/1970, you'll need two specialized validators.

Related

Automatically populate value of inputText [duplicate]

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.

JSF bean creates more than once [duplicate]

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.

JSF pass parameters between pages without backend bean [duplicate]

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.

How to pass object between pages in Primefaces [duplicate]

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.

JSF events not propagating from composite component with backing component

all
I've been working on a composite component for a date range. Essentially, my composite component uses two Richfaces 4.3 calendar components underneath to capture the individual date values, generate a date range (a pair of LocalDate objects). I found this blog entry which was the basis for my custom component that combines the two submitted values on the calendar into one pair value.
Everything seems to work fine and the values are getting updated. However, I'm trying to figure out how to propagate the change event to the using xhtml page for a partial render of another component, and I've been unsuccessful. I've tried everything I could think of, but I think I'm missing something.
The page:
<rich:panel>
<f:facet name="header">Calendar Date Range Component</f:facet>
<h:outputText id="out1" binding="#{calendarDateRangeTestBean.component1}"
value="#{calendarDateRangeTestBean.dateRange}" converter="localDatePairConverter" /><br/>
<h:outputText id="out2" value="#{calendarDateRangeTestBean.dateRange}" converter="localDatePairConverter" /><b>NOT WORKING</b>
<yxp:calendarDateRange id="calendarDateRange" value="#{calendarDateRangeTestBean.dateRange}"
dataModel="#{calendarDateRangeTestBean}"
valueChangeListener="#{calendarDateRangeTestBean.processValueChange}">
<f:ajax execute="#all" listener="#{calendarDateRangeTestBean.processBehaviorEvent}"/>
<!-- This doesn't seem to work???? -->
<f:ajax execute="#all" render="out2" />
</yxp:calendarDateRange>
</rich:panel>
My test managed bean:
#ViewScoped
#ManagedBean
public class CalendarDateRangeTestBean extends AbstractCalendarDateRangeDataModel implements
ValueChangeListener, Serializable {
private static Logger logger = LoggerFactory.getLogger(CalendarDateRangeTestBean.class);
private Pair<LocalDate> dateRange = Pair.of(LocalDate.now(), LocalDate.now().plusDays(7));
private UIComponent component1;
public UIComponent getComponent1() {
return component1;
}
public LocalDateRange getDateRange() {
return dateRange;
}
public void processBehaviorEvent(final javax.faces.event.AjaxBehaviorEvent event) {
logger.info("processing event " + event + ": " + event.getBehavior());
final FacesContext context = FacesContext.getCurrentInstance();
logger.info("Setting render to " + component1.getClientId(context));
// This seems to cause a rerender of the first component
context.getPartialViewContext().getRenderIds().add(component1.getClientId(context));
}
#Override
public void processValueChange(final ValueChangeEvent event) throws AbortProcessingException {
logger.info(this.toString() + ": processing value change event " + event + ": ["
+ event.getOldValue() + ":" + event.getNewValue() + "]");
}
public void setComponent1(final UIComponent component1) {
this.component1 = component1;
}
public void setDateRange(final Pair<LocalDate> dateRange) {
logger.info("Setting date range to " + dateRange);
this.dateRange = dateRange;
}
}
My composite component:
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:a4j="http://richfaces.org/a4j"
xmlns:rich="http://richfaces.org/rich"
xmlns:composite="http://java.sun.com/jsf/composite">
<!-- Methods exposed on rich:component are available in the __proto__ object. -->
<composite:interface componentType="com.yieldex.platform.ui.CalendarDateRange">
<composite:attribute name="value" required="true" type="demo.Pair"/>
<composite:attribute name="dataModel" required="false" type="demo.Pair" />
<composite:clientBehavior name="change" event="change" targets="startCalendar endCalendar" default="true"/>
</composite:interface>
<composite:implementation>
<h:outputStylesheet library="yieldex/platform" name="css/yieldex-platform.css" target="head" />
<div id="#{cc.clientId}" class="yxp-calendar-date-range">
<rich:calendar id="startCalendar"
binding="#{cc.startCalendar}"
styleClass="yxp-start-date-range"
converter="localDateConverter" mode="ajax"
dataModel="#{not empty cc.attrs.dataModel ? cc.attrs.dataModel.startCalendarDataModel : standardCalendarDateRangeDataModel.startCalendarDataModel}"
monthLabels="#{dateRangeMessages.monthNames}"
weekDayLabelsShort="#{dateRangeMessages.weeksShort}"
monthLabelsShort="#{dateRangeMessages.monthNames}" popup="false"
showInput="false" showFooter="false" showWeeksBar="false"
showWeekDaysBar="true" showApplyButton="false"
buttonIcon="#{resource['yieldex/platform:img/1x1-transparent.png']}"
buttonDisabledIcon="#{resource['yieldex/platform:img/1x1-transparent.png']}">
<f:facet name="weekDays"></f:facet>
<f:ajax immediate="true" execute="#all" render="#this endCalendar"/>
</rich:calendar>
<rich:calendar id="endCalendar"
binding="#{cc.endCalendar}"
styleClass="yxp-end-date-range"
converter="localDateConverter" mode="ajax"
dataModel="#{not empty cc.attrs.dataModel ? cc.attrs.dataModel.endCalendarDataModel : standardCalendarDateRangeDataModel.endCalendarDataModel}"
monthLabels="#{dateRangeMessages.monthNames}"
weekDayLabelsShort="#{dateRangeMessages.weeksShort}"
monthLabelsShort="#{dateRangeMessages.monthNames}" popup="false"
showInput="false" showFooter="false" showWeeksBar="false"
showWeekDaysBar="true" showApplyButton="false"
buttonIcon="#{resource['yieldex/platform:img/1x1-transparent.png']}"
buttonDisabledIcon="#{resource['yieldex/platform:img/1x1-transparent.png']}">
<f:facet name="weekDays"></f:facet>
<f:ajax immediate="true" execute="#all" render="startCalendar #this"/>
</rich:calendar>
</div>
</composite:implementation>
</ui:composition>
My backing component:
#FacesComponent("com.yieldex.platform.ui.CalendarDateRange")
public class YXCalendarDateRange extends UIInput implements NamingContainer {
private UICalendar startCalendarComponent;
private UICalendar endCalendarComponent;
#Override
public void encodeBegin(final FacesContext context) throws IOException {
final Pair<LocalDate> value = (Pair<LocalDate>) this.getValue();
if (value == null) {
startCalendarComponent.setValue(null);
endCalendarComponent.setValue(null);
} else {
startCalendarComponent.setValue(value.getStart());
endCalendarComponent.setValue(value.getEnd());
}
super.encodeBegin(context);
}
#Override
protected Object getConvertedValue(final FacesContext context, final Object submittedValue) {
final LocalDate startDate = (LocalDate) startCalendarComponent.getConverter().getAsObject(context,
startCalendarComponent, (String) this.startCalendarComponent.getSubmittedValue());
final LocalDate endDate = (LocalDate) endCalendarComponent.getConverter().getAsObject(context,
endCalendarComponent, (String) this.endCalendarComponent.getSubmittedValue());
if (startDate == null || endDate == null) {
return null;
} else {
if (startDate.isAfter(endDate)) {
final FacesMessage message = new FacesMessage();
message.setSeverity(FacesMessage.SEVERITY_ERROR);
message.setSummary("start date cannot be after end date");
message.setDetail("start date cannot be after end date");
throw new ConverterException(message);
}
return Pair.of(startDate, endDate);
}
}
public UICalendar getEndCalendar() {
return this.endCalendarComponent;
}
#Override
public String getFamily() {
return UINamingContainer.COMPONENT_FAMILY;
}
public UICalendar getStartCalendar() {
return this.startCalendarComponent;
}
#Override
public Object getSubmittedValue() {
return this;
}
public void setEndCalendar(final UICalendar endCalendarComponent) {
this.endCalendarComponent = endCalendarComponent;
}
public void setStartCalendar(final UICalendar startCalendarComponent) {
this.startCalendarComponent = startCalendarComponent;
}
}
What I see is that the valueChangedEvent is coming though. I also see my processBehaviorEvent being called, and the first outputText being rerendered as I'm calling that programmatically. But the second one doesn't seem to get rerendered. I am trying to figure out if this is a bug in Mojarra 2.1.25 or is there something fundamentally wrong with my approach. Any suggestions would be greatly appreciated.
Any client ID in <f:ajax render> is evaluated relative to the parent naming container of the component it has been attached to. In this construct, the <f:ajax> ends up being attached inside the composite component, which is by itself a naming container. However, there's no component with ID out2 inside the composite, which is the problem.
To solve it, specify the absolute client ID. For example, when it's inside a <h:form id="formId"> element:
<f:ajax execute="#all" render=":formId:out2" />
If it's more complicated, binding the component to the view and refer to its client ID dynamically:
<h:outputText id="out2" binding="#{out2}" ... />
...
<f:ajax execute="#all" render=":#{out2.clientId}" />
See also:
How to find out client ID of component for ajax update/render? Cannot find component with expression "foo" referenced from "bar"

Resources