I've been working with converters in my PrimeFaces SelectOneMenu objects. They work fine if I only tell which class the converter is referring to:
#FacesConverter(forClass = DescriptorVO.class)
public class DescriptorVOConverter implements Converter { ... }
This way, I don't have to explicitly tell the JSF component which converter should be used when it is populated with objects of class DescriptorVO.
However, I made a page that used a p:SelectManyCheckbox and I couldn't for the life of me know why it wasn't calling my converter. Then I gave it a name, like so:
#FacesConverter(forClass = RoleVO.class, value = "roleVOConverter")
public class RoleVOConverter implements Converter { ... }
and passed it as one of the component's properties
<p:selectManyCheckbox id="cbx_roles" required="true" converter="roleVOConverter"
requiredMessage="At least one role must be selected."
value="#{userView.selectedRoles}" layout="responsive">
<f:selectItems value="#{userView.roles}" var="role"
itemLabel="#{role.title}" itemValue="#{role}" />
</p:selectManyCheckbox>
and voi la, it started calling the converter correctly. This raised a question to me regarding when I should name my converters (through the value attribute) and when telling them which class the converter should be used with (with the forClass attribute) is enough. I never had to namy any converters when working with PrimeFaces, only for this particular SelectManyCheckbox component. Do different components have different necessities regarding converters or did I just get the concept of converters wrong?
That can happen when the value of the UISelectMany component references a generic java.util.Collection type like List<Role> instead of an array like Role[]. This is specified in javadoc of UISelectMany (emphasis mine):
Obtain the Converter using the following algorithm:
If the component has an attached Converter, use it.
If not, look for a ValueExpression for value (if any). The ValueExpression must point to something that is:
An array of primitives (such as int[]). Look up the registered by-class Converter for this primitive type.
An array of objects (such as Integer[] or String[]). Look up the registered by-class Converter for the underlying element type.
A java.util.Collection. Do not convert the values.
If for any reason a Converter cannot be found, assume the type to be a String array.
The reason is, the generic type <Role> is lost during runtime and not directly determinable. It works when you're using MyFaces instead of Mojarra as it will in case of a java.util.Collection inspect the actual type by manually iterating over <f:selectItem(s)> and determine the Converter based on first non-null value.
I created spec issue 1422 on this to get this in JSF spec and improve Mojarra too.
See also:
UISelectMany on a List<T> causes java.lang.ClassCastException: java.lang.String cannot be cast to T
Why does JSF put String values in a Map<..., Integer>? And how to work around it?
Use enum in h:selectManyCheckbox
Related
I have created a custom component. I add a dynamic input text box to it (from the encode function).
The component is correctly is rendered as HTML.
But I want to bind the value of the text box to some property on the Managed Bean. So some other developer can use the component on his jsp with his managed bean.
I want to know, what should I do, so that the value entered in the text box (which my component dynamically creates) is set to the some Managed bean property.
You need to ensure that your custom component class extends UIInput and that you're in the encodeEnd() method of your renderer writing the component's client ID as name attribute of the HTML input element. Then you can in the overriden decode() method of your renderer just grab the submitted value from the request parameter map with the component's client ID as parameter name and set it as UIInput#setSubmittedValue() and let JSF do the remnant of the job of converting, validating and updating the model value.
#Override
public void decode(FacesContext context, UIComponent component) {
// Do if necessary first validation on disabled="true" or readonly="true", if any.
// Then just get the submitted value by client ID as name.
String clientId = component.getClientId(context);
String submittedValue = context.getExternalContext().getRequestParameterMap().get(clientId);
((UIInput) component).setSubmittedValue(submittedValue);
}
Unrelated to the concrete problem, are you aware of the new composite component support in JSP's successor Facelets? I have the impression that you don't necessarily need a custom component for this purpose. Or are you really restricted to using the legacy JSP as view technology in spite of that you're already on JSF 2.x? See also When to use <ui:include>, tag files, composite components and/or custom components?
Well, the problem is solved.
In the encodeEnd() method I added the element as
HtmlInputHidden hidden = new HtmlInputHidden();
hidden.setParent(this);
hidden.setId("someId");
ValueExpression ve = getValueExpression("value");
hidden.setValueExpression("value", ve);
hidden.encodeBegin(context);
hidden.encodeEnd(context);
This seems to have some problem.
Then I changed this to ...
HtmlInputHidden hidden = new HtmlInputHidden();
hidden.setId("someId");
ValueExpression ve = getValueExpression("value");
hidden.setValueExpression("value", ve);
this.getChildren().add(hidden);
hidden.encodeBegin(context);
hidden.encodeEnd(context);
The use of this.getChildren().add(); solved my problem
P.S. Obviously before adding the element, it needs to be checked if the element is already present.
Seems basic enough...
I have a custom JSF component and its associated renderer. The renderer does the decode and encodeEnd.
In decode i successfully retrie the submitted value via component.setSubmittedValue(ctx.getExternalContext().getRequestParameterMap().get(c.getClientId()));
In encodeEnd, i basically create the markup and if component.getValue() is not null, i insert its contents in the markup. So far so good.
Problem is that getValue() can be only be String. I have custom class that represents a compound data type and i want to use that as the component's local value. But doesn't work - JSF converts to String.
I also tried using component.getAttributes() - from the decode method, where i put my custom object in keyed to private static final String someKey = "asd". But later at encodeEnd there is no value/key in the map.
I want the users of this component to be able to specify in their backing bean the custom data type and not worry about serialization/deserialization to text representation between client/server. Presumably i have to use a converter for that? But how can i set up a default and immutable converter for the custom component?
The problem has a simple enough of a solution. Inside the Renderer class (or right into the Component class if using that directly):
#Override
public Object getConvertedValue(FacesContext context, UIComponent component, Object submittedValue) throws ConverterException {
SomeCustomObject thing;
//... do magic to create SomeCustomObject based on submittedValue
return thing;
}
Now whenever getValue() is called on that component, SomeCustomObject will be returned and you can safely cast to it. Also in the backing beans can use SomeCustomObject as the type.
Also note when calling component.getValue() in the actual Renderer, it will return SomeCustomObject as well, so if you're restoring state, you must convert back to its String representation.
For more details see and #Kukeltje's answer above and check how the same thing is done for primefaces calendar component here: https://github.com/primefaces/primefaces/blob/master/src/main/java/org/primefaces/component/calendar/BaseCalendarRenderer.java
For another more concise and clear illustration, check #BalusC's answer as well.
Section 3.1.4 of the JSF 2.1 Specification says that all attributes of standard components are value expression enabled.
I want to assign a value expression to the action attribute of a commandButton:
<h:commandButton value="OK" action="#{myBean.valExp}" />
I also defined corresponding getValExp and setValExp methods in the bean's class.
However my JSF implementation (JBoss 6) takes that expression to be a method expression and thus yields a "method not found" error because there's no valExp()-method.
Am I doing something wrong or is the specification just too sloppy and actually doesn't mean all, but only the non method expression attributes? Or am I misunderstanding the spec?
[Remark: The reason for this question is no actual technical problem but me trying to understand the difference of value and method expressions.]
Value expressions are bound to properties which are exposed by public getter/setter methods.
<h:inputText value="#{bean.value}" />
This requires public T getValue() and public void setValue(T value) methods. Note that the presence of a private T value; property with literally exactly the same name is not mandatory. In pure output components like <h:outputText>, <h:dataTable>, <f:selectItems>, etc, the setter method is also not mandatory.
Method expressions are bound to non-getter/setter methods ("action" methods).
<h:commandButton value="submit" action="#{bean.submit}" />
This requires a public T submit() method where T can eventually be void and the method can eventually take additional arguments, depending on the attribute's method expression signature. You can read the exact details in the view declaration language documentation, for example the <h:inputText>, <h:commandButton> and <f:ajax> ones. Here's an extract of the action and actionListener attribute definitions of the <h:commandButton>:
Name: action
Type: javax.el.MethodExpression (signature must match java.lang.Object
action())
Description: MethodExpression representing the application action to invoke when
this component is activated by the user. The expression must
evaluate to a public method that takes no parameters, and returns an
Object (the toString() of which is called to derive the logical
outcome) which is passed to the NavigationHandler for this
application.
Name: actionListener
Type: javax.el.MethodExpression (signature must match void
actionListener(javax.faces.event.ActionEvent))
Description: MethodExpression representing an action listener method that will be
notified when this component is activated by the user. The
expression must evaluate to a public method that takes an
ActionEvent parameter, with a return type of void, or to a public
method that takes no arguments with a return type of void. In the
latter case, the method has no way of easily knowing where the event
came from, but this can be useful in cases where a notification is
needed that "some action happened".
Yes, I agree that the spec is somewhat sloppy in stating that all attributes support value expressions. Generally, they actually mean that all attributes support expression language as in #{}. From the other hand on, you can also interpret method expressions as if they are just "special" value expressions, although they are not exactly that. I've posted a spec issue report about this with the request to clear up some confusion: issue 1036.
I have a <h:selectBooleanCheckbox value="#{someBean.prop}"> where prop is a property of type int. Is it really not working straight away? Do I have to implement a custom converter to convert Boolean to int? Does anyone happen to have converter code for that at stock? I read that there is some bug in JSF 2.0 that prevents converters for <h:selectBooleanCheckbox> to be processed. Can anyone confirm that?
I use MyFaces 2, Tomahawk and Tomcat 6.
The <h:selectBooleanCheckbox> should, as its name say, be bound to a boolean or Boolean property. Nothing else. That it allows a converter attribute is actually a bug in the spec. It should never have allowed it.
The problem is more in your model, why would you use an int to represent a boolean state? Change your model to let it be a fullworthy boolean.
If changing the model isn't an option for some reason (a 3rd party API, a stupid architect, or stupid business restrictions, etc), then wrap the model getter/setter in the backing bean like follows
public boolean isChecked() {
return someModel.getSomeInt() != 0;
}
public void setChecked(boolean checked) {
someModel.setSomeInt(checked ? 1 : 0);
}
and use it instead as <h:selectBooleanCheckbox value="#{bean.checked}" />.
just to say MySQL doesn't have boolean as field type choice, and it could be an example for this need.
You can set field as tinyint in MySQL. Actually problem is getting method name when you create a boolean variable. Use method name getChecked() instead of isChecked().
I am new to JSF and managed beans. I have a managed bean with some private property with public setter and Getter methods. Now when I add the managed bean's properties to JSF forms, should I add the private methods directly or should I use call the property by Getter methods?
For example:
<h:inputText value="#{BeanName.userName}"/>
<h:inputText value="#{BeanName.getUserName()}"/>
Which one is correct in above?
Assuming that you're using JBoss EL or EL 2.2+, both ways would work fine in the initial display. But the first one is actually more correct because the second one would only get the value, but never set the value. If you want to collect input values, you should always go for the first way. The EL (Expression Language) will then automatically locate the getUserName() and setUserName() methods whenever needed.
The second way will never work when you're using standard JSF EL implementation since it doesn't support direct method calls.
To learn more about JSF, start at our JSF wiki page.
If in your java class you have something like
....
private String coolStuff;
public String getCoolStuff() {
return coolStuff;
}
....
Then in your jsf page you access it like so:
#{myBackingBean.coolStuff}
The framework automatically looks for a method called getCoolStuff()
Hope that helps
number 1 is correct from above it is the private field that you connect if you are using EL with JSF in your form.
You still need the getter and the setter which the managed bean calls to get the values so you can save them in a database ....etc