Прямым подходом было бы следующее представление:
<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>
С этим bean-компонентом:
@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, поскольку более старые версии не смогут поддерживать bean-компонент с областью просмотра, что приведет к неправильному включению во время обратной передачи. Если вы не можете выполнить обновление, вам придется вернуться к нижеприведенному (относительно неуклюжему) подходу условного рендеринга представления вместо условного построения представления:
...
<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>
Недостатком является то, что представление станет относительно большим и что все связанные управляемые компоненты могут быть инициализированы без необходимости, даже если они не будут использоваться в соответствии с условием визуализации.
Что касается позиционирования элементов, это просто вопрос применения правильного CSS. Это выходит за рамки JSF :) По крайней мере, <h:panelGroup layout="block">
отображает <div>
, так что этого должно быть достаточно.
И последнее, но не менее важное: подход SPA (одностраничное приложение) не оптимален для SEO. Все страницы не индексируются поисковыми роботами и не могут быть добавлены в закладки для конечных пользователей. Возможно, вам придется повозиться с историей HTML5 в клиенте и предоставить резервный сервер на стороне сервера. Более того, в случае страниц с формами один и тот же экземпляр bean-компонента с областью представления будет повторно использоваться на всех страницах, что приведет к неинтуитивному поведению при переходе на ранее посещенную страницу. Я бы предложил вместо этого использовать шаблонный подход, как описано во 2-й части этого ответа: Как включить другой XHTML в XHTML с помощью Facelets JSF 2.0? См. также Как ориентироваться в JSF? Как сделать так, чтобы URL отображал текущую страницу (а не предыдущую).
person
BalusC
schedule
18.08.2011