我想分享以下强大的“hack”,用于获取使用 p:inplace 编辑其值的实体。
我的问题是关于如何使用 p:inplace 或其他的其他现有属性更优雅地实现这一点。
该技巧改编自 BalusC 在 Stackoverflow 上的建议:
JSF(和 PrimeFaces)如何将参数传递给 ManagedBean 中的方法
那里的问题涉及获取已编辑 ap:inplace #{emp.firstName} 的 Employee 对象:
<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>
BalusC 的建议是:
假设您确实在一个重复组件中,其中 #{emp} 被其 var 属性公开,您可以从值更改侦听器内的 EL 范围中获取它,如下所示:
FacesContext context = FacesContext.getCurrentInstance();
Employee employee = context.getApplication().evaluateExpressionGet(context, "#{emp}", Employee.class);
Long id = employee.getId();
这样做的问题是 EL 表达式(在本例中为 #{emp})是硬编码的,因此不可重用。
我现在正在使用 p:inplace 的 id 成功地传播一个字符串,该字符串可以被解析为侦听器中实体的 EL 表达式,但是因为不能使用点 '。在 id 字符串中,我必须翻译每个“。” 作为一个 hiphen '-' 然后将其转换为侦听器中的有效 EL 表达式。通过这种方式,我可以传播非常复杂的 EL 表达式,然后我知道要合并到数据库的实体(或任何需要的持久性操作)。
public static String policy_convert_id_to_el_expression(String $id) {
if ($id == null) {
throw new IllegalArgumentException("null String $id");
}
return $id.replaceAll("-", ".");
}
我有一系列深值实体,它们包装特定的浅值,例如 BooleanValue、FloatQuantity 等,每个都有一个特定类型的 getValue() 方法和一个通用的瞬态 getObject() 方法,并携带有关包装的浅值的其他持久元数据(如单位、作者等)。
我有一个 ValueManager 支持 bean:
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
..
}
现在这是一种高度可重用的策略。
例如,我有一个带有 List getFloors() 的实体 OfficeBuilding,每个 BuildingFloor 都有一个深 FloatQuantity getArea() 实体,其中 FloatQuantity 扩展了 Value。
在 dataTable 中,我使用 var=floor over value=#{officeBuilding.floors} 进行迭代,并就地编辑该区域的浅层 .value ,因此:
<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>
这可以封装为高度可重用和方便的复合组件资源/util/inplaceValue.xhtml,因此:
<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>
使用上面的例子,这被称为:
<util:inplaceValue elid="floor-area" value=#{floor.area}/>
只需要记住在指定所有出现的“。”的 elid 属性时要小心。在 EL 表达式中被翻译为 hiphen “-”,但它对于各种复杂情况的作用就像一个魅力,它节省了大量的编码。它非常优雅,但它仍然是一个 hack。
问:如何更好地将访问其值已被编辑的实体所需的 EL 表达式传播给侦听器?
我还专门为此目的为 p:inplace 的新属性提出了 Primefaces 功能建议。