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.
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.
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 the following composite component (relevant code only):
<cc:interface>
<cc:attribute name="message" type="com.virtuafisica.business.problemsmgmt.entity.Message" required="true" />
<cc:attribute name="notifiedListener" method-signature="void listener(javax.faces.event.AjaxBehaviorEvent)" />
</cc:interface>
<cc:implementation>
<h:selectBooleanCheckbox value="#{cc.attrs.message.notified}">
<f:attribute name="msg" value="#{cc.attrs.message}" />
<c:if test="#{cc.getValueExpression('notifiedListener') != null}">
<f:ajax event="change" listener="#{cc.attrs.notifiedListener}" />
</c:if>
</h:selectBooleanCheckbox>
</cc:implementation>
This composite is called from a Facelet:
<my:message message="#{msg}" notifiedListener="#{problem.changeNotified}"/>
And the change event is handled like this in a backing bean:
public void changeNotified(AjaxBehaviorEvent event) {
Message message = (Message) event.getComponent().getAttributes().get("msg");
if (message == null) {
logger.log(Level.FINEST, "Null message!");
return;
}
....
}
The problem is that message is null in the backing bean, although the event.getComponent() is the right one (is the HTMLSelectBooleanCheckbox).
Why isn't the attribute set?
Thank you guys
The attribute is set (you can confirm it by determining that getValueExpression("msg") didn't return null), but the attribute value simply evaluated null. Deferred expressions are evaluated at exactly the moment when you want to get the value. So, the #{cc.attrs.message} is evaluated at exactly the moment you call get("msg") inside the ajax listener method. However, at that point, #{cc} doesn't exist anywhere in the EL scope.
The workaround to exactly that technical problem would be to manually put it back in the EL scope (and cleanup afterwards!).
UIComponent component = event.getComponent();
UIComponent cc = UIComponent.getCompositeComponentParent(component);
Map<String, Object> requestScope = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap();
try {
requestScope.put("cc", cc);
Message message = (Message) component.getAttributes().get("msg");
}
finally {
requestScope.remove("cc");
}
Another workaround would be to just obtain it directly from the composite.
UIComponent cc = UIComponent.getCompositeComponentParent(event.getComponent());
Message message = (Message) cc.getAttributes().get("msg");
Either way, this is only rather clumsy as the backing bean should not need to know that the ajax method is called by a composite. Simply put, you've there a design problem.
Consider creating a backing component and delegating the ajax call from there, so that you can directly pass the Message as method argument.
#FacesComponent("yourCompositeName")
public class YourComposite extends UINamingContainer {
public void notifiedListener(AjaxBehaviorEvent event) {
MethodExpression notifiedListener = (MethodExpression) getAttributes().get("notifiedListener");
Message message = (Message) getAttributes().get("message");
notifiedListener.invoke(getFacesContext().getELContext(), new Object[] { message });
}
}
Register it as <cc:interface componentType>, alter the method attribute to take the Message argument and let the <f:ajax> call the listener method on the above backing component, which will in turn delegate to the method attribute.
<cc:interface componentType="yourCompositeName">
<cc:attribute name="message" type="com.virtuafisica.business.problemsmgmt.entity.Message" required="true" />
<cc:attribute name="notifiedListener" method-signature="void listener(com.virtuafisica.business.problemsmgmt.entity.Message)" />
</cc:interface>
<cc:implementation>
<h:selectBooleanCheckbox value="#{cc.attrs.message.notified}">
<c:if test="#{cc.getValueExpression('notifiedListener') != null}">
<f:ajax listener="#{cc.notifiedListener}" />
</c:if>
</h:selectBooleanCheckbox>
</cc:implementation>
(note: I removed event="change" as that's the wrong one for checkboxes/radiobuttons and the default value is valueChange already which is the right one — it evaluates to event="click" for checkboxes/radiobuttons)
Now you can have just this in bean.
public void changeNotified(Message message) {
// ...
}
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
}