web-dev-qa-db-ja.com

ナビゲーションメニューによる動的インクルードコンテンツのAjax更新方法(JSF SPA)

このサイトのおかげでJSF 2を学んでいるのは、この短い期間で多くのことを学んだからです。

私の質問は、すべてのJSF 2ページに共通のレイアウトを実装し、別のパネルからリンク/メニューをクリックするたびにページ全体ではなくページのコンテンツ部分のみを更新する方法に関するものです。私はFaceletsアプローチを使用していますが、パネルからリンクをクリックするたびに(左パネルのメニュー項目など)ページ全体が更新されることを除いて、私が望むことをします。私が探しているのは、ページのコンテンツ部分のみを更新する方法です。これを以下に説明するのが、私のターゲットのページレイアウトです。

enter image description here Faceletsでこれができるかどうかわからないため、コードを投稿しませんでした。 Facelets以外に私の要件により適した他のアプローチはありますか?

34
royjavelosa

簡単なアプローチは次のビューです。

<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.xhtmlinclude2.xhtmlおよびinclude3.xhtml in /WEB-INF/includesフォルダー(フォルダーと場所は自由に選択できます。ファイルは/WEB-INFブラウザのアドレスバーのURLを推測して直接アクセスを防止するために

このアプローチはすべてのMyFacesバージョンで機能しますが、少なくともMojarra 2.3.0が必要です。 2.3.0より古いバージョンのMojarraを使用している場合、<ui:include>ページには<h:form>。ビューステートが完全に欠落しているため、ポストバックは失敗します。これを最小限に抑えるには、Mojarra 2.3.0にアップグレードするか、この回答に記載されているスクリプトを使用します h:commandButton/h:commandLinkは最初のクリックでは機能せず、2回目のクリックでのみ機能します 、またはすでにJSFユーティリティライブラリ OmniFaces を使用している場合は、その FixViewStateを使用しますスクリプト 。または、すでにPrimeFacesを使用しており、<p:xxx> ajax、それはすでに透過的に考慮されています。

また、古いバージョンではビュースコープBeanを有効に保つことができず、ポストバック中に誤ったincludeが使用されるため、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部で概説されているように、代わりにテンプレートアプローチを使用することをお勧めします: JSF 2.0 Faceletsを使用してXHTMLに別のXHTMLを含める方法? また JSFでナビゲートする方法は? URLに現在のページを反映させる方法(前のページではなく)

62
BalusC

ページの一部のみを更新したい場合は、2つの方法しかありません(JSFだけでなく、一般的なWebの場合)。フレームまたはAjaxを使用する必要があります。 JSF 2はajaxをネイティブでサポートしています。ページ全体をリロードせずに1つのコンポーネントのみを更新するには、f:ajaxタグをチェックしてください。

2
GBa