我最近发现了 Facelet 标记文件作为复合组件的替代品,这要归功于 BalusC 编写的许多 stackoverflow 答案,并且一直在使用它们来解决 PrimeFaces 数据表的问题。我想将 p:dataTable 的一堆杂项属性标准化为一个组件/facelet,但是一些我希望是可选的属性,比如“selectionMode”,不能评估为空字符串,否则组件会被破坏(参见http:/ /forum.primefaces.org/viewtopic.php?f=3&t=29066)。使用 Facelet 标记文件,我根据存在的可选属性有条件地呈现了备用 p:dataTables。
但是现在我对嵌套在 dataTable 中的 AjaxBehaviors 的 EL 表达式有一个新问题。
/WEB-INF/tags/dataTable.xhtml 的简化示例:
<ui:composition>
<p:dataTable var="row"
value="#{value}"
filteredValue="#{state.filteredValue}"
first="#{state.first}"
rows="#{state.rows}">
<f:event type="preRenderComponent" listener="#{state.onPreRenderComponent}"/>
<p:ajax event="page" listener="#{state.onPage}"/>
<p:ajax event="sort" listener="#{state.onSort}"/>
<p:ajax event="filter" listener="#{state.onFilter}"/>
<ui:insert/>
</p:dataTable>
</ui:composition>
使用标签文件的示例页面:
<my:dataTable value="#{mybean.list}" state="#{mybean.state}">
<p:column headerText="Name"
sortBy="#{row.name}"
filterBy="#{row.name}">
#{row.name}
</p:column>
</my:dataTable>
看起来我的“状态”表达式处理得很好,直到三个 p:ajax 事件被执行。事件被破坏(即分页/排序无效)并记录以下堆栈跟踪:
12:36:06,660 WARNING [javax.enterprise.resource.webcontainer.jsf.lifecycle] (http--0.0.0.0-8080-3) Target Unreachable, identifier 'state' resolved to null: javax.el.PropertyNotFoundException: Target Unreachable, identifier 'state' resolved to null
at org.apache.el.parser.AstValue.getTarget(AstValue.java:98) [jbossweb-7.0.13.Final.jar:]
org.apache.el.parser.AstValue.invoke(AstValue.java:244) [jbossweb-7.0.13.Final.jar:]
org.apache.el.MethodExpressionImpl.invoke(MethodExpressionImpl.java:278) [jbossweb-7.0.13.Final.jar:]
org.jboss.weld.util.el.ForwardingMethodExpression.invoke(ForwardingMethodExpression.java:39) [weld-core-1.1.5.AS71.Final.jar:2012-02-10 15:31]
org.jboss.weld.el.WeldMethodExpression.invoke(WeldMethodExpression.java:50) [weld-core-1.1.5.AS71.Final.jar:2012-02-10 15:31]
org.primefaces.component.behavior.ajax.AjaxBehaviorListenerImpl.processCustomListener(AjaxBehaviorListenerImpl.java:70) [primefaces-3.5.jar:]
org.primefaces.component.behavior.ajax.AjaxBehaviorListenerImpl.processArgListener(AjaxBehaviorListenerImpl.java:59) [primefaces-3.5.jar:]
org.primefaces.component.behavior.ajax.AjaxBehaviorListenerImpl.processAjaxBehavior(AjaxBehaviorListenerImpl.java:47) [primefaces-3.5.jar:]
org.primefaces.event.data.SortEvent.processListener(SortEvent.java:45) [primefaces-3.5.jar:]
javax.faces.component.behavior.BehaviorBase.broadcast(BehaviorBase.java:106) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
当实现为复合组件时,不存在此问题,即正确使用#{cc.attrs.state} 函数与这些AjaxBehaviors。
我对 Facelet 标记文件的范围和生命周期、它们的属性和 EL 表达式的理解是有限的。我是否遇到了 p:ajax 的错误,或者我的“状态”表达式仅在早期阶段有效?
仅供参考,“mybean”和“state”对象使用 CDI 和 CODI,定义如下:
@Named("mybean")
@ViewAccessScoped
@SuppressWarnings("serial")
public class MyBean implements Serializable {
private List<SomeEntiy> list;
@Inject
private DataTableState state;
public List<SomeEntity> getList() {
return list;
}
public DataTableState getState() {
return state;
}
// Details about loading the list hidden
}
...和...
@Dependent
@SuppressWarnings("serial")
public class DataTableState implements Serializable {
private final static Logger log = Logger.getLogger(DataTableState.class.getName());
private List<?> filteredValue;
private int first;
private int rows = 20;
private Map<String,String> filters = new HashMap<String,String>();
private ValueExpression sortBy;
private String sortOrder;
public List<?> getFilteredValue() {
return filteredValue;
}
public void setFilteredValue(List<?> filteredValue) {
this.filteredValue = filteredValue;
if (filteredValue == null && !filters.isEmpty()) {
if (log.isLoggable(Level.FINEST))
log.finest("setFilteredValue: Resetting filters");
filters.clear();
}
}
public int getFirst() {
return first;
}
public void setFirst(int first) {
this.first = first;
}
public int getRows() {
return rows;
}
public void setRows(int rows) {
if (log.isLoggable(Level.FINEST))
log.finest("setRows: " + rows);
this.rows = rows;
}
public Map<String,String> getFilters() {
return filters;
}
public void onPreRenderComponent(ComponentSystemEvent event) {
DataTable source = (DataTable) event.getSource();
if (sortBy != null)
source.setValueExpression("sortBy", sortBy);
if (sortOrder != null)
source.setSortOrder(sortOrder);
}
public void onPage(PageEvent event) {
DataTable source = (DataTable) event.getSource();
first = source.getFirst();
}
public void onSort(SortEvent event) {
UIColumn column = event.getSortColumn();
sortBy = column.getValueExpression("sortBy");
if (event.isAscending())
sortOrder = "ascending";
else
sortOrder = "descending";
}
public void onFilter(FilterEvent event) {
Map<String,String> filters = event.getFilters();
this.filters.clear();
this.filters.putAll(filters);
}
}