如何使JSF复合组件的网格?

我在panelGrid中有很多的outputLabel和inputText对

<h:panelGrid columns="2"> <h:outputLabel value="label1" for="inputId1"/> <h:inputText id="inputId1/> <h:outputLabel value="label2" for="inputId2"/> <h:inputText id="inputId2/> ... </h:panelGrid> 

我想对他们有一些行为:像每个inputText相同的验证或相同的大小。 所以我创建了一个只包含一个outputLabel和一个inputText的复合组件

 <my:editField value="field1"/> <my:editField value="field2"/> 

但现在,当我把它们放在gridPanel中时,它们不会根据标签文本的长度而被对齐。 我明白为什么,但我不知道如何解决。

一个复合组件确实呈现为一个单一的组件。 您想改为使用Facelet标记文件。 它完全按照输出呈现的方式呈现。 假设你想在第三列中有一个消息字段的3列表单,这里是一个启发式的例子。

/WEB-INF/tags/input.xhtml创建标记文件(或者当您想要在/WEB-INF/lib包含的JAR文件中提供标记时,在/META-INF )。

 <ui:composition xmlns:c="http://java.sun.com/jsp/jstl/core" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:ui="http://java.sun.com/jsf/facelets"> <c:set var="id" value="#{not empty id ? id : (not empty property ? property : action)}" /> <c:set var="required" value="#{not empty required and required}" /> <c:choose> <c:when test="#{type != 'submit'}"> <h:outputLabel for="#{id}" value="#{label}&#160;#{required ? '*&#160;' : ''}" /> </c:when> <c:otherwise> <h:panelGroup /> </c:otherwise> </c:choose> <c:choose> <c:when test="#{type == 'text'}"> <h:inputText id="#{id}" value="#{bean[property]}" label="#{label}" required="#{required}"> <f:ajax event="blur" render="#{id}-message" /> </h:inputText> <h:message id="#{id}-message" for="#{id}" /> </c:when> <c:when test="#{type == 'password'}"> <h:inputSecret id="#{id}" value="#{bean[property]}" label="#{label}" required="#{required}"> <f:ajax event="blur" render="#{id}-message" /> </h:inputSecret> <h:message id="#{id}-message" for="#{id}" /> </c:when> <c:when test="#{type == 'select'}"> <h:selectOneMenu id="#{id}" value="#{bean[property]}" label="#{label}" required="#{required}"> <f:selectItems value="#{options.entrySet()}" var="entry" itemValue="#{entry.key}" itemLabel="#{entry.value}" /> <f:ajax event="change" render="#{id}-message" /> </h:selectOneMenu> <h:message id="#{id}-message" for="#{id}" /> </c:when> <c:when test="#{type == 'submit'}"> <h:commandButton id="#{id}" value="#{label}" action="#{bean[action]}" /> <h:message id="#{id}-message" for="#{id}" /> </c:when> <c:otherwise> <h:panelGroup /> <h:panelGroup /> </c:otherwise> </c:choose> </ui:composition> 

/WEB-INF/example.taglib.xml定义它(或者当你想在要包含在/WEB-INF/lib中的JAR文件中提供标记时,在/META-INF ):

 <?xml version="1.0" encoding="UTF-8"?> <facelet-taglib xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facelettaglibrary_2_0.xsd" version="2.0"> <namespace>http://example.com/jsf/facelets</namespace> <tag> <tag-name>input</tag-name> <source>tags/input.xhtml</source> </tag> </facelet-taglib> 

/WEB-INF/web.xml声明taglib的用法(当/WEB-INF/lib包含的JAR文件提供标记时,这是不需要的)JSF将自动加载所有*.taglib.xml文件来自/META-INF )。

 <context-param> <param-name>javax.faces.FACELETS_LIBRARIES</param-name> <param-value>/WEB-INF/example.taglib.xml</param-value> </context-param> 

(多个taglib文件可以用分号隔开;

最后在你的主页面模板中声明它。

 <!DOCTYPE html> <html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:my="http://example.com/jsf/facelets" > <h:head> <title>Facelet tag file demo</title> </h:head> <h:body> <h:form> <h:panelGrid columns="3"> <my:input type="text" label="Username" bean="#{bean}" property="username" required="true" /> <my:input type="password" label="Password" bean="#{bean}" property="password" required="true" /> <my:input type="select" label="Country" bean="#{bean}" property="country" options="#{bean.countries}" /> <my:input type="submit" label="Submit" bean="#{bean}" action="submit" /> </h:panelGrid> </h:form> </h:body> </html> 

#{bean.countries}应该返回一个Map<String, String> ,国家代码作为键和国家名称作为值)

截图:

在这里输入图像描述

希望这可以帮助。

应该在panelGrid中有一个开关来分别渲染复合组件。 我有这个解决方案。 你可以有独立的复合组件,而不是将它们聚合在一起。 在每个复合组件中,您可以使用ui:fragments来划分要分别放在不同列中的组件。 以下是从我的inputText.xhtml摘录:

 <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:composite="http://java.sun.com/jsf/composite" xmlns:ui="http://java.sun.com/jsf/facelets"> <composite:interface> <composite:attribute name="id" /> <composite:attribute name="value" /> <composite:attribute name="label" /> <composite:attribute name="readonly" /> <composite:attribute name="disabled" /> <composite:attribute name="required" /> </composite:interface> <composite:implementation> <ui:fragment id="label"> <h:outputText id="#{cc.attrs.id}Label" value="#{cc.attrs.label}" for="#{cc.attrs.id}" /> <h:outputLabel value="#{bundle['label.STAR']}" rendered="#{cc.attrs.required}" styleClass="mandatory" style="float:left"></h:outputLabel> <h:outputLabel value="&nbsp;" rendered="#{!cc.attrs.required}" styleClass="mandatory"></h:outputLabel> </ui:fragment> <ui:fragment id="field"> <h:inputText id="#{cc.attrs.id}" value="#{cc.attrs.value}" styleClass="#{not component.valid ? 'errorFieldHighlight medium' : 'medium'}" disabled="#{cc.attrs.disabled}" required="#{cc.attrs.required}" label="#{cc.attrs.label}" readonly="#{cc.attrs.readonly}"> </h:inputText> </ui:fragment> </composite:implementation> </html> 

现在,这将不会在panelGrid中的形式对齐:

 <h:panelGrid width="100%"> <my:inputText label="#{bundle['label.fname']}" value="#{bean.fname}" id="fname"></my:inputtext> <my:inputText label="#{bundle['label.lname']}" value="#{bean.lname}" id="lname"></my:inputtext> </panelGrid> 

所以我扩展了GroupRenderer的encodeRecursive方法,在标签和前一个字段之后添加:

 // inside my extended renderer protected void encodeRecursive(FacesContext context, UIComponent component) throws IOException { // Render our children recursively if (component instanceof ComponentRef && component.getId().equals("field")) { context.getResponseWriter().startElement("td", component); } super.encodeRecursive(context, component); if (component instanceof ComponentRef && component.getId().equals("label")) { context.getResponseWriter().endElement("td"); } }