Насколько хорошо JSF (и PrimeFaces) работает с веб-компонентами без сохранения состояния?
Это технически возможно.
JSF использует состояние просмотра в первую очередь для отслеживания атрибутов «отключено», «только для чтения» и «обработано» компонентов UIInput
и UICommand
, а также «отправленное значение», «локальное значение» и «действительно?» состояния компонентов EditableValueHolder
(реализованных, среди прочего, UIInput
).
В случае атрибутов «отключено», «только для чтения» и «обработано», если они представляют собой EL выражение, то JSF перепроверит его во время обработки запроса на отправку формы. Ниже приведен базовый пример c:
<h:form>
<h:commandButton value="toggle" action="#{bean.toggle}">
<f:ajax render="panel" />
</h:commandButton>
<h:panelGroup id="panel">
<h:commandButton value="submit" action="#{bean.submit}" rendered="#{bean.toggled}">
<f:ajax />
</h:commandButton>
</h:panelGroup>
</h:form>
@Named
@ViewScoped
public class Bean implements Serializable {
private static final long serialVersionUID = 1L;
private boolean toggled;
public void toggle() {
this.toggled = !toggled;
}
public void submit() {
System.out.println("Submitted");
}
public boolean isToggled() {
return toggled;
}
}
Сначала нажмите кнопку «Переключить», а затем кнопку «Отправить». В случае bean-компонента с областью просмотра он будет работать нормально. Если вы, однако, замените здесь @ViewScoped
на @RequestScoped
, тогда это не удастся, потому что toggled
по умолчанию возвращается к false
в тот момент, когда JSF необходимо декодировать кнопку «отправить» во время запроса обратной передачи, и поэтому его * Атрибут 1025 * будет оценивать false
, и в конечном итоге JSF не будет помещать событие действия в очередь.
В таком случае вам нужно убедиться, что свойство предварительно инициализировано до ожидаемого значения во время (пост) создания bean-компонент запроса. Один из способов - использовать для этого скрытые поля ввода в обновленном ajax компоненте. Вот скорректированный пример:
<h:form>
<h:commandButton value="toggle" action="#{bean.toggle}">
<f:ajax render="panel" />
</h:commandButton>
<h:panelGroup id="panel">
<input type="hidden" name="toggled" value="#{bean.toggled}" />
<h:commandButton value="submit" action="#{bean.submit}" rendered="#{bean.toggled}">
<f:ajax />
</h:commandButton>
</h:panelGroup>
</h:form>
@Named
@RequestScoped
public class Bean {
@Inject @ManagedProperty("#{param.toggled}")
private boolean toggled;
public void toggle() {
this.toggled = !toggled;
}
public void submit() {
System.out.println("Submitted");
}
public boolean isToggled() {
return toggled;
}
}
ПРИМЕЧАНИЕ: <h:inputHidden>
, к сожалению, не будет работать, поскольку он обновляет значение модели только после событие действия должно быть в очереди. Даже не с immediate="true"
на нем. Это, кстати, подводит меня к идее нового <o:inputHidden>
для OmniFaces.
С этими изменениями все будет работать нормально.
Однако , поскольку состояние, которое изначально было ограничено просмотром (свойство toggled
), теперь стало параметром запроса, оно полностью открыто для мира и, следовательно, также может быть изменено хакерами. Хакеры, желающие активировать кнопку «Отправить», не нажимая сначала кнопку «Переключить», теперь могут просто вручную добавить параметр запроса toggled=true
. Желательно ли это, зависит от бизнес-требований вашего приложения, но чаще всего это совершенно нежелательно.
Это то, от чего JSF пытается вас защитить, предлагая возможность поместить эти чувствительные свойства в @ViewScoped
bean вместо этого.
Это оказывается проблематичным для сложных страниц с AJAX, таблицами данных и условным рендерингом
Верно, но все же технически невозможно. Вам нужно только вручную переносить разбитые на страницы, отсортированные и отфильтрованные состояния через вручную заполненные скрытые поля ввода, как показано выше. <p:dataTable>
поддерживает привязку этих состояний к свойствам компонента. Например:
<p:dataTable ...
first="#{bean.first}"
sortField="#{bean.sortField}"
sortOrder="#{bean.sortOrder}"
filterBy="#{bean.filterBy}">
...
</p:dataTable>
. Вы можете просто скопировать их в поля <input type="hidden">
, как было показано ранее (которые, как вы убедитесь, охватываются <p:ajax update>
!), И, наконец, получить их через @ManagedProperty
и / или @PostConstruct
.
В эффектах вы, таким образом, в основном заново изобретаете работу, которая в настоящее время уже выполняется с помощью javax.faces.ViewState
скрытого поля ввода в сочетании с @ViewScoped
beans. Так почему бы не использовать его прямо сейчас? :)
Если вас больше всего беспокоит использование памяти, вам необходимо тщательно спроектировать bean-компоненты таким образом, чтобы только состояние с ограниченным представлением сохранялось в bean-компоненте @ViewScoped
и только состояние области запроса сохраняется в bean-компоненте @RequestScoped
. Например, вполне нормально поместить модель данных в bean-компонент с областью действия запроса и состояние разбивки на страницы / сортировки / фильтрации в bean-компоненте с областью просмотра. Вы также можете рассмотреть OmniFaces @ViewScoped
вместо этого, поскольку это сразу же уничтожает состояние представления и физический компонент при выгрузке страницы.
Тем не менее, имея в виду этот вопрос, я 've всего за несколько часов go проверил и улучшил библиотеку OptimusFaces , чтобы убедиться, что она также полностью поддерживает представления без сохранения состояния с <f:view transient="true">
, а также новый интеграционный тест. Преимущество OptimusFaces, помимо прочего, в том, что вам больше не нужно вручную беспокоиться о переносе состояния разбивки на страницы / сортировки / фильтрации. OptimusFaces позаботится об этом за вас.
См. Также: