Ответ 1
Поскольку происхождение на самом деле не является XML, а джавабеем, а другой ответ не заслуживает редактирования в совершенно другой вкус (он может быть полезен для будущих ссылок другими), я буду добавьте еще один ответ, основанный на явавском происхождении.
Я вижу в основном три варианта, когда происхождение является джавабеем.
-
Используйте атрибут JSF
rendered
или даже теги JSTL<c:choose>
/<c:if>
для условного рендеринга или сборки желаемых компонентов. Ниже приведен пример с использованием атрибутаrendered
:<ui:repeat value="#{bean.fields}" var="field"> <div class="field"> <h:inputText value="#{bean.values[field.name]}" rendered="#{field.type == 'TEXT'}" /> <h:inputSecret value="#{bean.values[field.name]}" rendered="#{field.type == 'SECRET'}" /> <h:inputTextarea value="#{bean.values[field.name]}" rendered="#{field.type == 'TEXTAREA'}" /> <h:selectOneRadio value="#{bean.values[field.name]}" rendered="#{field.type == 'RADIO'}"> <f:selectItems value="#{field.options}" /> </h:selectOneRadio> <h:selectOneMenu value="#{bean.values[field.name]}" rendered="#{field.type == 'SELECTONE'}"> <f:selectItems value="#{field.options}" /> </h:selectOneMenu> <h:selectManyMenu value="#{bean.values[field.name]}" rendered="#{field.type == 'SELECTMANY'}"> <f:selectItems value="#{field.options}" /> </h:selectManyMenu> <h:selectBooleanCheckbox value="#{bean.values[field.name]}" rendered="#{field.type == 'CHECKONE'}" /> <h:selectManyCheckbox value="#{bean.values[field.name]}" rendered="#{field.type == 'CHECKMANY'}"> <f:selectItems value="#{field.options}" /> </h:selectManyCheckbox> </div> </ui:repeat>
Пример подхода JSTL можно найти в Как создать сетку составного компонента JSF? Нет, JSTL абсолютно не является "плохой практикой". Этот миф остается в стороне от эпохи JSF 1.x и продолжается слишком долго, потому что стартеры не поняли жизненный цикл и полномочия JSTL. По сути, вы можете использовать JSTL только тогда, когда модель за
#{bean.fields}
, как в приведенном выше фрагменте, никогда не изменяется во время, по крайней мере, области просмотра JSF. См. Также JSTL в JSF2 Facelets... имеет смысл? Вместо этого использованиеbinding
для свойства bean по-прежнему является "плохой практикой".Что касается
<ui:repeat><div>
, на самом деле не имеет значения, какой итеративный компонент вы используете, вы можете даже использовать<h:dataTable>
как в своем первоначальном вопросе, так и в компоненте, который выполняет итерацию компонента, например<p:dataGrid>
или<p:dataList>
. При необходимости переформатируйте большой кусок кода в файл include или tagfile.Что касается сбора представленных значений,
#{bean.values}
должен указывать наMap<String, Object>
, который уже был предварительно обработан. AHashMap
. Возможно, вы захотите предварительно заполнить карту в случае элементов управления, которые могут устанавливать несколько значений. Затем вы должны предварительно заполнить его значениемList<Object>
. Обратите внимание, что я ожидаю, чтоField#getType()
будетenum
, поскольку это облегчает обработку в кодовой части Java. Затем вы можете использовать инструкциюswitch
вместо неприятного блокаif/else
. -
Создайте компоненты программным способом в прослушивателе событий
postAddToView
:<h:form id="form"> <f:event type="postAddToView" listener="#{bean.populateForm}" /> </h:form>
С
public void populateForm(ComponentSystemEvent event) { HtmlForm form = (HtmlForm) event.getComponent(); for (Field field : fields) { switch (field.getType()) { // It easiest if it an enum. case TEXT: UIInput input = new HtmlInputText(); input.setId(field.getName()); // Must be unique! input.setValueExpression("value", createValueExpression("#{bean.values['" + field.getName() + "']}", String.class)); form.getChildren().add(input); break; case SECRET: UIInput input = new HtmlInputSecret(); // etc... } } }
(обратите внимание: НЕ создавайте
HtmlForm
самостоятельно! используйте созданный JSF, этот никогда неnull
)Это гарантирует, что дерево заселено ровно в нужный момент и не дает геттерам быть свободным от бизнес-логики и избегает потенциальных проблем с "повторяющимся идентификатором компонента", когда
#{bean}
находится в более широкой области действия, чем область запроса (так что вы можете безопасно использовать, например, область видимости bean здесь) и сохраняет bean без свойствUIComponent
, что, в свою очередь, позволяет избежать возможных проблем с сериализацией и утечки памяти, когда компонент удерживается как свойство сериализуемого bean.Если вы все еще используете JSF 1.x, где
<f:event>
недоступен, вместо этого свяжите компонент формы с запросом (не session!) с областью bean черезbinding
<h:form id="form" binding="#{bean.form}" />
И затем лениво заселить его в getter формы:
public HtmlForm getForm() { if (form == null) { form = new HtmlForm(); // ... (continue with code as above) } return form; }
При использовании
binding
очень важно понять, что компоненты пользовательского интерфейса в основном связаны с запросами и не должны быть назначены как свойство bean в более широкой области. См. Также Как работает атрибут привязки в JSF? Когда и как его использовать? -
Создайте настраиваемый компонент с помощью настраиваемого средства визуализации. Я не собираюсь публиковать полные примеры, так как много кода, которое в конце концов было бы очень сложным и специфичным для приложения беспорядком.
Плюсы и минусы каждого варианта должны быть четкими. Он идет от самого легкого и удобного в обслуживании к самому жесткому и наименее поддерживаемому, а затем и из менее пригодного для повторного использования. Вам решать, что лучше всего подходит для вашего функционального требования и текущей ситуации.
Отмечено, что существует абсолютно ничего, что возможно только на Java (путь №2) и невозможно в XHTML + XML (путь №1). Все возможно в XHTML + XML так же хорошо, как в Java. Многие начинающие недооценивают XHTML + XML (особенно <ui:repeat>
и JSTL) в динамическом создании компонентов и неправильно думают, что Java будет "единственным и единственным" способом, в то время как это обычно заканчивается хрупким и запутанным кодом.