Описание тега composite-component

"Составной компонент" - это специальный термин JSF 2.x / Facelets для повторно используемых компонентов пользовательского интерфейса, которые объявлены с использованием чистого XML, а не Java. Теги объявления XML составного компонента доступны в пространстве имен http://xmlns.jcp.org/jsf/ композит.

"Составной компонент" - это специальный термин JSF 2.x / Facelets для многократно используемых компонентов пользовательского интерфейса, которые объявляются с использованием чистого XML, а не Java. Теги объявления XML составного компонента доступны в пространстве имен http://xmlns.jcp.org/jsf/composite. До JSF 2.2 пространство имен http://java.sun.com/jsf/composite следует использовать вместо этого.

Создание составных компонентов

Подготовить структуру каталогов

Сначала создайте каталог resources в общедоступном веб-контенте (там, где WEB-INF каталог и все обычные файлы Facelets тоже есть).

WebContent
 |-- WEB-INF
 |    `-- lib
 |-- resources   <---
 `-- test.xhtml

JSF 2.2 и более поздних версий позволяет изменить каталог ресурсов, указав параметр с именем javax.faces.WEBAPP_RESOURCES_DIRECTORYв файле web.xml. Может быть разумным изменить каталог, например, на/WEB-INF/resources, поскольку файлы в /WEB-INF не читаются через HTTP.

В каталоге ресурсов создайте каталог исключительно для составных компонентов. Имя каталога заканчивается как дополнительный путь в URI пространства имен составного компонента. Вы можете выбрать имя. Мы возьмемmycomponents В качестве примера.

WebContent
 |-- WEB-INF
 |    `-- lib
 |-- resources
 |    `-- mycomponents   <---
 `-- test.xhtml

Таким образом, составные компоненты в этом каталоге будут доступны во всех шаблонах в следующем пространстве имен:

<html xmlns:my="http://xmlns.jcp.org/jsf/composite/mycomponents">

Префикс myсвободен на ваш выбор. Вhttp://xmlns.jcp.org/jsf/composite/часть является обязательной. Вmycomponents часть должна быть такой же, как имя каталога.

В качестве примера составного компонента Hello World мы создадим составной компонент, который показывает простой рейтинг со звездами. Нам нужно создать новый файл XHTML. Имя файла становится именем тега составного компонента. Вы можете выбрать имя. Назовем этоrating.xhtml.

WebContent
 |-- WEB-INF
 |    `-- lib
 |-- resources
 |    `-- mycomponents
 |         `-- rating.xhtml   <---
 `-- test.xhtml

Таким образом, составной компонент доступен во всех шаблонах следующим образом:

<my:rating />

Создать составной компонент

Вот как выглядит базовый шаблон составного компонента. Поместите это вrating.xhtml.

<ui:component
    xmlns="http://www.w3.org/1999/xhtml"
    xmlns:f="http://xmlns.jcp.org/jsf/core"
    xmlns:h="http://xmlns.jcp.org/jsf/html"
    xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
    xmlns:cc="http://xmlns.jcp.org/jsf/composite"
>
    <cc:interface>
        <!-- Define component attributes here -->
    </cc:interface>
    <cc:implementation>
        <!-- Define component body here -->
    </cc:implementation>
</ui:component>

Мы хотели бы определить следующие атрибуты:

  • score, целое, обязательное. Звездный счет.
  • maxScore, целое число, необязательный, по умолчанию 100. Максимально возможный балл.
  • totalStars, целое число, необязательный, по умолчанию 10. Общее количество отображаемых звездочек.

Вот как выглядит окончательная реализация. Обратите внимание, что атрибуты доступны по#{cc.attrs.attributename}.

<ui:component 
    xmlns="http://www.w3.org/1999/xhtml"
    xmlns:f="http://xmlns.jcp.org/jsf/core"
    xmlns:h="http://xmlns.jcp.org/jsf/html"
    xmlns:cc="http://xmlns.jcp.org/jsf/composite"
    xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
    xmlns:c="http://xmlns.jcp.org/jsp/jstl/core"
    xmlns:fn="http://xmlns.jcp.org/jsp/jstl/functions"
>
    <cc:interface>
        <cc:attribute name="score" type="java.lang.Integer" required="true" />
        <cc:attribute name="maxScore" type="java.lang.Integer" default="100" />
        <cc:attribute name="totalStars" type="java.lang.Integer" default="10" />
    </cc:interface>
    <cc:implementation>
        <c:set var="filled" value="#{fn:substringBefore(cc.attrs.score / (cc.attrs.maxScore / cc.attrs.totalStars), '.')}" />
        <span style="font-size: 1.5em;">
            <c:forEach begin="1" end="#{cc.attrs.totalStars}" varStatus="loop">
                <h:outputText value="&#9733;" rendered="#{loop.index le filled}" />            
                <h:outputText value="&#9734;" rendered="#{loop.index gt filled}" />            
            </c:forEach>
        </span> 
    </cc:implementation>
</ui:component>

Вот как вы можете использовать это в test.xhtml:

<!DOCTYPE html>
<html lang="en"
    xmlns="http://www.w3.org/1999/xhtml"
    xmlns:f="http://xmlns.jcp.org/jsf/core"
    xmlns:h="http://xmlns.jcp.org/jsf/html" 
    xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
    xmlns:my="http://xmlns.jcp.org/jsf/composite/mycomponents"
>
    <h:head>
        <title>Rating composite component demo</title>
    </h:head>
    <h:body>
        <my:rating score="60" /><br />
        <my:rating score="5" maxScore="10" /><br />
        <my:rating score="80" totalStars="5" /><br />
    </h:body>
</html>

Вот как должен выглядеть результат (только если ваш браузер поддерживает шрифты со звездочкой Unicode; вы можете заменить их реальными изображениями или даже ввести половину звезды, что, к сожалению, недоступно в Unicode):

★★★★★★☆☆☆☆

★★★★★☆☆☆☆☆

★★★★☆

Создать компонент поддержки

Вышеупомянутая реализация имеет один недостаток из-за использования JSTL. <c:forEach>: поскольку JSTL запускается во время сборки представления вместо времени визуализации представления, указанная выше реализация не может использоваться внутри повторяющегося компонента, такого как <h:dataTable> или <ui:repeat>. Мы хотели бы использовать<ui:repeat> вместо этого, но он не поддерживает begin а также endатрибуты. Итак, мы хотели бы прикрепить код Java, чтобы он преобразовывалtotalStars в пустой массив объектов точно такого размера, чтобы его можно было использовать в valueатрибут. В идеале это можно было бы сделать с помощью функции EL, но для целей обучения / вики мы будем использовать вместо этого так называемый "компонент поддержки".

Чтобы создать такой компонент поддержки, нам нужно создать класс, который расширяет UINamingContainer или хотя бы орудия NamingContainer и возвращается UINamingContainer.COMPONENT_FAMILY в getFamily()метод. Вот базовый шаблон:

package com.example;

import javax.faces.component.FacesComponent;
import javax.faces.component.UINamingContainer;

@FacesComponent("myCompositeComponent")
public class MyCompositeComponent extends UINamingContainer {

    // ...

}

Обратите внимание на значение @FacesComponentатрибут. Это тот, который вы должны указать вcomponentType атрибут <cc:interface> тег:

<cc:interface componentType="myCompositeComponent">

Таким образом, экземпляр компонента поддержки будет использоваться за #{cc}вместо этого переменная. Это дает вам возможность определять методы получения и действия, такие какvalue="#{cc.items}", action="#{cc.doSomething}"и так далее. Все из<cc:attribute> атрибуты доступны в компоненте поддержки унаследованным UIComponent#getAttributes() метод, обеспечивающий легкий доступ к атрибутам.

Для нашего составного компонента рейтинга реализация компонента поддержки должна выглядеть следующим образом:

package com.example;

import javax.faces.component.FacesComponent;
import javax.faces.component.UINamingContainer;

@FacesComponent("ratingComponent")
public class RatingComponent extends UINamingContainer {

    public Object[] getItems() {
        Object totalStars = getAttributes().get("totalStars");
        int size = Integer.valueOf(String.valueOf(totalStars));
        return new Object[size];
    }

}

А вот как rating.xhtml теперь должно выглядеть так:

<ui:component 
    xmlns="http://www.w3.org/1999/xhtml"
    xmlns:f="http://xmlns.jcp.org/jsf/core"
    xmlns:h="http://xmlns.jcp.org/jsf/html"
    xmlns:cc="http://xmlns.jcp.org/jsf/composite"
    xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
    xmlns:c="http://xmlns.jcp.org/jsp/jstl/core"
    xmlns:fn="http://xmlns.jcp.org/jsp/jstl/functions"
>
    <cc:interface componentType="ratingComponent">
        <cc:attribute name="score" type="java.lang.Integer" required="true" />
        <cc:attribute name="maxScore" type="java.lang.Integer" default="100" />
        <cc:attribute name="totalStars" type="java.lang.Integer" default="10" />
    </cc:interface>
    <cc:implementation>
        <c:set var="filled" value="#{fn:substringBefore(cc.attrs.score / (cc.attrs.maxScore / cc.attrs.totalStars), '.')}" />
        <span style="font-size: 1.5em;">
            <ui:repeat value="#{cc.items}" varStatus="loop">
                <h:outputText value="&#9733;" rendered="#{loop.index lt filled}" />            
                <h:outputText value="&#9734;" rendered="#{loop.index ge filled}" />            
            </ui:repeat>
        </span>
    </cc:implementation>
</ui:component>

Обратите внимание value="#{cc.items}"в приведенном выше примере. Это в основном вызываетgetItems()в экземпляре компонента поддержки. Также обратите внимание, что мы избавились от JSTL<c:forEach>, поэтому приведенное выше будет правильно работать внутри повторяющегося компонента JSF, такого как <ui:repeat>, <h:dataTable> и так далее.

<h:dataTable value="#{bean.products}" var="product">
    <h:column>#{product.name}</h:column>
    <h:column><my:rating score="#{product.rating}" /></h:column>
</h:dataTable>

Интернет-ресурсы:

Информационные страницы связанных тегов: