javascript
多个动态包含一个JSF标签
每個JSF開發人員都知道ui:include和ui:param標簽。 您可以包括一個facelet(XHTML文件)并傳遞一個對象,該對象將在包含的facelet中可用,如下所示:
因此,您可以在帶有dynamich列的PrimeFaces DataTable中使用它(p:columns)
<p:dataTable value="#{bean.entries}" var="data" rowKey="#{data.id}" ...>...<ui:include src="/sections/columns.xhtml"><ui:param name="data" value="#{data}"/><ui:param name="columns" value="#{bean.columns}"/></ui:include></p:dataTable>其中包含的facelet可能包含此代碼
<ui:composition xmlns="http://www.w3.org/1999/xhtml"xmlns:p="http://primefaces.org/ui"xmlns:ui="http://java.sun.com/jsf/facelets"...><p:columns value="#{columns}" var="column"><f:facet name="header"><h:outputText value="#{msgs[column.header]}"/></f:facet>// place some input / select or complex composite component for multiple data types here.// a simple example for demonstration purpose:<p:inputText value="#{data[column.property]}"/></p:columns> </ui:composition> #{bean.columns}表示描述這些列的特殊對象的列表。 我將此類對象命名為ColumnModel。 所以,這是一個
列出<ColumnModel>。 ColumnModel具有例如屬性標頭和屬性。
繼續。 現在,如果要添加對排序/過濾的支持,我們可以使用動態路徑,該路徑引用包含排序或/和過濾功能的特定facelet文件。 簡單地將src屬性綁定到bean屬性。
<ui:include src="#{bean.columnsIncludeSrc}"><ui:param name="data" value="#{data}"/><ui:param name="columns" value="#{bean.columns}"/> </ui:include>豆有類似的東西
private boolean isFilterRight; private boolean isSortRight// setter / getterpublic String getColumnsIncludeSrc() {if (isFilterRight && isSortRight) {return "/include/columnsTableFilterSort.xhtml";} else if (isFilterRight && !isSortRight) {return "/include/columnsTableFilter.xhtml";} else if (!isFilterRight && isSortRight) {return "/include/columnsTableSort.xhtml";} else {return "/include/columnsTable.xhtml";} }根據所設置的布爾權限,包含了不同的方面。 因此,將要包含的文件的決定放在Bean中。 為了更加靈活,我們可以將表封裝在一個復合組件中,并將決策邏輯移至組件類。
<cc:interface componentType="xxx.component.DataTable"><cc:attribute name="id" required="false" type="java.lang.String"shortDescription="Unique identifier of the component in a NamingContainer"/><cc:attribute name="entries" required="true"shortDescription="The data which are shown in the datatable. This is a list of object representing one row."/><cc:attribute name="columns" required="true" type="java.util.List"shortDescription="The columns which are shown in the datatable. This is a list of instances of type ColumnModel."/>... </cc:interface> <cc:implementation><p:dataTable value="#{cc.attrs.entries}" var="data" rowKey="#{data.id}" ...>...<ui:include src="#{cc.columnsIncludeSrc}"><ui:param name="data" value="#{data}"/><ui:param name="columns" value="#{cc.attrs.columns}"/></ui:include></p:dataTable> </cc:implementation>ui:include如何工作? 這是在構建視圖時應用的標記處理程序。 在JSF 2中,組件樹根據POST請求構建兩次,一次在RESTORE_VIEW階段,一次在RENDER_RESPONSE階段。 在GET上,它在RENDER_RESPONSE階段構建一次。 此行為在JSF 2規范中指定,并且在Mojarra和MyFaces中相同。 如果頁面作者使用條件包含或條件模板,則必須在RENDER_RESPONSE中建立視圖。 因此,您可以確保ui:include的src屬性在呈現階段之前不久就得到了評估。
但是到了重點! 到目前為止,我所寫的內容只是介紹了擴展ui:include的動機。 最近,我有一項任務要使用帶有動態列的ap:dataTable和p:rowEditor。 在PrimeFaces展示柜中就是這樣的 。 問題只是–這種編輯功能不支持p:columns。 我的想法是動態地多次添加p:column標簽,但是具有不同的上下文參數。 您可以將其想象為ui:include和ui:param在循環中。 在上面的示例中,我們打算遍歷List <ColumnModel>。 每次循環迭代都應在所包含的facelet中提供ColumnModel類型的實例。 因此,我編寫了一個自定義標簽處理程序以多次包含任何facelet。
package xxx.taghandler;import xxx.util.VariableMapperWrapper; import java.io.IOException; import java.util.List; import java.util.UUID; import javax.el.ExpressionFactory; import javax.el.ValueExpression; import javax.el.VariableMapper; import javax.faces.component.UIComponent; import javax.faces.view.facelets.FaceletContext; import javax.faces.view.facelets.TagAttribute; import javax.faces.view.facelets.TagAttributeException; import javax.faces.view.facelets.TagConfig; import javax.faces.view.facelets.TagHandler;/*** Tag handler to include a facelet multiple times with different contextes (objects from "value").* The attribute "value" can be either of type java.util.List or array.* If the "value" is null, the tag handler works as a standard ui:include.*/ public class InlcudesTagHandler extends TagHandler {private final TagAttribute src;private final TagAttribute value;private final TagAttribute name;public InlcudesTagHandler(TagConfig config) {super(config);this.src = this.getRequiredAttribute("src");this.value = this.getAttribute("value");this.name = this.getAttribute("name");}@Overridepublic void apply(FaceletContext ctx, UIComponent parent) throws IOException {String path = this.src.getValue(ctx);if ((path == null) || (path.length() == 0)) {return;}// wrap the original mapper - this is important when some objects passed into include via ui:param// because ui:param invokes setVariable(...) on the set variable mappper instanceVariableMapper origVarMapper = ctx.getVariableMapper();ctx.setVariableMapper(new VariableMapperWrapper(origVarMapper));try {this.nextHandler.apply(ctx, null);ValueExpression ve = (this.value != null) ? this.value.getValueExpression(ctx, Object.class) : null;Object objValue = (ve != null) ? ve.getValue(ctx) : null;if (objValue == null) {// include facelet only oncectx.includeFacelet(parent, path);} else {int size = 0;if (objValue instanceof List) {size = ((List) objValue).size();} else if (objValue.getClass().isArray()) {size = ((Object[]) objValue).length;}final ExpressionFactory exprFactory = ctx.getFacesContext().getApplication().getExpressionFactory();final String strName = this.name.getValue(ctx);// generate unique Id as a valid Java identifier and use it as variable for the provided value expressionfinal String uniqueId = "a" + UUID.randomUUID().toString().replaceAll("-", "");ctx.getVariableMapper().setVariable(uniqueId, ve);// include facelet multiple timesStringBuilder sb = new StringBuilder();for (int i = 0; i < size; i++) {if ((strName != null) && (strName.length() != 0)) {// create a new value expression in the array notation and bind it to the variable "name"sb.append("#{");sb.append(uniqueId);sb.append("[");sb.append(i);sb.append("]}");ctx.getVariableMapper().setVariable(strName,exprFactory.createValueExpression(ctx, sb.toString(), Object.class));}// included facelet can access the created above value expressionctx.includeFacelet(parent, path);// reset for next iterationsb.setLength(0);}}} catch (IOException e) {throw new TagAttributeException(this.tag, this.src, "Invalid path : " + path);} finally {// restore original mapperctx.setVariableMapper(origVarMapper);}} }最重要的調用是ctx.includeFacelet(parent,path)。 JSF API中的 includeFacelet(…)方法在相對于當前標記的某個路徑上包含了facelet標記。 類VariableMapperWrapper用于通過ui:param從名稱到值的映射。 對于帶有列的示例,在每次調用includeFacelet(...)之前,變量列還將映射到表達式#{columns [0]},#{columns [1]}等。 好吧,不完全是這些表達式,在列的位置應該是一個唯一的名稱,該名稱再次映射到columns對象(以避免可能的名稱沖突)。 映射器類如下所示
package xxx.util;import java.util.HashMap; import java.util.Map; import javax.el.ELException; import javax.el.ValueExpression; import javax.el.VariableMapper;/*** Utility class for wrapping a VariableMapper. Modifications occur to the internal Map instance.* The resolving occurs first against the internal Map instance and then against the wrapped VariableMapper* if the Map doesn't contain the requested ValueExpression.*/ public class VariableMapperWrapper extends VariableMapper {private final VariableMapper wrapped;private Map<String, ValueExpression> vars;public VariableMapperWrapper(VariableMapper orig) {super();this.wrapped = orig;}@Overridepublic ValueExpression resolveVariable(String variable) {ValueExpression ve = null;try {if (this.vars != null) {// try to resolve against the internal mapve = this.vars.get(variable);}if (ve == null) {// look in the wrapped variable mapperreturn this.wrapped.resolveVariable(variable);}return ve;} catch (Throwable e) {throw new ELException("Could not resolve variable: " + variable, e);}}@Overridepublic ValueExpression setVariable(String variable, ValueExpression expression) {if (this.vars == null) {this.vars = new HashMap<String, ValueExpression>();}return this.vars.put(variable, expression);} }在taglib XML文件中注冊標簽處理程序,您就可以完成。
<tag><tag-name>includes</tag-name><handler-class>xxx.taghandler.InlcudesTagHandler</handler-class><attribute><description><![CDATA[The relative path to a XHTML file to be include one or multiple times.]]></description><name>src</name><required>true</required><type>java.lang.String</type></attribute><attribute><description><![CDATA[Objects which should be available in the included XHTML files. This attribute can be eitherof type java.util.List or array. If it is null, the tag handler works as a standard ui:include.]]></description><name>value</name><required>false</required><type>java.lang.Object</type></attribute><attribute><description><![CDATA[The name of the parameter which points to an object of each iteration over the given value.]]></description><name>name</name><required>false</required><type>java.lang.String</type></attribute> </tag>現在我可以在復合組件中使用它了
<p:dataTable value="#{cc.attrs.entries}" var="data" rowKey="#{data.id}" ...>...<custom:includes src="#{cc.columnsIncludeSrc}" value="#{cc.attrs.columns}" name="column"><ui:param name="data" value="#{data}"/></custom:includes> </p:dataTable>典型的facelet文件(和組件樹)包含一個非常規則的p:column標記,這意味著我們能夠使用DataTable的所有功能!
<ui:composition xmlns="http://www.w3.org/1999/xhtml"xmlns:p="http://primefaces.org/ui"xmlns:ui="http://java.sun.com/jsf/facelets"...><p:column headerText="#{msgs[column.header]}"><p:cellEditor><f:facet name="output"><custom:typedOutput outputType="#{column.outputTypeName}"typedData="#{column.typedData}"value="#{data[column.property]}"timeZone="#{cc.timeZone}"calendarPattern="#{cc.calendarPattern}" locale="#{cc.locale}"/></f:facet><f:facet name="input"><custom:typedInput inputType="#{column.inputTypeName}"typedData="#{column.typedData}"label="#{column.inputTypeName}"value="#{data[column.property]}"onchange="highlightEditedRow(this)"timeZone="#{cc.timeZone}"calendarPattern="#{cc.calendarPattern}"locale="#{cc.locale}"/></f:facet></p:cellEditor></p:column> </ui:composition>
注意 :此方法可以應用于其他組件和用例。 InlcudesTagHandler可以正常運行。 例如,我可以想象在沒有基礎MenuModel的情況下在PrimeFaces中創建一個動態Menu組件。 當然,仍然需要某個模型類的列表或數組。
翻譯自: https://www.javacodegeeks.com/2013/06/multiple-dynamic-includes-with-one-jsf-tag.html
總結
以上是生活随笔為你收集整理的多个动态包含一个JSF标签的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java EE CDI依赖关系消歧示例
- 下一篇: 电脑怎么进行屏幕录像如何录电脑屏幕