如何制作JSF复合组件的网格?

发布于 2024-11-02 05:43:44 字数 590 浏览 0 评论 0原文

我在 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 中时,它们不会根据标签文本的长度进行对齐。我明白为什么,但我不知道如何解决。

I have lot's of outputLabel and inputText pairs in panelGrids

<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>

I want to have some behaviour for all of them: like same validation or same size for every inputText. So I have created a composite component which just includes an outputLabel and and an inputText

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

But now when I put them in a gridPanel, they do not get aligned depending on the length of the label text. I understand why, but I don't know how to work around.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

枫林﹌晚霞¤ 2024-11-09 05:43:44

复合组件确实被渲染为单个组件。您想改用 Facelet 标记文件。它的渲染与输出的渲染完全一样。下面是一个启动示例,假设您需要一个 3 列表单,并在第三列中包含消息字段。

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

<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 中定义,当您想要在 JAR 文件中提供要包含在/WEB-INF/lib):

<?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 的用法(当标签由 JAR 文件提供时不需要这样做)包含在/WEB-INF/lib!JSF 将自动加载 /META-INF 中的所有 *.taglib.xml 文件。

<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,其中国家/地区代码作为键,国家/地区名称作为值)

# :

在此处输入图像描述

希望这会有所帮助。

A composite component gets indeed rendered as a single component. You want to use a Facelet tag file instead. It gets rendered exactly as whatever its output renders. Here's a kickoff example assuming that you want a 3-column form with a message field in the third column.

Create tag file in /WEB-INF/tags/input.xhtml (or in /META-INF when you want to provide tags in a JAR file which is to be included in /WEB-INF/lib).

<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>

Define it in /WEB-INF/example.taglib.xml (or in /META-INF when you want to provide tags in a JAR file which is to be included in /WEB-INF/lib):

<?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>

Declare the taglib usage in /WEB-INF/web.xml (this is not needed when the tags are provided by a JAR file which is included in /WEB-INF/lib! JSF will auto-load all *.taglib.xml files from /META-INF).

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

(multiple taglib files can be separated by semicolon ;)

Finally just declare it in your main page templates.

<!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>

(the #{bean.countries} should return a Map<String, String> with country codes as keys and country names as values)

Screenshot:

enter image description here

Hope this helps.

卸妝后依然美 2024-11-09 05:43:44

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=" " 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");
        }
    }

There should have been a switch in panelGrid to render composite components separately. I have a solution for this. You can have separate composite components instead of clubbing them together. In each composite component you can use ui:fragments to demarcate the components you want to separately fall under different columns. Following is extract from my 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>

Now this will not going to align in the form which is inside the 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>

So i have extended the GroupRenderer's encodeRecursive method, to add after label and a before field:

// 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");
        }
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文