Composite component - change external bean value - jsf

I have got a composite component:
<cc:interface>
<cc:attribute name="value" required="true">
</cc:interface>
<cc:implementation>
<h:outputText value="#{cc.attrs.value}"/>
<h:commandButton action="#{internalBean.someAction}"/>
</cc:implementation>
And I would like to change the #{cc.attrs.value} by #{internalBean.someAction}, in other words: change the (String) value of user defined (external) bean by a method of my composite component. How I can do it?
Thanks.

UPDATE
One way I can think of is to use <f:setPropertyActionListener>.
<cc:interface>
<cc:attribute name="value" required="true"/>
</cc:interface>
<cc:implementation>
<h:outputText value="#{cc.attrs.value}"/>
<h:commandButton action="#{internalBean.someAction}">
<f:setPropertyActionListener value="#{cc.attrs.value}" target="#{internalBean.stringValueFromExternalBean}"/>
</h:commandButton>
</cc:implementation>

But it is not necessary to use StringBuilder:
<composite:interface>
<composite:attribute name="value" required="true"/>
</composite:interface>
<cc:implementation>
...
<f:setPropertyActionListener target="#{cc.attrs.value}" value="#{internalBean.value}"/>
...
</cc:implementation>
Where values are normal String. It works fine!

I have finally found the best solution ever. It works immediately as a normal component - every change updates the external bean property:
public void setValue(String value) {
this.value = value;
FacesContext facesContext = FacesContext.getCurrentInstance();
ELContext elContext = facesContext.getELContext();
ValueExpression valueExpression = facesContext.getApplication().getExpressionFactory()
.createValueExpression(elContext, "#{cc.attrs.value}", String.class);
valueExpression.setValue(elContext, value);
}

Related

JSF composite component, multiple value fields

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?

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: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?

How to find out ID for ajax render using two composite?

I have two composites and I need to render the field in the second composite when I change the name of the car, but shows this error:
<f:ajax> contains an unknown id 'nameCli' - cannot locate it in the context of the component nameCar.
Follow the code:
1ª composite car.xhtml
<composite:interface>
<composite:attribute name="id" required="true" type="java.lang.String" />
<composite:attribute name="nameCar" />
<composite:attribute name="clear" method-signature="java.lang.String action()" />
</composite:interface>
<composite:implementation>
<label>Car:</label>
<h:inputText id="nameCar" value="#{cc.attrs.nameCar}">
<!-- Here is the ajax that call the method 'clear' when the inputText changed -->
<f:ajax listener="#{cc.attrs.clear}" event="change" render="nameCli ageCli" />
</h:inputText>
</composite:implementation>
2ª composite client.xhtml
<composite:interface>
<composite:attribute name="id" required="true" type="java.lang.String" />
<composite:attribute name="nameCli" />
<composite:attribute name="ageCli" />
</composite:interface>
<composite:implementation>
<label>Client's Name:</label>
<h:inputText id="nameCli" value="#{cc.attrs.nameCli}" />
<label>Age:</label>
<h:inputText id="ageCli" value="#{cc.attrs.ageCli}" />
</composite:implementation>
The principal.xhtml
<proj:car id="car" nameCar="#{beanController.car.name}" clear="#{beanController.clear}" />
<proj:client id="client" nameCli="#{beanController.client.name}" ageCli="#{beanController.client.age}" />
And BeanController.java
private Car car;
private Client client;
public String clear() {
client.setName(null);
client.setAge(null);
return null;
}
//getters and setters
If I change the
<f:ajax listener="#{cc.attrs.clear}" event="change" render="nameCli ageCli" />
to
<f:ajax listener="#{cc.attrs.clear}" event="change" render=":client:nameCli :client:ageCli" />
doesn't work too.
I can't call to render #all or #form because there are others fields that I want to render.
Edited: Resolution
#BalusC I appreciated your comment, but your answer in other question didn't work to me. It was necessary to implement my code dynamically. Look at bellow.
In my car.xhtml composite I take off the render of ajax
<f:ajax listener="#{cc.attrs.clear}" event="change" />
And in my BeanController.java I implemented the clear method like this.
public String clear() {
client.setName(null);
client.setAge(null);
// Get the fields
UIInput uiNameCli = (UIInput) FacesContext.getCurrentInstance().getViewRoot().findComponent("myForm:client:nameCli");
UIInput uiAgeCli = (UIInput) FacesContext.getCurrentInstance().getViewRoot().findComponent("myForm:client:ageCli");
FacesContext context = FacesContext.getCurrentInstance(); // Update the fields.
context.getPartialViewContext().getRenderIds().add(uiNameCli.getClientId(context));
context.getPartialViewContext().getRenderIds().add(uiAgeCli.getClientId(context));
return null;
}

Parameterizing managed bean instance as composite component attribute

I've been searched how to parametrize the Managed Bean class, and until now nothing.
What I'm doing?
I have a JSF component that access some methods from my Bean, but this methods are already implemented by an abstract Class.
The name of the methods and attributes don't change, but now I'm copying and pasting, changing only the Bean name.
The first implementation is work, but I would like to improve with the second one.
1- Component (Now):
...
<cc:interface>
<cc:attribute name="title" type="java.lang.String" required="true"/>
<cc:attribute name="data" type="java.util.List" required="true"/>
<cc:attribute name="columnModel" type="java.util.List" required="true"/>
<cc:attribute name="newEntity" type="java.lang.Object" required="true"/>
<cc:attribute name="crudState" type="java.lang.Integer" required="true"/>
</cc:interface>
...
<cc:implementation>
<p:dataTable var="entity" value="#{cc.attrs.data}" >
<p:columns value="#{cc.attrs.columnModel}"
var="column" style="#{column.style}"
styleClass="#{column.styleClass}">
...
</p:columns>
</p:dataTable>
</cc:implementation>
...
1- Implementations (Now):
...
<comp:crud
title="#{cfgUserBean.title}"
data="#{cfgUserBean.data}"
columnModel="#{cfgUserBean.coluuns}"
newEntity="#{cfgUserBean.newEntity}"
newEntity="#{cfgUserBean.crudState}"/>
...
<comp:crud
title="#{cfgCityBean.title}"
data="#{cfgCityBean.data}"
columnModel="#{cfgCityBean.columns}"
newEntity="#{cfgCityBean.newEntity}"
crudState="#{cfgCityBean.curdState}"/>
Desired:
Pass the Bean Name as a Parameter
2- Component (Desired):
...
<cc:interface>
<cc:attribute name="BEANNAME" type="java.lang.Object" required="true"/>
</cc:interface>
...
<cc:implementation>
<p:dataTable var="entity" value="#{cc.attrs.BEANNAME.data}" >
<p:columns value="#{cc.attrs.BEANNAME.columnModel}"
var="column" style="#{column.style}"
styleClass="#{column.styleClass}">
...
</p:columns>
</p:dataTable>
</cc:implementation>
2- Implementations (Desired):
...
<comp:crud BEANNAME="cfgUserBean" />
...
<comp:crud BEANNAME="cfgCityBean" />
Conclusion
As you can see, If I could parametrize the Bean name, I would be able to simplify a lot the final coding.
Any one with any Idea what I could do?
Thank you in advance
Helped by #BalusC to clarify my ideas, and I notice that the second implementation almost answered my question.
So, that what I did:
Componet
...
<cc:interface>
<cc:attribute name="managedBean" type="company.AbstractWebCrud" required="true"/>
</cc:interface>
...
<cc:implementation>
<p:dataTable var="entity" value="#{cc.attrs.managedBean.data}" >
...
</p:dataTable>
</cc:implementation>
...
Implementation
...
<comp:crud-lista
managedBean="#{cfgUserBean}"/>
...
Conclusion
Passing the abstract class as the parameter type, I was able to access all public methods from the class. Worked like a charm!
Thank you #BalusC for attention!! :-)

Resources