我在panelGrids
中有很多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中时,根据标签文本的长度,它们不会对齐。我理解为什么,但我不知道如何解决。
答案 0 :(得分:44)
复合组件确实呈现为单个组件。您想要使用Facelet标记文件。它的呈现方式与输出呈现的内容完全相同。这是一个启动示例,假设您需要一个包含第三列中的消息字段的3列表单。
在/WEB-INF/tags/input.xhtml
(或/META-INF
中创建标记文件时,如果要在/WEB-INF/lib
中包含的JAR文件中提供标记。)
<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} #{required ? '* ' : ''}" />
</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
(或/META-INF
中定义,如果要在/WEB-INF/lib
中包含的JAR文件中提供标记时):
<?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>
,国家/地区代码为国家/地区代码,国家/地区名称为值
截图:
希望这有帮助。
答案 1 :(得分:4)
panelGrid中应该有一个开关来分别渲染复合组件。我有一个解决方案。您可以使用单独的复合组件,而不是将它们组合在一起。在每个复合组件中,您可以使用ui:fragment来划分您想要分别位于不同列下的组件。以下是我的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=" " 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方法,添加标签之后和before字段:
// 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");
}
}