JSF 2.0 view parameters to pass objects - jsf-2

I am trying to pass an object from one page to another page where each page is in a different view.
On the first page I have an input text, where myBean is a ViewScoped Bean and name is an object.
<h:inputText value="#{myBean.name}"/>
The second page contains,
<f:metadata>
<f:viewParam name="userId" value="#{myBean.name}"/>
</f:metadata>
I get the error Conversion Error setting value mypackage.myBean#257100b' for 'null Converter'.
Can we pass objects other than String values to view parameters?

Yes, you can. You just have to supply a converter which converts between the string representation of the object type behind #{myBean.name} and the real object. The string representation is usually the unique technical/natural identifier of the object in question. For example, the table's PK. It has got to be a string, simply because HTTP request parameters can be strings only. You can't pass complex Java objects around in URLs. URLs are just strings.
Here's a kickoff example:
E.g. the following in the initial view:
<h:link value="Edit" outcome="edit">
<f:param name="id" value="#{personViewer.person.id}" />
</h:link>
(which generates an Edit)
and the following in the linked view:
<f:metadata>
<f:viewParam name="id" value="#{personEditor.person}"
converter="#{personConverter}" converterMessage="Bad request. Unknown person."
required="true" requiredMessage="Bad request. Please use a link from within the system."
/>
</f:metadata>
<h:messages />
with this converter
#ManagedBean
#RequestScoped
public class PersonConverter implements Converter {
#EJB
private PersonService personService;
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
return String.valueOf(((Person) value).getId());
}
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
return personService.find(Long.valueOf(value));
}
}
(oversimplified; all null/number checks omitted, but you got the idea)
See also:
Communication in JSF 2.0 - Converting and validating GET request parameters
Update as per the comments, you actually want to fire a POST request. You don't need a <f:viewParam> at all. It's for setting/validating/converting GET request parameters only. Attempting to serialize the whole object makes also no utter sense. Just use #ManagedProperty.
E.g.
#ManagedBean
#ViewScoped
public class PersonEditor implements Serializable {
#ManagedProperty("#{personViewer.person}")
private Person person;
// ...
}
It's only not bookmarkable anymore and not SEO-friendly (but that's the nature of POST, you're probably already for long aware of this). Note that the #{personViewer} bean must by itself also be #ViewScoped (and thus not #ReqestScoped). You also need to make sure that you don't navigate back with a redirect, but just a forward.

Related

#FacesConverter without converter-id and forClass

What should work properly if I do not specify the converter-id and(or) forClass in determining the #FacesConverter annotation?
For example:
#FacesConverter
public class SplitConverter implements Converter{
#Override
public Object getAsObject(FacesContext context, UIComponent component,
String value) {
//...
}
#Override
public String getAsString(FacesContext context, UIComponent component,
Object value) {
//...
}
After registred in faces-config.xml and use that on .xhtml page:
<h:inputText id="badges" value="#{article.badges}"
required="true">
<f:converter converterId="com.katcgr.SplitConverter" />
</h:inputText>
And all is work. The documentation say that
if converter-id is the empty string, Application.addConverter(java.lang.Class,java.lang.String) is called, passing the converter-for-class as the first argument and the derived converter-class as the second argument.
But why everything works fine even if I not specified the forClass ?
After registred in faces-config.xml
Registration via #FacesConverter and <converter> in faces-config.xml are mutually exclusive whereby the XML registration overrides any annotation registration. So, when your converter is referenced via the converter ID as registered in XML, then the converter instance behaves basically exactly as if it had no annotations. If you remove the XML registration, then you should have retrieved the below exception on specified converterId:
javax.faces.FacesException: Expression Error: Named Object: com.katcgr.SplitConverter not found.
at com.sun.faces.application.ApplicationImpl.createConverter(ApplicationImpl.java:1339)
at javax.faces.application.ApplicationWrapper.createConverter(ApplicationWrapper.java:393)
at com.sun.faces.facelets.tag.jsf.ConverterTagHandlerDelegateImpl.createConverter(ConverterTagHandlerDelegateImpl.java:158)
...
This would basically only work if you had a
#FacesConverter("com.katcgr.SplitConverter")
If you however remove both the XML configuration and the <f:converter>, then it will "work" because the #FacesConverter without an explicit forClass will be automatically invoked for every bean property which is an instance of java.lang.Object (basically, everything), which does not have a more specific converter already registered. In other words, your converter will behave like:
#FacesConverter(forClass=Object.class)
It hopefully doesn't need an elaborate explanation that this is a terribly bad idea. It will also confuse PrimeFaces, because it will then internally initialize and use it as default converter for String class.

Which one takes higher precendence between value and forClass in FacesConverter annotation when defining custom converter?

I have the following as annotation for the custom converter
#FacesConverter(value="capsConverter", forClass=String.class)
public class CapsConverter implements Converter{
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
return value.toUpperCase();
}
#Override
public String getAsString(FacesContext context, UIComponent component,Object value) {
if(value!=null && value instanceof String){
String s = (String)value;
s = WordUtils.capitalize(s.toLowerCase());
return s;
}
return null;
}
}
Issue is, the converter sometimes gets called even when I didnt explicitly call it in my page, does the forClass have anything to do with the interference for my inputTexts, should I be using value only and remove forClass=String.class ?
Any shed of light is highly appreciated.
It are actually two distinct ways of registering a converter. Both are mutually exclusive. They have no influence on each other and you can omit one or the other.
The forClass attribute will register the converter in the JSF application to kick in during processing and rendering of every model value of the specified type. In other words, with forClass=String.class, the converter will be implicitly invoked for every String typed input and output, unless the associated input/output component has already an explicit converter declared.
Generally you use forClass only for custom/complex types, not for standard types such as String, Long, BigDecimal, etc for which JSF has already builtin implicit converters.
So you should indeed remove it and rely on converter ID only.
#FacesConverter("capsConverter")
<h:someComponent ... converter="capsConverter" />
An example of correct appliance of forClass would be the following converter between a Person entity and a String representing its ID
#FacesConverter(forClass=Person.class)
on something like
<f:viewParam name="id" value="#{bean.person}" />
which converts an incoming request parameter representing an entity ID like below
/edit-person.xhtml?id=42
to a fullworthy entity property in bean (without needing any additional logic in bean)
private Person person;

Use ManagedBean in FacesConverter

I want to use ManagedBean in my Converter. The ManagedBean is responsible for getting data from database. In Converter I want to convert string into object which must be get from database.
This is my Converter
#FacesConverter(forClass=Gallery.class, value="galleryConverter")
public class GalleryConverter implements Converter {
// of course this one is null
#ManagedProperty(value="#{galleryContainer}")
private GalleryContainer galleryContainer;
#Override
public Object getAsObject(FacesContext context, UIComponent component, String galleryId) {
return galleryContainer.findGallery(galleryId);
...
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object gallery) {
...
}
}
I know that galleryContainer will be null and if I want to inject ManagedBean into Converter I can mark it as ManagedBean too. The problem is that I want to do it in beautiful way, I don't want to look for some 'strange solution'. Maybe the problem is in my application? Maybe there is some other good solution to create object which must get data from database and used in converter? I want also to mention that I will prefer to use DependencyInjection instead of creating new object using new statement (it is easier to test and maintain). Any suggestions?
Instead of using #FacesConverter you should use #ManagedBean, because currently faces converter isn't a valid injection target. Nonetheless, you can choose your converter to be a managed bean, thus refer to it in your view as converter="#{yourConverter}" (by managed bean name) instead of converter="yourConverter" (by converter id).
Basic usage example:
#ManagedBean
#RequestScoped
public class YourConverter implements Converter {
#ManagedProperty...
...
//implementation of converter methods
}
Of course, reading BalusC's invaluable Communication in JSF 2.0 will shed some light on this question as well.
It is also worth mentioning that the scope of your converter bean may be changed to, for example, application or session, if it is not supposed to hold any state.

Passing a JSF2 managed pojo bean into EJB or putting what is required into a transfer object

Currently i am calling EJB 3 Session Beans from JSF 2. However, i am not sure if i should be passing JSF managed beans into EJB?
Assuming that whatever on the form (and thus the backing bean) was everything i needed to persist through the EJB layer, should i clone out all the attributes by hand into a transfer object, or is there a better way of doing this?
The backing bean though POJO is heavily annotated with JSF lifecycle tags (Such as #ManagedBean) and resides in the Web project while the EJBs reside separately in the EJB project.
It sounds like as if you've tight-coupled the model with the controller like as shown in most basic JSF tutorials. You should decouple the model from the controller into its own class. As you're using EJBs, the chance is big that you're also using JPA (how else would EJBs be really useful for persistence?), you can just use the existing JPA #Entity class as model.
E.g.
#Entity
public class Product {
#Id
private Long id;
private String name;
private String description;
private Category category;
// ...
}
with
#ManagedBean
#ViewScoped
public class ProductController {
private Product product;
#EJB
private ProductService service;
public void save() {
service.save(product);
}
// ...
}
which is to be used as
<h:form>
<h:inputText value="#{productController.product.name}" />
<h:inputTextarea value="#{productController.product.description}" />
<h:selectOneMenu value="#{productController.product.category}">
<f:selectItems value="#{applicationData.categories}" />
</h:selectOneMenu>
<h:commandButton value="Save" action="#{productController.save}" />
</h:form>
I was trying to do the same with CDI and the main diffrence (excluding using #Named instead of #ManagedBean) was that I had to initialize my transport object in the Controller class.
So instead of:
private Product product;
I had to use:
private Product product = new Product();
Maybe it will help someone :)

Struts2 xwork Type Conversion - Multiple different parameters into one Object

Is it possible to convert different paremeters into one object on your action?
Say from my html form, I pass in variables, "firstname", "lastname". Can I write a type converter that will convert those into a Person object on my action?
I didn't see any examples of this, and I don't see API. I don't see how I can access the value stack in the StrutsTypeConverter to get to the other variables.
Thanks!
Example :
// JavaBeans
public class Person {
#Getter #Setter private String firstname;
#Getter #Setter private String lastname;
}
// Action
#Setter private Person person;
// form
<s:form>
<s:textfield name="person.firstname" />
<s:textfield name="person.lastname" />
</s:form>
Similar example : vaannila : Domain Object as JavaBeans Property
I am agree what lschin said.you can use the build in OGNL and value stack combination to achieve what you want.still if u need some specific type conversion machanism here are the details from the Struts2 docs
Struts2 Type Conversion

Resources