How to make a JSF custom component to store a custom type as its value? - jsf

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.

Related

How to obtain submitted value from a custom JSF input component? [duplicate]

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.

Get rendered html code in Backing Component from Composite Component

How can I get the posted form data in the backing component in the
processUpdates method?
#Override
public void processUpdates(FacesContext context) {
//get here rendered html code
}
Or can I get the posted form data in the decode method?
[Edit]:
My goal is to get the posted form data - Not to get the generated html code (Sry I wasn't precisely)
It is unclear what you want to achive, yet. I mean, at high level.
UIComponent.decode and processUpdates are medium-level lifecycle APIs which should be overriden when you want to extend the framework.
If you just need to use the framework, you need a managed bean, not a backing component.
Furthermore, generally only components that extend UIInput need to hook in those phases, because they are bound to a value="#{...}" value expression (which in turn refers to a managed bean, in most cases), and need to synchronize those values with the bound expression.
I suspect you are uselessly complicating your life: hooking into medium or low-level APIs is a real pain if you don't have an excellent understanding about how the framework operates.
Anyway, the standard request parameters decode into input component is this:
String clientId = this.getClientId(context);
Map<String, String> requestMap = context.getExternalContext().getRequestParameterMap();
String newValue = requestMap.get(clientId);
if (newValue != null)
{
this.setSubmittedValue(newValue);
}
Please, post the full xhtml facelet code (not the composite one, but the facelet using that composite), so I can understand where you want to go and I can try to point you to the right tool to use.

When to name a converter in JSF

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

How to get UIComponent value in Java?

I have a method in my JSF controller that is invoked by an ajax tag nested inside a visual component (really irrelevant which one). The method takes a single argument of type AjaxBehaviorEvent, from which I can obtain a Java representation of the invoking HTML visual component as a UIComponent and also downcast it to its specific corresponding type (e.g. h:inputText corresponding to HtmlInputText).
I understand that, in most cases, the value of the HTML visual component would be retrieved easily by referencing either the controller or entity [g|s]etters to which the form fields are mapped in the view. However, in my particular case, I would like to fetch the value of the visual component (in my case a form field) through its Java object rendering. While studying the faces API, I found ways to read various properties of the object, such as the ID or context but not the value that the component currently holds in the view.
Can anybody explain whether I am just not finding the right way to read it or it is so by design? If the latter, can you explain why it is designed like that? Is it to disable "backdoor" access to form fields as opposed to going through the view mapping?
There are a multitude of ways to pull values off a component. Going by what you already have UIInputt#getValue() and UIInput#getSubmittedValue() will provide the value.
The UIInput#getSubmittedValue() is fit for the purpose only between the APPLY_REQUEST_VALUES and VALIDATE phases of the JSF request. All other phases after, use the UIInputt#getValue(). You'll be using UIInput instead of the raw UIComponent you pulled from the event (UIInput extends UIComponent and it's the parent class for all input components that accept user-edited values). What you'll have will eventually look like:
UIInput theInput = (UIInput)event.getSource();
Object theValue = theInput.getValue();
There are other ways (not as clean) to get values within the request lifecycle also

JSF 2.0: When is encodeAll called and when is encodeBegin called?

Consider a custom UIComponent (for test purposes only):
public class UITest extends UIComponentBase {
#Override
public void encodeBegin(FacesContext context) throws IOException {
System.out.println("encodeBegin");
}
#Override
public void encodeAll(FacesContext context) throws IOException {
System.out.println("encodeAll");
}
}
When I add it to page inside a composite component, the encodeBegin() method gets called. However, when add it to page outside a composite component, the encodeAll() method gets called instead.
Adding it inside other UIComponents makes no difference, only composite component wrapper seems to change the behavior.
Couldn't find info why it is so? A link to the spec?
The spec is really messy in this case, stating that:
"These methods are called during the Render Response phase of the request processing lifecycle. encodeAll() will
cause this component and all its children and facets that return true from isRendered() to be rendered, regardless
of the value of the getRendersChildren() return value. encodeBegin(), encodeChildren(), and
encodeEnd()have the responsibility of creating the response data for the beginning of this component, this
component’s children (only called if the rendersChildren property of this component is true), and the ending of
this component, respectively."
However, this seems to be a mixture of new and old features, where the new functionality (encodeAll) seems to be incomplete in some ways:
I tried the following:
A) Calling the component directly in the page (no wrapper)
extend UIComponentBase (or other UIComponent class such as UIInput, UIOutput.. etc), declare it as a tag, and use it in the UI.
In this case the encodeAll method is called IF it is present (overridden), if not the encodeBegin and encodeEnd methods will be called!!
Another thing to note is that you can create a custom Renderer for the component, so you can separate rendering logic from behaviour. (by creating another class that extends Renderer, and annotating it with #FacesRenderer)
This is where it gets interesting; Renderer defines only encodeBegin, encodeChildren and encodeEnd (with no mention of encodeAll).
Now the logic seems to go roughly like this:
if (encodeAll is present)
encodeAll is called (and the renderer is ignored!)
else if(any of encodeBegin,Children,or end exist in the class that extends UIComponent)
call the method that was found in that component
else if(encodeBegin, children or end exist in the class that extends Renderer)
call the corresponding method that was found.
So this means that implementing encodeAll (or encodeBegin.. etc ) in the class extending UIComponent causes the renderer to be ignored!
B) Wrapping the component (cc:implementation.. etc)
In this case the same thing happened as above, except that encodeAll was not called in any case, no matter what I did.
Conclusion:
It seems that encodeAll is some kind of new functionality (or shortcut) to implement the rendering code, and it seems that cc:implementation has a bug in this case (it doesn't look for encodeAll).
I hope this is at least of some value to you, unfortunately I cannot provide a more thorough answer. :(
It also seems that nobody else knows about this.

Resources