Простым подходом будет следующий вид:
<h:panelGroup id="header" layout="block">
<h1>Header</h1>
</h:panelGroup>
<h:panelGroup id="menu" layout="block">
<h:form>
<f:ajax render=":content">
<ul>
<li><h:commandLink value="include1" action="#{bean.setPage('include1')}" /></li>
<li><h:commandLink value="include2" action="#{bean.setPage('include2')}" /></li>
<li><h:commandLink value="include3" action="#{bean.setPage('include3')}" /></li>
</ul>
</f:ajax>
</h:form>
</h:panelGroup>
<h:panelGroup id="content" layout="block">
<ui:include src="/WEB-INF/includes/#{bean.page}.xhtml" />
</h:panelGroup>
С этим компонентом:
@ManagedBean
@ViewScoped
public class Bean implements Serializable {
private String page;
@PostConstruct
public void init() {
page = "include1"; // Default include.
}
// +getter+setter.
}
В этом примере фактические включаемые шаблоны: include1.xhtml
, include2.xhtml
и include3.xhtml
в папке /WEB-INF/includes
(папка и папка по вашему выбору полностью свободны; файлы просто помещаются в /WEB-INF
, чтобы запретить прямой доступ путем угадывания URL-адреса в адресной строке браузера).
Этот подход работает во всех версиях MyFaces, но требует как минимум Mojarra 2.3.0.В случае, если вы используете версию Mojarra более раннюю, чем 2.3.0, тогда все это завершится ошибкой, если страница <ui:include>
в свою очередь содержит <h:form>
.Любая обратная передача не удастся, потому что в ней полностью отсутствует состояние просмотра.Вы можете решить эту проблему путем обновления до минимально Mojarra 2.3.0 или с помощью скрипта, найденного в этом ответе h: commandButton / h: commandLink не работает при первом щелчке, работает только при втором щелчке или, если выВы уже используете библиотеку утилит JSF OmniFaces , используйте ее FixViewState скрипт .Или, если вы уже используете PrimeFaces и используете исключительно <p:xxx>
ajax, то это уже прозрачно учтено.
Кроме того, убедитесь, что вы используете минимально Mojarra 2.1.18, так как старые версии не будут работатьв поддержании живого объекта видимости, вызывая неправильное использование во время обратной передачи.Если вы не можете выполнить обновление, тогда вам придется вернуться к (относительно неуклюжему) подходу условной визуализации представления вместо условного построения представления:
...
<h:panelGroup id="content" layout="block">
<ui:fragment rendered="#{bean.page eq 'include1'}">
<ui:include src="include1.xhtml" />
</ui:fragment>
<ui:fragment rendered="#{bean.page eq 'include2'}">
<ui:include src="include2.xhtml" />
</ui:fragment>
<ui:fragment rendered="#{bean.page eq 'include3'}">
<ui:include src="include3.xhtml" />
</ui:fragment>
</h:panelGroup>
Недостатком является то, что представлениестанет относительно большим, и что все связанные управляемые bean-компоненты могут быть излишне инициализированы, даже если они не будут использоваться согласно визуализированному условию.
Что касается позиционирования элементов, то это просто вопрос применения правильного CSS,Это выходит за рамки JSF :) По крайней мере, <h:panelGroup layout="block">
отображает <div>
, так что этого должно быть достаточно.
И последнее, но не менее важное, этот подход SPA (одностраничное приложение) не подходит для SEO,Все страницы не индексируются поисковыми роботами и не могут быть добавлены в закладки конечными пользователями, возможно, вам придется поиграться с историей HTML5 в клиенте и предоставить запасной вариант на стороне сервера.Более того, в случае страниц с формами один и тот же экземпляр bean-объекта области видимости будет повторно использоваться на всех страницах, что приведет к неинтуитивному поведению области видимости при переходе к ранее посещенной странице.Вместо этого я предложил бы использовать шаблонный подход, как описано во 2-й части этого ответа: Как включить еще один XHTML в XHTML с использованием JSF 2.0 Facelets? См. Также Как перемещаться в JSF?Как сделать так, чтобы URL отражал текущую (а не предыдущую) страницу .