Как обновить динамику ajax, включив контент в меню навигации? (JSF SPA)

Я просто изучаю JSF 2 благодаря этому сайту, за который я многому научился за такое короткое время.

Мой вопрос заключается в том, как реализовать общий макет на всех моих страницах JSF 2 и обновлять только часть содержимого страницы не всю страницу, когда я нажимаю ссылку/меню с другой панели. Я использую подход Facelets, он делает то, что я хочу, за исключением того, что каждый раз, когда я нажимаю ссылку с панели (например, элементы меню с левой панели), вся страница обновляется. Я ищу способ обновить только часть содержимого моей страницы. Чтобы проиллюстрировать это ниже, моя цель pagelayout.

enter image description hereНе отправлял мой код, потому что я не уверен, может ли Facelets сделать это. Есть ли другой подход, который больше подходит для моего требования, кроме Facelets?

Ответ 1

Прямым подходом будет следующий вид:

<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>. Любой postback не будет работать, потому что он полностью не видит состояние представления. Вы можете решить эту проблему путем обновления до минимального значения Mojarra 2.3.0 или с помощью script, найденного в этом ответе h: commandButton/h: commandLink не работает при первом нажатии, работает только на втором нажмите, или если вы уже используете библиотеку служебных программ JSF OmniFaces, используйте FixViewState script. Или, если вы уже используете 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>

Недостатком является то, что представление станет относительно большим и что все связанные управляемые beans могут быть излишне инициализированы, даже если они не будут использоваться в соответствии с предоставленным условием.

Что касается позиционирования элементов, это просто вопрос применения правого CSS. Это выходит за рамки JSF:) По крайней мере, <h:panelGroup layout="block"> отображает <div>, поэтому это должно быть достаточно хорошим.

И последнее, но не менее важное: этот подход SPA (Single Page Application) не оптимизирован для SEO. Все страницы не индексируются поисковыми роботами и не могут быть заклассифицированы конечными пользователями. Возможно, вам придется столкнуться с историей HTML5 на клиенте и предоставить резервную копию на стороне сервера. Более того, в случае страниц с формами тот же экземпляр с ограниченным охватом bean будет использоваться повторно во всех страницах, что приведет к неинтуитивному поведению при переходе на предыдущую страницу. Я бы предложил вместо этого использовать шаблонный подход, как указано во второй части этого ответа: Как включить другой XHTML в XHTML с помощью JSF 2.0 Facelets? См. Также Как перемещаться в JSF? Как заставить URL-адрес отражать текущую страницу (а не предыдущую).

Ответ 2

Если вы хотите обновить часть страницы, есть всего 2 пути (для Интернета в целом, а не только для JSF). Вы должны использовать фреймы или Ajax. JSF 2 поддерживает ajax изначально, проверьте тег f: ajax, чтобы обновить только один компонент без перезагрузки всей страницы.

Ответ 3

Netbeans предоставляет мастер, который создает предложенный макет с минимальными усилиями, используя JSF. Итак, лучший способ начать - взглянуть на Мастер шаблонов Facelets и посмотреть на сгенерированный источник.