问题
I would like to share the following powerful "hack" for getting at an entity whose value as been edited using p:inplace.
My question is about how to achieve this more elegantly using other existing attributes of p:inplace or otherwise.
The trick is adapted from a suggestion by BalusC on Stackoverflow:
JSF (and PrimeFaces) How to pass parameter to a method in ManagedBean
The question there involves getting at an Employee object that has a p:inplace edited #{emp.firstName}:
<h:outputText value="First name:"/>
<p:inplace id="firstname" editor="true">
<p:ajax event="save" onsuccess="#{employeeController.saveName()}"/>
<p:inputText id="firstName" value="#{emp.firstName}"
required="true" label="text"
valueChangeListener="#{employeeController.firstNameChanged}">
<p:ajax event="valueChange" listener="#{employeeController.onValueChangedStart}"/>
</p:inputText>
</p:inplace>
The suggestion from BalusC is:
Assuming that you're indeed inside a repeating component where #{emp} is been exposed by its var attribute, you could just grab it from the EL scope inside the value change listener as follows:
FacesContext context = FacesContext.getCurrentInstance();
Employee employee = context.getApplication().evaluateExpressionGet(context, "#{emp}", Employee.class);
Long id = employee.getId();
The problem with this is that the EL expression (in this case #{emp}) is hard-coded, so this is not reusable.
I am now using the id of the p:inplace successfully to propagate a String that can be parsed as an EL expression for an entity within a listener, however since one can't use a dot '.' in an id String, I have to translate every '.' as a hiphen '-' and then convert this to a valid EL expression in the listener. This way I can propagate very complicated EL expressions and then I know what entity to merge to database (or whatever persistence operation is required).
public static String policy_convert_id_to_el_expression(String $id) {
if ($id == null) {
throw new IllegalArgumentException("null String $id");
}
return $id.replaceAll("-", ".");
}
I have a family of deep Value entities that wrap specific shallow values such as BooleanValue, FloatQuantity etc., each with a specifically typed getValue() method, and a generic transient getObject() method, and carrying other persistent metadata about the wrapped shallow value (such as units, author etc.).
I have a ValueManager backing bean with:
public void onInplaceSaveEvent(AjaxBehaviorEvent ae) {
UIComponent component = ae.getComponent();
String id = component.getId();
String $el = policy_convert_id_to_el_expression(id);
FacesContext context = FacesContext.getCurrentInstance();
Value v = context.getApplication().evaluateExpressionGet(context, "#{" + $el + "}", Value.class);
..
// merge Value entity to database (catches change to shallow wrapped value)
// and/or perform other persistence operations such as metadata updates
..
}
This is now a highly reusable strategy.
For example, I have an entity OfficeBuilding with List getFloors(), and each BuildingFloor has a deep FloatQuantity getArea() entity, where FloatQuantity extends Value.
In a dataTable I iterate with var=floor over value=#{officeBuilding.floors} and edit inplace the shallow .value of the area thus:
<p:inplace
id="floor-area"
editor="true"
emptyLabel="UNDEF"
>
<p:ajax
event="save"
update="@form"
listener="#{valueManager.onInplaceSaveEvent}"
process="@this floor-area-value"
/>
<p:inputText
id="floor-area-value"
value="#{floor.area.value}"
/>
</p:inplace>
This can be encapsulated as a highly reusable and convenient composite component resources/util/inplaceValue.xhtml thus:
<cc:interface>
<cc:attribute name="value" type="com.example.entity.value.Value" required="true"/>
<cc:attribute name="elid" required="true"/>
</cc:interface>
<cc:implementation>
<p:inplace
id="#{cc.attrs.elid}"
editor="true"
emptyLabel="UNDEF"
>
<p:ajax
event="save"
update="@form"
listener="#{valueManager.onInplaceSaveEvent}"
process="@this #{cc.attrs.elid}-value"
/>
<p:inputText
id="#{cc.attrs.elid}-value"
value="#{cc.attrs.value.value}"
/>
</p:inplace>
</cc:implementation>
Using the example above this is called as:
<util:inplaceValue elid="floor-area" value=#{floor.area}/>
One just has to remember to be careful in specifying the elid attribute that all occurrences of "." in the EL expression are translated to hiphen "-", but it works like a charm for a wide range of complex cases, and it saves a huge amount of coding. It is quite elegant, but it is still a hack.
Q: How might one better propagate to the listener the EL expression needed to access the entity whose value has been edited ?
I have also made a Primefaces feature suggestion for a new attribute for p:inplace specifically for this purpose.
来源:https://stackoverflow.com/questions/11405613/primefaces-pinplace-how-to-more-elegantly-propagate-the-el-expression-for-enti