Switch validator on component - jsf

How can i switch my validator in a component dependence on a boolean.
I have a selectBooleanCheckbox and when its true i want use FirstValidator else i want use SecondValidator. I found nothing about this "special" case.
Nothing special only for example code:
Xhtml:
<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html">
<h:head>
</h:head>
<h:body>
<h:form>
<h:selectBooleanCheckbox value="#{testBean.firstValidator}"/>
<h:inputText>
<f:validator validatorId="FirstValidator" />
</h:inputText>
<h:commandButton value="Test" />
<h:messages />
</h:form>
</h:body>
</html>
Bean:
#ManagedBean
#ViewScoped
public class TestBean implements Serializable{
private boolean firstValidator;
public boolean isfirstValidator() {
return firstValidator;
}
public void setfirstValidator(boolean firstValidator) {
this.firstValidator = firstValidator;
}
}
Validator1:
#FacesValidator("FirstValidator")
public class FirstValidator implements Validator{
#Override
public void validate(FacesContext context, UIComponent component,
Object value) throws ValidatorException {
String valueAsString = (String) value;
if(valueAsString.contains("a")){
throw new ValidatorException(new FacesMessage("Fail!"));
}
}
}
Validator2:
#FacesValidator("SecondValidator")
public class SecondValidator implements Validator{
#Override
public void validate(FacesContext context, UIComponent component,
Object value) throws ValidatorException {
String valueAsString = (String) value;
if(valueAsString.contains("b")){
throw new ValidatorException(new FacesMessage("Fail2!"));
}
}
}

Just set/disable the validator depending on checkbox's value. You only need to make sure that you pick the checked value as available during view build time (which runs during restore view phase). So you definitely can't use the model value #{testBean.firstValidator} for this (which is only set during update model values phase). You'd need to determine the HTTP request parameter instead. It's empty if the checkbox is unchecked, otherwise it's not empty.
First bind the checkbox component to the view (not to a bean!) via binding attribute:
<h:selectBooleanCheckbox binding="#{checkbox}" ... />
This way the request parameter can be dynamically obtained as #{param[checkbox.clientId]}.
Now you can use either conditional setting of validator ID:
<f:validator validatorId="#{empty param[checkbox.clientId] ? 'firstValidator' : 'secondValidator'}" />
Or conditional setting the validator's disabled attribute:
<f:validator validatorId="firstValidator" disabled="#{not empty param[checkbox.clientId]}" />
<f:validator validatorId="secondValidator" disabled="#{empty param[checkbox.clientId]}" />
Note that I altered the validator IDs as per Java instance variable naming conventions. You also don't do as follows in normal Java code, right?
Validator FirstValidator = new FirstValidator();

Related

JSF can't get a renderer for a custom composite input component

I'm building my custom JSF composite component in order to input periodicities. I've got this model:
public class Periodicity implements Serializable {
private PeriodicityType type;
private Integer value = 0;
public PeriodicityExchange() {
super();
}
/**Getter and setters**/
}
Where PeriodicityType is an enum containing DAY, WEEK, MONTH, YEAR.
The input component for this model consists of a p:spinner element to select a numeric value and a h:selectOneMenu to select the periodicity type. The component works well, but now I want to have a java class backing it, so I have modified my facelet file:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:composite="http://xmlns.jcp.org/jsf/composite"
xmlns:p="http://primefaces.org/ui">
<composite:interface componentType="periodicityInput">
<composite:attribute name="value" type="model.Periodicity" />
</composite:interface>
<composite:implementation>
<div class="col-xs-2">
<p:spinner binding="#{cc.periodicityValue}"
value="#{cc.attrs.value.value}" min="0" />
</div>
<div class="col-xs-3">
<h:selectOneMenu binding="#{cc.periodicityType}"
value="#{cc.attrs.value.type}" styleClass="form-control">
<f:selectItem itemLabel="No periodicity" noSelectionOption="true" />
<f:selectItem itemLabel="Days" itemValue="DAY" />
<f:selectItem itemLabel="Weeks" itemValue="WEEK" />
<f:selectItem itemLabel="Months" itemValue="MONTH" />
<f:selectItem itemLabel="Years" itemValue="YEAR" />
</h:selectOneMenu>
</div>
</composite:implementation>
</html>
And that's my java class (based in this BalusC's tutorial). It's registered as a component using the faces-config.xml file:
public class PeriodicityInput extends UIInput implements NamingContainer {
private UIInput periodicityValue;
private UIInput periodicityType;
#Override
public void encodeBegin(FacesContext context) throws IOException {
super.encodeBegin(context);
}
#Override
protected Object getConvertedValue(FacesContext context, Object newSubmittedValue)
throws ConverterException {
try {
return new PeriodicityExchange(
Integer.parseInt(newSubmittedValue.toString().split("-")[0]),
newSubmittedValue.toString().split("-")[1]);
} catch (Exception ex) {
throw new ConverterException(ex);
}
}
public String getFamily() {
return COMPONENT_FAMILY;
}
public UIInput getPeriodicityType() {
return periodicityType;
}
public UIInput getPeriodicityValue() {
return periodicityValue;
}
#Override
public Object getSubmittedValue() {
return periodicityValue.getSubmittedValue() + "-" + periodicityType.getSubmittedValue();
}
public void setPeriodicityType(UIInput periodicityType) {
this.periodicityType = periodicityType;
}
public void setPeriodicityValue(UIInput periodicityValue) {
this.periodicityValue = periodicityValue;
}
}
The issue is that as soon as I use the componentType attribute to bind my component to the java class, the component stops rendering. encodeBegin gets called, but doing some further debugging, I've noticed that when JSF tries to get the renderer for the composite component, it returns null. The method getRenderer(FacesContext) from the UIComponentBase class tries to access context.getRenderKit().getRenderer(getFamily(), rendererType), where the family evaluates to javax.faces.Input and rendererType to javax.faces.Composite.
I'm using Mojarra 2.2.12. What am I missing here?
where the family evaluates to javax.faces.Input
This is not correct. It's supposed to be javax.faces.NamingContainer, the constant value of UINamingContainer.COMPONENT_FAMILY.
The COMPONENT_FAMILY below is likely messed up during refactoring with static imports:
#Override
public String getFamily() {
return COMPONENT_FAMILY;
}
The above is actually inherited from UIInput while it has to be UINamingContainer. Better specify it with FQN.
#Override
public String getFamily() {
return UINamingContainer.COMPONENT_FAMILY;
}

Is it possible to call a method defined in method-signature programmatically?

Suppose I have a composite component
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:composite="http://java.sun.com/jsf/composite"
`enter code here` xmlns:h="http://java.sun.com/jsf/html"
xmlns:p="http://primefaces.org/ui">
<h:head></h:head>
<h:body>
<composite:interface componentType="editableLabel">
<composite:attribute name="value" required="true" type="java.lang.String"/>
<composite:attribute name="oldValue" type="java.lang.String"/>
<composite:attribute name="editMode" required="false" default="#{false}" type="java.lang.Boolean"/>
<composite:attribute name="updateListener" required="false" method-signature="void actionListener()"/>
<composite:attribute name="cancelListener" required="false" method-signature="void actionListener()"/>
</composite:interface>
<composite:implementation>
<h:panelGroup id="editableLabelComponent">
<h:panelGroup rendered="#{cc.attrs.editMode}">
<p:inputText value="#{cc.attrs.value}"/>
<p:commandButton value="Update" actionListener="#{cc.update}" update="editableLabelComponent"/>
<p:commandButton value="Cancel" actionListener="#{cc.cancel}" update="editableLabelComponent"/>
</h:panelGroup>
<p:commandLink>
<p:outputLabel id="display" value="#{cc.attrs.value}" rendered="#{!cc.attrs.editMode}"/>
<p:ajax event="click" listener="#{cc.toggleEditMode}" update="editableLabelComponent"/>
</p:commandLink>
</h:panelGroup>
</composite:implementation>
</h:body>
</html>
with a FacesComponent
import org.apache.commons.lang3.StringUtils;
import javax.el.ELContext;
import javax.el.ValueExpression;
import javax.faces.component.FacesComponent;
import javax.faces.component.NamingContainer;
import javax.faces.component.UIComponentBase;
import javax.faces.component.UINamingContainer;
import javax.faces.context.FacesContext;
import java.io.Serializable;
/**
* Created by labraham on 1/14/16.
*/
#FacesComponent(value = "editableLabel")
public class EditableLabel extends UIComponentBase implements Serializable, NamingContainer {
private static final long serialVersionUID = 108467781935083432L;
public static final String VALUE_ATTRIBUTE = "#{cc.attrs.value}";
public static final String OLD_VALUE_ATTRIBUTE = "#{cc.attrs.oldValue}";
public static final String EDIT_MODE_ATTRIBUTE = "#{cc.attrs.editMode}";
private FacesContext context;
private ELContext elContext;
#Override
public String getFamily() {
return UINamingContainer.COMPONENT_FAMILY;
}
/**
*
*/
public void update() {
ValueExpression oldValue = getAttr(OLD_VALUE_ATTRIBUTE, String.class);
String value = (String) getAttr(VALUE_ATTRIBUTE, String.class).getValue(getElContext());
oldValue.setValue(getElContext(), value);
toggleEditMode();
}
/**
*
*/
public void cancel() {
ValueExpression value = getAttr(VALUE_ATTRIBUTE, String.class);
String oldValue = (String) getAttr(OLD_VALUE_ATTRIBUTE, String.class).getValue(getElContext());
value.setValue(getElContext(), oldValue);
toggleEditMode();
}
/**
*
*/
public void toggleEditMode() {
if (StringUtils.isEmpty((String) getAttr(OLD_VALUE_ATTRIBUTE, String.class).getValue(getElContext()))) {
getAttr(OLD_VALUE_ATTRIBUTE, String.class).setValue(getElContext(),
getAttr(VALUE_ATTRIBUTE, String.class).getValue(getElContext())
);
}
ValueExpression editmode = getAttr(EDIT_MODE_ATTRIBUTE, Boolean.class);
editmode.setValue(getElContext(), !((Boolean) editmode.getValue(getElContext())));
}
/**Get value expression for a given attr.*/
private ValueExpression getAttr(final String attribute, final Class<?> attributeType) {
return getContext().getApplication().getExpressionFactory()
.createValueExpression(getElContext(), attribute, attributeType);
}
/**Get ElContext.*/
public ELContext getElContext() {
if (this.elContext == null) {
elContext = getContext().getELContext();
}
return elContext;
}
public void setElContext(final ELContext elContext) {
this.elContext = elContext;
}
/**Get faces context*/
public FacesContext getContext() {
if (this.context == null) {
context = FacesContext.getCurrentInstance();
}
return context;
}
public void setContext(final FacesContext context) {
this.context = context;
}
}
now all instances of this widget will have the baseline functionality for cancel and update as defined by the cancel() and update() functions. However in addition this basic functionality the user may want some additional functionality (for instance calling a webservice on update) which they will add by some sort of backing bean method in the updateListener and/or cancelListener attributes.
My question is Is it possible to call the method that the user attached to the listener attributes programmatically from inside the FacesComponent or am I going about this the wrong way?
Yes, it's available as a MethodExpression typed attribute. All composite attributes are available the usual way by the inherited UIComponent#getAttributes() map. See also How to access Composite Component attribute values in the backing UIComponent?
E.g., to get #{cc.attrs.updateListener} and invoke with no params:
MethodExpression updateListener = (MethodExpression) getAttributes().get("updateListener");
updateListener.invoke(getFacesContext().getELContext(), null);
Unrelated to the concrete problem, the way how you deal with ValueExpression attributes is clumsy. To get #{cc.attrs.oldvalue}, just do:
String oldvalue = (String) getAttributes().get("oldvalue");
To set #{cc.attrs.value}, just do:
getAttributes().put("value", oldvalue);
Having FacesContext and ELContext as instance variables does technically fortunately not harm in this specific construct, but this is fishy, you'd better get them in method local scope. The FacesContext is just available by inherited UIComponent#getFacesContext().
See also:
How can I create a JSF composite component that allows me to read/write to the attributes?
java.lang.IllegalStateException at com.sun.faces.context.FacesContextImpl.assertNotReleased

Issue when converting GET request parameters on viewParam

I'm trying to convert GET request parameters passed from another view like this:
<f:metadata>
<f:viewParam name="id"
value="#{targetViewBean.fooFromSourceView}"
converter="fooConverter"
converterMessage="Foo converter message"
required="true" requiredMessage="Foo required message"/>
<f:viewAction action="#{targetViewBean.doSomethingWithFoo()}"/>
</f:metadata>
But only the Converter.getAsString(..., Object value) method is called and value is always null, even thou the GET parameter is really sent.
I found BalusC blog post about this and, AFAIK, I followed it to the letter. Still no good. Here's the full code:
Source view
<h:head>
<title>Source view</title>
</h:head>
<h:body>
<ul>
<ui:repeat value="#{sourceViewBean.foos}" var="foo">
<li>
<h:link value="Foo \##{foo.id}" outcome="target-view">
<f:param name="id" value="#{foo.id}" />
</h:link>
</li>
</ui:repeat>
</ul>
</h:body>
Backing bean
#Named #ViewScoped
public class SourceViewBean implements Serializable {
public Collection<Foo> getFoos() {
return Db.INSTANCE.getFoos();
}
private static final long serialVersionUID = 1L;
}
Target view
<f:metadata>
<f:viewParam name="id"
value="#{targetViewBean.fooFromSourceView}"
converter="fooConverter"
converterMessage="Foo converter message"
required="true" requiredMessage="Foo required message"/>
<f:viewAction action="#{targetViewBean.doSomethingWithFoo()}"/>
</f:metadata>
<h:head>
<title>Target view</title>
</h:head>
<h:body>
<h:outputText value="ID: #{targetViewBean.fooFromSourceView.id}" />
</h:body>
Target view backing bean
#Named
#ViewScoped
public class TargetViewBean implements Serializable {
private Foo fooFromSourceView;
public void doSomethingWithFoo() {
System.out.println("Foo is here? " + fooFromSourceView != null);
}
public Foo getFooFromSourceView() {
return fooFromSourceView;
}
public void setFooFromSourceView(Foo fooFromSourceView) {
this.fooFromSourceView = fooFromSourceView;
}
private static final long serialVersionUID = 1L;
}
The converter
#FacesConverter(value = "fooConverter")
public class FooConverter implements Converter {
#Override
public Object getAsObject(
FacesContext context, UIComponent component, String value) {
if (value == null || !value.matches("\\d+")) {
return null;
}
for (Foo foo : Db.INSTANCE.getFoos()) {
if (foo.getId().equals(Integer.parseInt(value))) {
return foo;
}
}
throw new ConverterException(new FacesMessage("No Foo found!"));
}
#Override
public String getAsString(
FacesContext context, UIComponent component, Object value) {
if (!(value instanceof Foo) || ((Foo) value).getId() == null) {
return null;
}
return ((Foo) value).getId().toString();
}
}
I was able to find the problem after taking a look at the actual code you sent. The issue is not with the converter. It's with the xml namespaces at the top of your project. For instance, in source-view.xml you have
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:f="http://xmlns.jcp.org/jsf/core"
But they should be
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core">
And target-view.xhtml should be
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
GlassFish seems to change the namespaces for some reason. I didn't try to find out why it behaves like that though so keep that in mind. Anyway, once I changed it, the correct phases were being outputted in GlassFish's output window. So go and make the necessary change where needed.
Note: In case you are wondering why you are getting the following error
The metadata component needs to be nested within a f:metadata tag. Suggestion: enclose the necessary components within <f:metadata>
This seems to be a reported issue with JSF 2.2
Also, I'm not sure why your h:link is nested inside an h:form. It's not needed.
UPDATE
Seems like some of the taglibs are not fully functional or am I reading this wrong ?
https://java.net/jira/browse/JAVASERVERFACES-2868

Concating values from multiple output and input components

I have 2 outputText fields, 1 required field and 1 optional field. How can I concat, or append all of the values and set it as a single model property?
<h:outputText value="AT-" />
<h:outputText value="#{yearOfDate}"/>
<p:inputMask value="#{requiredRefNo}" required="true" mask="9999"/>
<p:inputMask value="#{optionalRefNo}" mask="aa"/>
In the given example I have for example the string AT-2012-6060-VI. How can I append all of the values and set it as a single model property?
For you it would probably be the easiest to create a composite component for this with a backing component which extends UIInput and wherein the desired format is returned by UIInput#getSubmittedValue().
Here's a kickoff example in its simplest form:
/resources/components/refNo.xhtml
<ui:component
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:cc="http://java.sun.com/jsf/composite"
xmlns:p="http://primefaces.org/ui"
>
<cc:interface componentType="refNoComposite" />
<cc:implementation>
AT-#{cc.year}-<p:inputMask id="ref1" required="true" mask="9999"/>-<p:inputMask id="ref2" mask="aa"/>
</cc:implementation>
</ui:component>
com.example.RefNoComposite
#FacesComponent("refNoComposite")
public class RefNoComposite extends UIInput implements NamingContainer {
public RefNoComposite() {
getStateHelper().put("year", new SimpleDateFormat("yyyy").format(new Date()));
}
#Override
public String getFamily() {
return UINamingContainer.COMPONENT_FAMILY;
}
#Override
public Object getSubmittedValue() {
return new StringBuilder()
.append("AT")
.append('-')
.append(getYear())
.append('-')
.append(((UIInput) findComponent("ref1")).getSubmittedValue())
.append('-')
.append(((UIInput) findComponent("ref2")).getSubmittedValue())
.toString();
}
public String getYear() {
return (String) getStateHelper().eval("year");
}
}
Usage example in a random Facelets page:
xmlns:cc="http://java.sun.com/jsf/composite/components"
...
<h:form>
<cc:refNo value="#{bean.value}" />
<p:commandButton value="submit" action="#{bean.submit}" />
</h:form>
Note: if you'd like to validate the value as well, you'd like to override the UIInput#validateValue() method in the backing component. The 2nd argument is by the way exactly the getSubmittedValue().

How to add a message to a specific component from JSF backing bean

I have an h:inputText and an h:message connected to it:
<h:inputText id="myText" value="#{myController.myText}" />
<a4j:outputPanel>
<h:message for="myText" .../>
</a4j:outputPanel>
I want to send a message to it from java, in a manner like:
FacesContext.getCurrentInstance().addMessage(arg0, arg1);
which is sent to h:messages, but to a specific id in a specific form.
How can I do this? (Without implementing validation bean or validation method - meaning without throwing validation exception).
You need to provide the so called client id, which you'll find on UIComponent.
The following is a quick example of how to use this.
Consider the following bean:
#ManagedBean
#RequestScoped
public class ComponentMsgBean {
private UIComponent component;
public UIComponent getComponent() {
return component;
}
public void setComponent(UIComponent component) {
this.component = component;
}
public String doAction() {
FacesContext context = FacesContext.getCurrentInstance();
context.addMessage(component.getClientId(), new FacesMessage("Test msg"));
return "";
}
}
being used on the following Facelet:
<html 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"
>
<h:body>
<h:form>
<h:outputText id="test" value="test component" binding="#{componentMsgBean.component}"/>
<h:message for="test"/>
<h:commandButton value="click me" action="#{componentMsgBean.doAction}" />
</h:form>
</h:body>
</html>
This will add a Faces message with content "Test msg" for the outputText component used in the example.
Another way to do that is: give an ID to the form, like "form1", then, when add the message, the clientId is "form1:test".

Resources