2014-02-03 5 views
9

Я пишу свой собственный составной составной компонент таблицы с помощью Mojarra JSF. Я также пытаюсь связать этот композит с компонентом поддержки. Цель состоит в том, чтобы иметь возможность указывать количество элементов, которые имеет таблица в составном атрибуте, а позже связанный компонент поддержки будет автоматически генерировать элементы перед просмотром. Я этот пример кода:Инициализировать составной компонент на основе предоставленных атрибутов

Главная страница:

<html xmlns="http://www.w3.org/1999/xhtml" 
    xmlns:f="http://java.sun.com/jsf/core" 
    xmlns:h="http://java.sun.com/jsf/html" 
    xmlns:comp="http://java.sun.com/jsf/composite/comp"> 
<h:head /> 
<body> 
    <h:form> 
     <comp:myTable itemNumber="2" /> 
    </h:form> 
</body> 
</html> 

myTable.xhtml:

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

<h:body> 
    <composite:interface componentType="components.myTable"> 
     <composite:attribute name="itemNumber" 
      type="java.lang.Integer" required="true" /> 
    </composite:interface> 

    <composite:implementation> 
     <h:dataTable value="#{cc.values}" var="value"> 
      <h:column headerText="column"> 
       #{value} 
       <h:commandButton value="Action" action="#{cc.action}" /> 
      </h:column> 
     </h:dataTable> 
    </composite:implementation> 
</h:body> 
</html> 

MyTable.java:

@FacesComponent("components.myTable") 
public class MyTable extends UINamingContainer { 

    private List<String> values = new ArrayList<String>(); 

    public void action() { 
     System.out.println("Called"); 
    } 

    @Override 
    public void encodeBegin(FacesContext context) throws IOException { 
     // Initialize the list according to the element number 
     Integer num = (Integer) getAttributes().get("itemNumber"); 
     for (int i = 0; i < num; i++) { 
      values.add("item" + i); 
     } 
     super.encodeBegin(context); 
    } 

    public List<String> getValues() { 
     return values; 
    } 

} 

Проблема заключается в том, что таблица получает рендеринг правильно (в данном случае с двумя элементами), но метод actionне называется при нажатии кнопки на линиях.

Если я следую wiki page для составных компонентов, я могу заставить его работать так, но имеющие для инициализации List каждый раз getValues() называется, вводя логику в метод получения :-(.

Любая идея об этом? Это, кажется, беда, связанные с наиважнейшей encodeBegin методом. Я также попытался его инициализации на markInitialState, но атрибуты не доступны там ...


Испытано с Mojarra 2.1.27 + Tomca t 6-7 & Mojarra 2.2.5 + Tomcat 7

ответ

13

Что касается причины, то UIComponent экземпляры по своей сути запрашивают область действия. Эффект postback эффективно создает новый экземпляр с такими свойствами, как values, переинициализированный по умолчанию. В вашей реализации он заполняется только во время encodeXxx(), который вызывается после decode(), где событие действия должно быть поставлено в очередь и, следовательно, слишком поздно.

Лучше заполнить его во время инициализации компонента. Если вы хотите использовать @PostConstruct-крючок для UIComponent экземпляров, то событие postAddToView является хорошим кандидатом. Это вызывается непосредственно после добавления экземпляра компонента в дерево компонентов.

<cc:implementation> 
    <f:event type="postAddToView" listener="#{cc.init}" /> 
    ... 
</cc:implementation> 

с

private List<String> values; 

public void init() { 
    values = new ArrayList<String>(); 
    Integer num = (Integer) getAttributes().get("value"); 

    for (int i = 0; i < num; i++) { 
     values.add("item" + i); 
    } 
} 

(и удалить метод encodeBegin() если он ничего полезного не делает больше)

Альтернативой будет ленивая инициализация в getValues() методом.

+0

спасибо @BalusC, еще раз. Не знал, что 'UIComponent' были без гражданства, но определенно это имеет смысл. Как решение сохранить состояние, я пошел с ссылкой «Список», которая создается управляемым bean-компонентом '@ ViewScoped' и совместно с самим компонентом ;-) –

+0

Добро пожаловать. – BalusC

+0

У этого метода работы есть недостаток. Обычно я инициализирую свой обработанный bean-материал методом 'preRenderView'. Я не использую '@ PostConstruct', так как имею дело с параметрами вида, которые должны быть установлены ранее, чтобы определить, что загрузить. Кажется, что 'postAddToView' вызывается до этого, поэтому мой управляемый компонент еще не отображает данные. Есть ли другое событие, которое я мог бы использовать вместо этого? –

Смежные вопросы