JSF 2.0: Как добавить UIComponents и их содержимое для просмотра root? - PullRequest
2 голосов
/ 24 ноября 2010

Я создаю пользовательский UIComponent и добавляю в него элементы (и другие стандартные UIComponents). Компонент отображается нормально, но его нельзя найти в ViewRoot.

Допустим, у меня есть:

ResponseWriter writer;

@Override
public void encodeBegin(FacesContext context) throws IOException {
    writer = context.getResponseWriter();
    writer.startElement("div", this);
    writer.writeText("testing", null);
    writer.writeAttribute("id", getClientId(context) + ":testDiv", null);
}

@Override
public void encodeEnd(FacesContext context) throws IOException {
    writer.endElement("div");        
}

Добавление этого как:

<x:myUiComponent id="myComponent" />

Это нормально, но я не могу найти компонент или его дочерний элемент из ViewRoot:

context.getViewRoot().findComponent("myComponent");  // returns null
context.getViewRoot().findComponent("myComponent:testDiv");  // returns null
findComponent("myComponent:testDiv");  // called within the custom component, throws java.lang.IllegalArgumentException?

Та же проблема возникает, когда я пытаюсь добавить другие UIComponents в качестве дочерних элементов моего пользовательского компонента - они успешно отображаются, но не могут быть найдены из дерева компонентов, поскольку сам мой пользовательский компонент там не находится.

Что за хитрость здесь, чтобы получить компоненты в дерево компонентов?

Редактировать : Изменен заголовок, чтобы лучше отражать вопрос.

Ответы [ 2 ]

4 голосов
/ 24 ноября 2010

Я не уверен, что getClientId(context) вернет myComponent. Действительно, если ваш компонент вложен в компонент NamingContainer, такой как <h:form>, его ID будет иметь префикс с идентификатором этого контейнера.

Например, если у вас есть следующая страница XHTML:

<h:form id="myForm">
    <x:myUiComponent id="myComponent" />

тогда вам придется получить ваш компонент, используя:

context.getViewRoot().findComponent("myForm:myComponent");
<Ч />

Что касается context.getViewRoot().findComponent("myComponent:testDiv"); (или context.getViewRoot().findComponent("myForm:myComponent:testDiv");, он вернет null, поскольку такого элемента в дереве компонентов JSF на стороне сервера нет. Код:

writer.writeAttribute("id", getClientId(context) + ":testDiv", null);

будет устанавливать атрибут ID только для HTML-сгенерированного компонента , т.е. у вас будет <div id="myForm:myComponent:testDiv"> на вашей HTML-странице, отправленной в браузер. Этот <div> компонент не существует в вашем дереве компонентов JSF и поэтому не может быть получен на стороне Java.

1 голос
/ 25 ноября 2010

Это было вызвано глупой ошибкой.Как можно было бы предположить, добавленные UIComponent действительно автоматически добавляются к ViewRoot.Я, однако, вызывал пользовательский UIComponent (тот, о котором я говорил в вопросе) из моего другого пользовательского UIComponent (о котором я не упомянул, потому что я забыл, что он там был), например:

UICodeEditor:

@Override
public void encodeAll(FacesContext context) throws IOException {
    UIEditPanel editor = new UIEditPanel();
    editor.encodeAll(context);
}

Затем вызывается в шаблоне, например:

<!-- codeEditor is taghandler for UICodeEditor -->
<x:codeEditor />

Здесь UICodeEditor делает автоматически добавленным к ViewRoot однако UIEditPanel внутри этого не делает, потому что я просто звонил encodeAll(context), и поэтому UIEditPanel не может узнать своего «родителя».Быстрое решение состоит в том, чтобы вручную установить родительский элемент для дочернего компонента:

editor.setParent(this);

Альтернативный подход (может быть, лучше) заключался бы в расширении с UIComponentBase (как мы обычно делаем), а не в ручной вызов encodeAll(context) но просто добавьте компонент в качестве дочернего элемента с getChildren.add(...) в encodeBegin(...), а не в encodeAll(...):

@Override
public void encodeBegin(FacesContext context) throws IOException {
    UIEditPanel editor = new UIEditPanel();
    getChildren().add(editor);
}

getChildren().add() внутренне добавляет текущий компонент в качестве родительского для дочерних элементов.

Учитывая расположение дочернего объекта, может быть лучше построить их прямо в конструкторе и вообще не переопределять методы encodeXXX, если нет необходимости использовать ResponseWriter (если есть, то вынужно переопределить те).Тем не менее, это более гибкий способ переопределить и вручную вызывать кодировку, что бы вам ни понадобилось.

Также обратите внимание, что пользовательский UIComponent не может быть непосредственно целью <f:ajax render=, поскольку он не представлен элементом DOM вДерево DOM.Это тот же случай, что и с составными компонентами, где я склоняюсь к реализации составного компонента в JSF-файл, управляемый panelGroup, так что содержимое может быть перерисовано позже.

...