Pass parameter to validator - jsf

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.

Related

Proper way to set a property of a controller when using <ui:include>

I have a big form that I need to reuse in multiple pages. So, I decided to create a
<ui:composition> that contains the form and include it in some pages (page1.xhtml and page2.xhtml).
form.xhtml:
<ui:composition ...>
<!-- The form goes here -->
</ui:composition>
This form has a controller called FormController.
In page1.xhtml and page2.xhtml I just include the form using a <ui:include> tag:
<ui:include src="/WEB-INF/.../form.xhtml"/>
I need to initialize a property in the FormController bean, so, in page1.xhtml I decided to set an attribute with the Id that I need (for example 5):
<c:set var="id" scope="request" value ="5"/>
And in the controller I just get the value of this attribute:
#PostConstruct
public init() {
Long id = ((HttpServletRequest)FacesContext.getCurrentInstance().getExternalContext().getRequest()).getAttribute("id");
//Do some queries to the database
}
Until know, everything works fine. But in page2.xhtml the "initialization" of the bean property has to be done after an ajax request, so I used the following code:
<h:selectOneMenu ...>
<f:selectItems ...>
<f:ajax listener="#{otherBean.doSomething}" render="panel"/>
</h:selectOneMenu>
<h:panelGroup id="panel">
<c:set var="id" scope="request" value ="#{otherBean.id}"/>
<ui:include src="/WEB-INF/.../form.xhtml"/>
</h:panelGroup>
What is weird is that this works just the first time I select an element in the <h:selectOneMenu>. The second time, the doSomething() method is called but the panel is not rendered (I don't know why, you know why?), so I decided to explore the following alternative that works well in both pages, but I feel that it isn't a good solution:
#{bean.init(otherBean.id)}
<ui:include src="/WEB-INF/modules/company/company.xhtml"/>
As you see, I am just calling an init method (before the <ui:include>) with the argument I need. In the controller I just set the property and do the corresponding queries:
public init(Long id) {
this.id = id;
//Do some queries
}
What do you thing about this solution?
If the form has to be initialized at start, you can use
<f:metadata>
<f:viewAction action="#{otherBean.initSomething('MYID2')}"/>
</f:metadata>
If the form has to be initialized by an action
<h:commandButton action='#{otherBean.doSomething('MYID1')}'...>
or
<f:ajax listener="#{otherBean.doSomething('MYID')}" .../>

Get form data in Backing Component from Composite Component

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.

h:commandButton inside composite is invoked but does not navigate to the specified page

I'm trying to learn JSF Composite component. I have created a registration form composite component and I have a problem with the attribute that invokes a method in my managed bean.
registrationComposite.xhtml (code snippet)
<composite:interface>
<composite:attribute name="registerButton_action"
method-signature="java.lang.String register()"/>
</composite:interface>
<composite:implementation>
<h:form>
<h:commandButton type="submit"
action="#{cc.attrs.registerButton_action}"
value="#{cc.attrs.registerButton_label}" />
</h:form>
</composite:implementation>
namespace;
xmlns:register="http://java.sun.com/jsf/composite/composite">
registration.xhtml
<register:registrationComposite
registerButton_label="Register"
registerButton_action="#{student.register()}" />
Student.class (Managedbean)
public String register() {
System.out.println("inside register method");
return "success.xhtml";
}
When I click the register button, the student's register() method is invoked but does not navigate to the returned string("success.xhtml"). Maybe it was not returning the string value? What seems to be the problem?

h:dataTable composite component, cc.attrs.var, IllegalArgumentException

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?

JSF2 composite components: are #{cc.childCount} and <composite:insertChildren/> mutually exclusive?

I just dont get it:
If I want my composite component to insert children, I use <composite:insertChildren/> but #{cc.childCount} always returns 0 in that case. On the other hand, If I do not use <composite:insertChildren/> I always get correct childCount without children being rendered. Why is that happening?
All I want to do in my component is to render some "default" panel if there are no children and do not render it in other case - behavior similar to <ui:insert name="param_name">default value</ui:insert>. So I need both insertChildren and childCount which do not seem to work together.
Here is the code:
<my:test>
<h:outputText value="child1" rendered="#{some.trueValue}"/>
<h:outputText value="child2" rendered="#{some.trueValue}"/>
<my:test>
If I use implementation below, I get 2 rendered as expected
<composite:implementation>
<h:outputText value="#{cc.childCount}"/>
</composite:implementation>
When insertChildren is used I get both children rendered and 0 at the end:
<composite:implementation>
<composite:insertChildren/>
<h:outputText value="#{cc.childCount}"/>
</composite:implementation>
Whereas my goal is:
<composite:implementation>
<composite:insertChildren/>
<h:panelGroup rendered="#{cc.childCount == 0}">
some default data
</h:panelGroup>
</composite:implementation>
Any ideas/workarounds?
Put the children in a panelGroup with an id (eg children).
Then
#{component.findComponent('children').childCount}
will give you the correct value. Good luck!
From the cc:insertChildren documentation:
Any child components or template text within the composite component tag in the using page will be re-parented into the composite component at the point indicated by this tag's placement within the section.
So by using <cc:insertChildren> you actually move the children from the #{cc} component to the component in which you specified <cc:insertChildren>, effectively making them (great)* grandchildren of #{cc}. To get easy access to these relocated children, I use a <ui:fragment> as a parent:
<ui:fragment binding="#{childContainer}">
<cc:insertChildren/>
</ui:fragment>
Now you can use #{childContainer.childCount} to get to count the children you specified for your composite component. This solution is a bit fragile though: you can only use your composite component once per view, because of the binding. This problem can of course be solved by binding a FacesComponent bean to your composite component. First the bean:
package com.stackoverflow.jsfinsertchildrenandcountexample;
import javax.faces.component.FacesComponent;
import javax.faces.component.UIComponent;
import javax.faces.component.UINamingContainer;
#FacesComponent(value = "myCompositeComponent")
public class MyCompositeComponent extends UINamingContainer {
private UIComponent childContainer;
public UIComponent getChildContainer() {
return childContainer;
}
public void setChildContainer(UIComponent childContainer) {
this.childContainer = childContainer;
}
}
And now you can bind this class to your composite component:
<?xml version='1.0' encoding='UTF-8' ?>
<ui:component
xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:cc="http://java.sun.com/jsf/composite"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
>
<cc:interface componentType="myCompositeComponent">
</cc:interface>
<cc:implementation>
I have #{cc.childContainer.childCount} children.
<ui:fragment binding="#{cc.childContainer}">
<cc:insertChildren/>
</ui:fragment>
</cc:implementation>
</ui:component>
Now you have a composite component with <cc:insertChildren> and direct access to these children.
EDIT: incorporated BalusC comment.
I had a similar problem. I switched to c:if and it worked, so in your case it would be
<composite:implementation>
<composite:insertChildren/>
<c:if test="#{cc.childCount == 0}">
some default data
</c:if>
</composite:implementation>

Resources