web-dev-qa-db-ja.com

JSF複合コンポーネントのグリッドを作成するにはどうすればよいですか?

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に配置すると、ラベルテキストの長さによっては整列しません。理由はわかりますが、回避方法がわかりません。

23
Anatoli

複合コンポーネントは、実際には単一のコンポーネントとしてレンダリングされます。代わりにFaceletタグファイルを使用します。出力がレンダリングするものとまったく同じようにレンダリングされます。これは、3列目にメッセージフィールドがある3列のフォームが必要であると想定したキックオフの例です。

_/WEB-INF/tags/input.xhtml_(または、_/META-INF_に含まれるJARファイルにタグを提供する場合は_/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}&#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_(または_/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の使用法を宣言します(タグが_/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>_を返す必要があります)

スクリーンショット:

enter image description here

お役に立てれば。

44
BalusC

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