JSF custom composite components
input.xhtml
<cc:interface>
<cc:attribute name="validator"/>
</cc:interface>
<cc:implementation>
<h:inputText validator="#{cc.attrs.validator}"/>
</cc:implementation>
*.xhtml
<l:input value = ... validator="#{testValidator.validator}"/>
java code
#ManagedBean
public class TestValidator {
public void validator(FacesContext context, UIComponent component, Object value) throws ValidatorException {
System.out.println("Call validator");
}
}
PropertyNotFoundException:
validator="#{testValidator.validator}": The class 'TestValidator' does not have the property 'validator'.
How to solve this problem?
my final way:
This is indeed not going to work. In order to attach a validator to an input component specified by the composite, you need to register the input component as a <cc:editableValueHolder> in the <cc:interface> first.
<cc:interface>
<cc:editableValueHolder name="yourInputName" targets="yourInputId" />
</cc:interface>
<cc:implementation>
<h:inputText id="yourInputId" ... />
</cc:implementation>
This way, any <f:validator for="yourInputName"> nested in the composite component declaration will be applied to the desired input component.
<l:input>
<f:validator validatorId="myValidator" for="yourInputName" />
</l:input>
You'll only need to replace the tight coupled validator method by a real standalone Validator implementation.
#FacesValidator("myValidator")
public class MyValidator implements Validator {
// ...
}
Note: the standard JSF validators like <f:validateLength>, <f:validateRequired>, etc have all also a for attribute for this purpose.
You need to define a cc:attribute "validator" with targets attribute:
<cc:interface>
<cc:attribute name="validator" targets="inputId"/>
</cc:interface>
<cc:implementation>
<h:inputText id="inputId"/>
</cc:implementation>
(Notice that I don't define validator attribute in h:inputText)
This is easy as long as you don't implement component class for your composite component. Well... even then it is easy, but it won't work if your component class extends UIInput. It works if it extends UINamingComponent class (I am not sure if this is the best solution, or if you can make it work with UIInput but it works).
So if you want to have your own component class try this one:
<cc:interface componentType="myComponent>
<cc:attribute name="validator" targets="inputId"/>
</cc:interface>
<cc:implementation>
<h:inputText id="inputId"/>
</cc:implementation>
#FacesComponent("myComponent")
public class MyComponent extends UINamingContainer {
#Override
public String getFamily() {
return UINamingContainer.COMPONENT_FAMILY;
}
//your code here
}
Related
Is it possible to have multiple values for a composite component. here is the example of what I want to be able to do:
<foo:zipcode id="zipcode" value="#{bean.zipcode}" city="#{bean.city}" >
<f:ajax eventt="changeEvent" update="city">
</foo:zipcode>
<h:inputText id="city" value="#{bean.city} />
Where the composite component has an input for the zipcode. Once entered I do a lookup/validation of the zipcode and update the city.
The composite component looks like:
<cc:interface componentType="component.zipCode">
<cc:attribute name="value" required="true" />
<cc:attribute name="city" />
</cc:interface>
<cc:implementation>
<p:inputText id="zipCode" value="#{cc.attrs.value}" >
<p:ajax partialSubmit="true" process="#this" update="#this" listener="#{cc.lookupZip}"/>
</p:inputText>
</cc:implementation>
Then for the backing component I have:
#FacesComponent(value = "component.zipCode")
public class UIZipCode extends UIInput implements NamingContainer {
public void lookupZip(AjaxBehaviorEvent event) {
// Code to lookup zipcode and get city.
// What goes HERE to update the city passed as attribute?
}
}
Am I missing something obvious. I've dug through tons of BalusC's posts and tried something like:
ValueExpression city = (ValueExpression) getAttributes().get("city");
city.setValue(FacesContext.getCurrentInstance().getELContext(), cityString);
I have most of this working, but can't figure out how to set the value of city in the backing bean from the lookupZip method. The value of city is always
null. Is this possible?
on my jsf page I use a composite component which is basically an input field. My backing bean holds a list that contains all allowed languages which the user is allowed to enter. On submit I want to validate the user's input based on that list.
So I need to pass the list to the validator because obtaining that list takes to long, so I do it when the backing bean is initialized. But I struggle here.
My composite component looks like this:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:composite="http://java.sun.com/jsf/composite">
<composite:interface>
<composite:attribute name="value" required="true" />
<composite:editableValueHolder name="nameValidator" targets="value" />
</composite:interface>
<composite:implementation>
<h:panelGroup>
<h:inputText id="value" value="#{cc.attrs.value}" />
</h:panelGroup>
</composite:implementation>
</html>
My validator look like this:
#FacesValidator("OcrLanguageValidator")
public class OcrLanguageValidator implements Validator {
#Override
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
Object param1 = context.getExternalContext().getRequestParameterMap().get("param1");
if (param1 != null) {
}
}
}
And I am using the component like this in my jsf page:
<h:form id="newParameter" styleClass="form-horizontal">
<uiComps:testCC value="test" >
<f:validator validatorId="OcrLanguageValidator" for="nameValidator" />
<f:param name="param1" value="blub" />
</uiComps:testCC>
</h:form>
When the validator method is called the param1 is always null. How can I achieve my goal? I use Mojarra 2.3.
Edit because of possible duplication:
I don't think, that this is a duplication. I am using a composite component here. Without it is a way to do it. But I don't like the idea of adding a special attribute to the component just because I need it in one single case but not the others. Hope there might be a better solution to this problem.
My Composite Component contains the following form:
<cc:interface componentType="answerCompositeComponent">
<cc:attribute name="AnswerType" type="code.elephant.domainmodel.AnswerType" required="true" />
<cc:attribute name="ItemSource" type="code.elephant.domainmodel.Answer" required="true" />
<cc:attribute name="QuestionId" type="java.lang.Long" required="true" />
</cc:interface>
<cc:implementation>
<input jsf:id="sc#{cc.attrs.ItemSource.answerId}" />
</cc:implementation>
How can I access the value of the <input jsf:id="sc#{cc.attrs.ItemSource.answerId}" /> in my Backing Component? I tried the following in my backing bean in the overriden processUpdates method.
Answer ItemSource = (Answer) getValueExpression("ItemSource").getValue(context.getELContext());
String formid = String.format("sc%d", ItemSource.getAnswerId());
String get = context.getExternalContext().getRequestParameterMap().get(formid);
String get is always null. Is there a way to get the value of the input?
PS: I know that using plain html in jsf is not the purpose of it. I'm just interessted how my plan is achievable.
I never used plain html with jsf attributes, so I don't know if it's applicable.
Generally, this is a common way to access nested components in a composite:
<cc:interface componentType="answerCompositeComponent">
<cc:attribute name="AnswerType" type="code.elephant.domainmodel.AnswerType" required="true" />
<cc:attribute name="ItemSource" type="code.elephant.domainmodel.Answer" required="true" />
<cc:attribute name="QuestionId" type="java.lang.Long" required="true" />
</cc:interface>
<cc:implementation>
<h:inputText id="questionInput" binding="#{cc.input}" />
<!-- maybe something like this might work
<input jsf:id="questionInput" jsf:binding="#{cc.input}" />
-->
</cc:implementation>
where
#FacesComponent("answerCompositeComponent")
public class AnswerCompositeComponent extends UINamingContainer
{
private UIInput input;
#Override
public void processUpdates(FacesContext context)
{
super.processUpdates(context);
Object value = input.getValue();
Object localValue = input.getLocalValue();
Object submittedValue = input.getSubmittedValue();
// do your things with values
}
public UIInput getInput()
{
return input;
}
public void setInput(UIInput input)
{
this.input = input;
}
}
Note that a composite backing component is a NamingContainer, so prefer static (or none at all) nested component IDs. Avoid dynamic IDs, unless you really need them and you know exactly what you're doing.
I'm trying to create my own dataTable like the primefaces one. The problem is that cc.attrs.var when used throws a IllegalArgumentException. So I'm wondering how I can have the var attribute like Primefaces.
<cc:interface>
<cc:attribute name="value"/>
<cc:attribute name="var"/>
<cc:attribute name="styleClass"/>
</cc:interface>
<cc:implementation>
<div>Previous</div>
<div>Next</div>
<h:dataTable value="#{cc.attrs.value}" var="#{cc.attrs.var}" styleClass="#{cc.attrs.styleClass}">
<ui:insert/>
</h:dataTable>
</cc:implementation>
As per the UIData#setValueExpression() javadoc, it's not allowed to have an EL expression in var attribute.
Throws:
IllegalArgumentException - if name is one of id, parent, var, or rowIndex
Your best bet is to create a backing component wherein you manually evaluate and set the var attribute of the UIData component bound to <h:dataTable> during the postAddToView event.
<cc:interface componentType="yourTableComposite">
<cc:attribute name="value" />
<cc:attribute name="var" />
</cc:interface>
<cc:implementation>
<f:event type="postAddToView" listener="#{cc.init}" />
<h:dataTable binding="#{cc.table}" value="#{cc.attrs.value}">
<cc:insertChildren />
</h:dataTable>
</cc:implementation>
#FacesComponent("yourTableComposite")
public class YourTableComposite extends UINamingContainer {
private UIData table;
public void init() {
table.setVar((String) getAttributes().get("var"));
}
public UIData getTable() {
return table;
}
public void setTable(UIData table) {
this.table = table;
}
}
Note that I fixed the <ui:insert> to be <cc:insertChildren>. The <ui:insert> can only be used in <ui:composition>/<ui:decorate>.
See also:
Initialize a composite component based on the provided attributes
How does the 'binding' attribute work in JSF? When and how should it be used?
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().