Составной JSF: actionSource во вложенных составных компонентах

Согласно документации JSF для <composite:interface>:

Вложение составных компонентов

Реализация должна поддерживать вложение составных компонентов. В частности, должна быть возможность, чтобы раздел составного компонента действовал как страница использования для другого составного компонента. Когда составной компонент предоставляет поведенческий интерфейс используемой странице, такой как , или другой поведенческий интерфейс, должна быть возможность «распространить» доступность такого интерфейса в случае вложенного составного компонента. Автор составного компонента должен убедиться, что значения атрибутов имени точно совпадают на всех уровнях вложенности, чтобы эта экспозиция работала. Реализация не обязана поддерживать «переназначение» имен во вложенном составном компоненте.

Далее показан пример вложения <composite:actionSource>, однако я тестировал почти такой же пример, и он не работает. Вот мой код:

Внутренний составной компонент:

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml"
  xmlns:ui="http://java.sun.com/jsf/facelets"
  xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core"
  xmlns:composite="http://java.sun.com/jsf/composite">

<composite:interface>
  <composite:attribute name="actionText" 
    type="java.lang.String" default="nestedastest action" />
  <composite:actionSource name="someaction" />
</composite:interface>

<composite:implementation>
  <h:commandLink id="someaction">#{cc.attrs.actionText}</h:commandLink>
</composite:implementation>
</html>

Внешний составной компонент:

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml"
  xmlns:ui="http://java.sun.com/jsf/facelets"
  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:test="http://java.sun.com/jsf/composite/components/test">

<composite:interface name="nestedActionTester"
  displayName="A test composite component for nested actions">
  <composite:attribute name="actionText" 
    type="java.lang.String" default="astest action" />
  <composite:actionSource name="someaction" />
</composite:interface>

<composite:implementation>
  <test:nestedastest actionText="#{cc.attrs.actionText}" />
</composite:implementation>
</html>

Фейслет:

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml"
  xmlns:ui="http://java.sun.com/jsf/facelets"
  xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core"
  xmlns:test="http://java.sun.com/jsf/composite/components/test">

<h:head />
<h:body>
  <ui:composition template="template.xhtml">
    <ui:define name="content">
      <h:form id="communityMembersForm">
        <div>
          <test:astest>
            <f:actionListener for="someaction"
              binding="#{testController.someActionActionListener}" />
          </test:astest>
        </div>
        <div>
          <test:nestedastest>
            <f:actionListener for="someaction"
              binding="#{testController.someActionActionListener}" />
          </test:nestedastest>
        </div>
      </h:form>
    </ui:define>
  </ui:composition>
</h:body>
</html>

Управляемый компонент:

package test.controller;


import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.ActionEvent;
import javax.faces.event.ActionListener;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


@ManagedBean
@ViewScoped
public class TestController {
    private static Logger logger = 
            LoggerFactory.getLogger( TestController.class );

    public ActionListener getSomeActionActionListener() {
        return new ActionListener() {
            @Override
            public void processAction( ActionEvent event ) 
                    throws AbortProcessingException {
                logger.debug( "someaction occurred..." );
            }

        };
    }
}

Я использую Мохарру 2.1.13:

<dependency>
  <groupId>com.sun.faces</groupId>
  <artifactId>jsf-api</artifactId>
  <version>2.1.13</version>
  <type>jar</type>
  <scope>compile</scope>
</dependency>
<dependency>
  <groupId>com.sun.faces</groupId>
  <artifactId>jsf-impl</artifactId>
  <version>2.1.13</version>
  <type>jar</type>
  <scope>runtime</scope>
</dependency>

На коте 6.0.32:

<dependency>
  <groupId>org.apache.tomcat</groupId>
  <artifactId>el-api</artifactId>
  <version>6.0.32</version>
  <type>jar</type>
  <scope>provided</scope>
</dependency>
<dependency>
  <groupId>org.apache.tomcat</groupId>
  <artifactId>servlet-api</artifactId>
  <version>6.0.32</version>
  <type>jar</type>
  <scope>provided</scope>
</dependency>

Когда я запускаю это приложение, ссылка для nestedastest action (это ссылка, которая напрямую использует внутренний составной компонент) вызывает запуск прослушивателя действий и печать сообщения журнала. Однако при нажатии на astest action (внешний составной компонент) ничего не происходит. Учитывая, что это почти точно пример, показанный в официальном javadoc JSF, я ожидаю, что это сработает. Любая идея, почему это не так?

---------- РЕДАКТИРОВАТЬ ---------

Я обнаружил, что если я изменю внешний составной компонент таким образом:

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml"
  xmlns:ui="http://java.sun.com/jsf/facelets"
  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:test="http://java.sun.com/jsf/composite/components/test">

<composite:interface name="nestedActionTester"
  displayName="A test composite component for nested actions">
  <composite:attribute name="actionText" 
    type="java.lang.String" default="astest action" />
  <composite:actionSource name="someaction" targets="inner" />
</composite:interface>

<composite:implementation>
  <test:nestedastest id="inner" actionText="#{cc.attrs.actionText}" />
</composite:implementation>
</html>

Обратите внимание, что я добавил атрибут target в actionSource и соответствующий идентификатор во вложенный составной компонент

Теперь он правильно свяжет действие. Это имеет смысл, но документация заставляет вас поверить, что в этом нет необходимости. Это ошибка в документации? Или в реализации (пробовал и на Мохарре 2.1.13 и на MyFaces 2.1.10)? Или в моем понимании?


person Lucas    schedule 30.11.2012    source источник
comment
Попробуйте с помощью MyFaces проверить, является ли ошибка в Mojarra или нет. По идее так и должно быть.   -  person lu4242    schedule 01.12.2012
comment
@lu4242 lu4242, когда я изменил проект на myfaces (2.1.10), он не смог загрузить страницу с этим: /xhtml/astestmain.xhtml at line 17 and column 70 <f:actionListener> Parent is not composite component or of type ActionSource, type is: javax.faces.component.html.HtmlForm@1ebdcc9a что вы можете видеть в разделе Facelet выше, родительский элемент является составным компонентом, < i>и у него есть <f:actionSource name='someaction'>   -  person Lucas    schedule 01.12.2012
comment
@lu4242 игнорируйте последний комментарий, я только что нашел MYFACES-3454   -  person Lucas    schedule 01.12.2012
comment
@lu4242 lu4242 Я убедился, что в myfaces такое же поведение. Я также проверил, что если я помещу идентификатор в <test:nestedastest id='inner' ... />, а затем добавлю атрибут target в <composite:actionSource name='someaction' targets='inner' />, он пройдет, как и ожидалось. Возможно, документация просто не совсем верна, и вы должны включить цели и значения идентификатора, чтобы связать ее?   -  person Lucas    schedule 01.12.2012
comment
Я могу утверждать, что документация верна. В этом случае, чтобы избежать объявления целей, необходимо установить идентификатор с тем же именем, чтобы использовать его в объявлении for, но если он вложен, необходимо сделать это по всей цепочке для всех связанных составных компонентов, потому что каждый составной компонент является NamingContainer, что означает, что он смотрит только на связанные компоненты.   -  person lu4242    schedule 01.12.2012
comment
@ lu4242, Хорошо, у меня есть идентификатор совпадения actionSource. И поскольку внутренний источник действия cc на самом деле нацелен на несколько компонентов, я не могу добиться совпадения id самих компонентов. Итак, вы говорите, что, поскольку фактический конечный компонент не имеет того же id, что и все actionSource в цепочке, я должен использовать targets полностью? Если да, пожалуйста, сделайте свой ответ ответом, чтобы я мог его принять.   -  person Lucas    schedule 03.12.2012


Ответы (1)


Алгоритм пытается найти по умолчанию компонент с тем же идентификатором, что и тот, который определен именем cc:actionSource на каждом вложенном уровне. В этом случае идентификатор компонента определяется только на внутреннем уровне. Если вы не хотите использовать один и тот же идентификатор для каждого компонента на всем протяжении, вы можете использовать атрибут «targets», чтобы указать алгоритм, о котором говорит компонент, и передать actionListener на всех уровнях.

person lu4242    schedule 05.12.2012