Primefaces p:inplace: How to more elegantly propagate the EL expression for entity to merge

只愿长相守 提交于 2020-01-11 04:11:05

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!