JSF 1.2 dynamically adding custom validator - jsf

If I understand the JSF lifecycle correctly, it registers the Validators during Apply Request phase.
Does that mean I cannot call addValidator to the Component object handle I have inside my decode() method that gets called during Process Request Events phase? If so, is there any other way of dynamically adding custom Validators based on component's attribute value?
Thanks

What I hope should work is similar to..
public class ValidatorWrapper implements Validator {
private DoubleRangeValidator dbRangeValidator;
private LongRangeValidator lRangeValidator;
private String requiredValidatorType;/*An attribute to choose the type of validator*/
public ValidatorWrapper(){
dbRangeValidator = new DoubleRangeValidator(10.01, 20.99);lRangeValidator = new LongRangeValidator(10, 20);
}
#Override
public void validate(FacesContext context, UIComponent component,
Object value) throws ValidatorException {
if("LONG".equalsIgnoreCase(requiredValidatorType))
lRangeValidator.validate(context, component, value);
else if("DBL".equalsIgnoreCase(requiredValidatorType))
dbRangeValidator.validate(context, component, value);
} }

Related

Programatically add Parameter to HtmlCommandLink using a Phase Listener

I need to add a component (UIParameter) to a HtmlCommandLink component dinamically through a Phase Listener.
What I want to achieve is that every element <h:link outcome="out"> renders as <a href="out_url_parsed + ?param=paramvalue">.Where "param" is my component.
I've tried using this
private void addElement(final PhaseEvent event, final Class clazz, final UIComponent component) {
final FacesContext fcontext = event.getFacesContext();
UIViewRoot root = fcontext.getViewRoot();
if (root == null) {
return;
}
root.visitTree(new FullVisitContext(fcontext), new VisitCallback() {
#Override
public VisitResult visit(VisitContext context, UIComponent target) {
if (clazz.isInstance(target)) {
LOGGER.info("Element Found");
UIParameter parameter = new UIParameter();
parameter.setValue("willberonadom");
parameter.setId("sessiontoken");
target.getChildren().add(parameter);
}
return VisitResult.ACCEPT;
}
});
}
But it's not working. The element is actually found on the tree but the UIParameter does not render.
I've found that the UIViewRoot only has child elements after RENDER_RESPONSE phase. So i think this is why my added element is not rendered at the end of the process.
I'm sure I can add this param editing the views but I don't want to do that since it must be present on all h:link in the application and must be present on any other new added too. So I consider this as a better approach to avoid missing tags
On a similar case I've managed to add input hidden elements to every form on view with this code...
HtmlInputHidden hiddenToken = new HtmlInputHidden();
hiddenToken.setId("sessiontoken");
hiddenToken.setValue("willberandom");
hiddenToken.setRendered(true);
root.addComponentResource(event.getFacesContext(), hiddenToken,"form");
But it doesn't work on anchor tags
There are several mistakes:
You want to add a parameter to a HtmlCommandLink component which represents <h:commandLink>, but you're giving an example with <h:link>, which is represented by HtmlOutcomeTargetLink. What exactly do you want?
A PhaseListener on beforePhase() of RENDER_RESPONSE may be too late on GET requests which would only build the view for the first time during render response. At the moment your PhaseListener runs, the UIViewRoot would have no children at all. You'd better hook on view build time instead. For that, a SystemEventListener on PostAddToViewEvent is the best suitable.
You're setting the parameter name as an id instead of name. Use UIParameter#setName() instead of UIParameter#setId().
Provided that you actually meant to add them to <h:link> components, then here's a kickoff example how you can achieve that with a SystemEventListener.
public class YourSystemEventListener implements SystemEventListener {
#Override
public boolean isListenerForSource(Object source) {
return source instanceof HtmlOutcomeTargetLink;
}
#Override
public void processEvent(SystemEvent event) throws AbortProcessingException {
UIParameter parameter = new UIParameter();
parameter.setName("sessiontoken");
parameter.setValue("willberonadom");
((UIComponent) event.getSource()).getChildren().add(parameter);
}
}
(if you actually want to apply them on <h:commandLink> as well, just extend the isListenerForSource() check with a || source instanceof HtmlCommandLink)
In order to get it to run, register it as follows in faces-config.xml:
<application>
<system-event-listener>
<system-event-listener-class>com.example.YourSystemEventListener</system-event-listener-class>
<system-event-class>javax.faces.event.PostAddToViewEvent</system-event-class>
</system-event-listener>
</application>

Why are my #Inject'ed fields null when using the Converter with #Param?

I've a pretty basic setup here:
#Named
#ApplicationScoped
public class TalentIdConverter implements Converter {
#EJB
private EntityManagerDao em;
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
if (Strings.isNullOrEmpty(value)) {
return null;
}
return em.find(Talent.class, Long.parseLong(value));
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
return String.valueOf(((Talent) value).getId());
}
}
// Manager.class
public class Manager {
#Inject #Param(converterClass = TalentIdConverter.class, name = "talentId")
private ParamValue<Talent> curTalent
#PostConstruct
public void init() {
// use curTalent.getValue()
}
}
But every time TalentIdConverter.getAsObject is called em is null. Can someone enlighten me why this is?
I've also tried using #FacesConverter on the converter, but the behavior did not change.
This is on Wildfly-8.0.0.Beta1 using Weld-2.1.0.CR1 and Omnifaces-1.6.3
In the current 1.6.3 version, the #Param(converterClass) creates an unmanaged instance of the given converter class. It's like as if you're doing new TalentIdConverter() without any injection. If you need a managed instance, then you should in this specific case with a CDI-managed converter class (registered via #Named) be using #Param(converter) instead:
#Inject #Param(converter = "#{talentIdConverter}", name = "talentId")
private ParamValue<Talent> curTalent;
Or, if it's registered as a #FacesConverter("talentIdConverter") and thus a JSF-managed converter class (which also just transparently supports EJB when you've OmniFaces 1.6 installed):
#Inject #Param(converter = "talentIdConverter", name = "talentId")
private ParamValue<Talent> curTalent;
Or, if it's registered as a #FacesConverter(forClass=Talent.class), then you don't need to explicitly specify the converter anymore.
#Inject #Param(name = "talentId")
private ParamValue<Talent> curTalent;
On the other hand, trying to create a managed instance via BeanManager for #Param(converterClass) wouldn't have been a bad idea after all. We may look into this for future OmniFaces versions.

Trim String in JSF h:outputText value

Is there any way to string JSF h:outPutTextValue ? my string is A-B-A03 ,i just want to display last 3 characters .does openfaces have any avialable function to do this ?
Thanks
You could use a Converter for this job. JSF has several builtin converters, but no one suits this very specific functional requirement, so you'd need to create a custom one.
It's relatively easy, just implement the Converter interface according its contract:
public class MyConverter implements Converter {
#Override
public String getAsString(FacesContext context, UIComponent component, Object modelValue) throws ConverterException {
// Write code here which converts the model value to display value.
}
#Override
public Object getAsObject(FacesContext context, UIComponent component, String submittedValue) throws ConverterException {
// Write code here which converts the submitted value to model value.
// This method won't be used in h:outputText, but in UIInput components only.
}
}
Provided that you're using JSF 2.0 (your question history confirms this), you can use the #FacesConverter annotation to register the converter. You can use the (default) value attribute to assign it a converter ID:
#FacesConverter("somethingConverter")
(where "something" should represent the specific name of the model value you're trying to convert, e.g. "zipcode" or whatever it is)
so that you can reference it as follows:
<h:outputText value="#{bean.something}" converter="somethingConverter" />
For your particular functional requirement the converter implementation can look like this (assuming that you actually want to split on - and return only the last part, which makes so much more sense than "display last 3 characters"):
#FacesConverter("somethingConverter")
public class SomethingConverter implements Converter {
#Override
public String getAsString(FacesContext context, UIComponent component, Object modelValue) throws ConverterException {
if (!(modelValue instanceof String)) {
return modelValue; // Or throw ConverterException, your choice.
}
String[] parts = ((String) modelValue).split("\\-");
return parts[parts.length - 1];
}
#Override
public Object getAsObject(FacesContext context, UIComponent component, String submittedValue) throws ConverterException {
throw new UnsupportedOperationException("Not implemented");
}
}
You can try and use the fn:substring functions from JSTL:
${fn:substring('A-B-A03', 4, 7)}
If your string comes from a bean you can add an extra getter to return the trimmed version:
private String myString = "A-B-A03";
public String getMyStringTrimmed()
{
// You could also use java.lang.String.substring with some ifs here
return org.apache.commons.lang.StringUtils.substring(myString, -3);
}
Now you can use the getter in your JSF page:
<h:outputText value="#{myBean.myStringTrimmed}"/>

After validation jsf fails to reset values

I have a jsf composite component implemented from two p:calendar components.
The idea is when the first calendar is selected, the value of the second calendar need to be reset. There is a problem when the validation takes place, and the reset of the second calendar is not performed.
After reading posts I decided to use EditableValueHolder in my validator.
I have custom validator: in which I added the following code:
#Override
public void validate(FacesContext fc, UIComponent uic, Object o) throws ValidatorException {
//....
resetValues(fc);
}
public void resetValues(FacesContext fc) {
PartialViewContext partialViewContext = fc.getPartialViewContext();
Collection<String> renderIds = partialViewContext.getRenderIds();
UIComponent input;
UIViewRoot viewRoot = fc.getViewRoot();
for (String renderId : renderIds) {
input = viewRoot.findComponent(renderId);
if (input.isRendered() && input instanceof EditableValueHolder) {
EditableValueHolder editableValueHolder = (EditableValueHolder) input;
editableValueHolder.setSubmittedValue(null);
editableValueHolder.setValue(null);
editableValueHolder.setValid(true);
editableValueHolder.setLocalValueSet(false);
}
}
}
After debug I can see that each code line is passed, but nothing is happening on jsf side.
This is not the right moment to reset the values. They will be overridden anyway for the current component after the validate() method leaves and also for the second calendar once it get validated. You need to perform the reset somewhere after the update model values phase, preferably before the invoke action phase, so that you've chance to change the model value in an action(listener) method. You could use an ActionListener or a PhaseListener for this.
By the way, the JSF utility library OmniFaces has a reuseable solution for this in flavor of ResetInputAjaxActionListener.

JSF 2.0 How to create state-saving UIComponents?

I tried simply:
public class UIDemoComponent extends UIComponentBase {
private String someVariable; // this gets always cleared, getters/setters omitted
public UIDemoComponent() {
super(); // place breakpoint here
}
#Override
public void encodeBegin(FacesContext context) throws IOException {
HtmlForm form = new HtmlForm();
form.setStyle("padding: 10px; background-color: blue;");
getChildren().add(form);
AjaxBehavior behavior = new AjaxBehavior();
behavior.setRender(Arrays.asList("#form"));
form.addClientBehavior("click", behavior);
}
}
I have registered a tag handler and succesfully inserted the component into page. However, when I click the blue form that is rendered, JSF re-creates the component (breakpoint in the constructor is caught). The effect of this is that any instance variables are lost. How is one supposed to save data into components if they always get re-created?
I tried overriding and inspecting the state staving mechanisms of StateHolder and PartialStateHolder wihout luck:
#Override
public Object saveState(FacesContext context) {
return super.saveState(context); // breakpoint
}
#Override
public void restoreState(FacesContext context, Object state) {
super.restoreState(context, state); // breakpoint
}
JSF is executing the saveState when page and components are created, but restoreState is never called. Actually, when the AJAX request is being processed, a new instamnce of UIDemoComponent is created but saveState method is called again, instead of restoreState.
How to create such a state-saving component (that retains the instance fields over AJAX requests)?
Seems like JSF is running some pre-checks on state object and not executing restoreState at all if custom fields are not entered. Only after actually inserting custom values into state object, the restoreState gets called.
For example:
#Override
public Object saveState(FacesContext context) {
Object[] rtrn = new Object[2];
rtrn[0] = super.saveState(context);
rtrn[1] = "dummy";
return rtrn;
}
After this, the restoreState gets called and property fields can be restored as wanted.

Resources