Найти компонент по типу в JSF

Мой вопрос связан с этим. Получить все скрытые поля ввода в JSF динамически, но это не то же самое, что я хочу работать с JSF, а не с простым HTML, и при условии, что у меня есть следующее в моем .xhtml файл:

<h:inputHidden id="name1" value="SomeValue1"/>
<h:inputHidden id="name2" value="SomeValue2"/>

Я разработал небольшой код, где я пытался получить все h:inputHidden динамически помечает теги и выводит их значения на консоль, но проблема в том, что я не могу придумать, как сделать все динамичным. В моем коде я должен знать форму id если я хочу перебрать uicomponents, как я могу перебрать все UIComponent в дереве компонентов? (Я старался UIViewRoot#getChildren() но я получаю только первые детские).

Вот фрагмент кода:

// formId is the id of my form
List<UIComponent> components = FacesContext.getCurrentInstance().getViewRoot().findComponent("formId").getChildren();
// A List of UIComponent where I am adding my Hidden Inputs
List<UIComponent> hiddenComponents = new ArrayList<UIComponent>();

for (UIComponent component : components) {

    // using the hidden inputs type in JSF: HtmlInputHidden
    if (component instanceof HtmlInputHidden) {
        hiddenComponents.add(component);
    }

}

for (UIComponent component : hiddenComponents) {

    // Printing the hidden inputs values for demonstration purposes
    System.out.println(((HtmlInputHidden)component).getValue());

}

1 ответ

Решение

Вам также нужно перебрать детей детей и их детей и так далее. Видите ли, это дерево компонентов.

Вот фрагмент кода служебного метода, который делает это с помощью хвостовой рекурсии:

public static <C extends UIComponent> void findChildrenByType(UIComponent parent, List<C> found, Class<C> type) {
    for (UIComponent child : parent.getChildren()) {
        if (type.isAssignableFrom(child.getClass())) {
            found.add(type.cast(child));
        }

        findChildrenByType(child, found, type);
    }
}

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

UIForm form = (UIForm) FacesContext.getCurrentInstance().getViewRoot().findComponent("formId");
List<HtmlInputHidden> hiddenComponents = new ArrayList<>();
findChildrenByType(form, hiddenComponents, HtmlInputHidden.class);

for (HtmlInputHidden hidden : hiddenComponents) {
    System.out.println(hidden.getValue());
}

Или лучше используйте UIComponent#visitTree() который использует шаблон посетителя. Основное отличие состоит в том, что он также выполняет итерации по таким компонентам, как <ui:repeat> а также <h:dataTable> и восстанавливает дочернее состояние для каждой итерации. В противном случае вы бы не получили никакой ценности, когда у вас есть <h:inputHidden> заключен в такой компонент.

FacesContext context = FacesContext.getCurrentInstance();
List<Object> hiddenComponentValues = new ArrayList<>();
context.getViewRoot().findComponent("formId").visitTree(VisitContext.createVisitContext(context), new VisitCallback() {
    @Override
    public VisitResult visit(VisitContext visitContext, UIComponent component) {
        if (component instanceof HtmlInputHidden) {
            hiddenComponentValues.add(((HtmlInputHidden) component).getValue());
            return VisitResult.COMPLETE;
        } else {
            return VisitResult.ACCEPT;
        }
    }
});

for (Object hiddenComponentValue : hiddenComponentValues) {
    System.out.println(hiddenComponentValue);
}

Смотрите также:

В конце концов, вероятно, проще всего связать их со свойством бина обычным способом, если это необходимо внутри <ui:repeat>:

<h:inputHidden id="name1" value="#{bean.name1}"/>
<h:inputHidden id="name2" value="#{bean.name2}"/>
Другие вопросы по тегам