標準リストコントローラーの使用
学習の目的
この単元を完了すると、次のことができるようになります。
- Visualforce 標準リストコントローラーとは何か、標準 (レコード) コントローラーとの違いは何かを説明する。
- 標準リストコントローラーで提供される、標準コントローラーとは異なるアクションを 3 つ挙げる。
- Visualforce ページで標準リストコントローラーを使用して、レコードのリストを表示する。
- ページネーションを定義して Visualforce ページに追加する。
標準リストコントローラーの概要
標準リストコントローラーを使用すると、レコードセットの表示や操作が行える Visualforce ページを作成できます。
レコードのリストの表示は、ほぼすべての Web アプリケーションの基本動作です。Visualforce では、バックエンドコードなしでマークアップのみを使用して、同じタイプのレコードのリストを非常に簡単に表示できます。この秘密の鍵は標準コントローラーにあり、この場合は標準リストコントローラーが使用されます。
標準リストコントローラーには、特定のオブジェクトレコードのクエリ、コレクション変数でのレコードの使用、結果の絞り込みやページネーションなど、多数の強力な自動動作があります。ページへの標準リストコントローラーの追加は標準 (レコード) コントローラーの追加と非常に似ていますが、一度に 1 レコードを操作する代わりに、一度に多数のレコードを操作することを目的としています。
レコードのリストを表示する
<apex:pageBlockTable>
などの標準リストコントローラーと反復コンポーネントを使用して、レコードのリストを表示します。
標準 (レコード) コントローラーにより、Visualforce ページで使用できる変数に 1 つのレコードを簡単に読み込むことができます。標準リストコントローラーも同様ですが、1 つのレコードの代わりに、レコードのリストまたはコレクションを変数に読み込みます。
個々のレコードではなくコレクションを操作するため、反復コンポーネントを使用してレコードを表示する必要があります。反復コンポーネントは、1 つの値ではなく類似した項目のコレクションと連動します。反復コンポーネントはそのコレクションをループし、各レコードに対して、コンポーネントマークアップの一部として提供するテンプレートに基づいて出力を生成します。これは複雑に思えるかもしれませんが、マークアップを読めばすぐに理解できます。
標準リストコントローラーを使用するためのマークアップは、一度に 1 レコードの標準コントローラーを使用するためのマークアップとほぼ同じです。その違いを明確にするため、次のサンプルでは主な違いが太字で強調表示されています。
- 新しい Visualforce ページを作成するには、開発者コンソールを開き、[File (ファイル)] | [New (新規)] | [Visualforce Page (Visualforce ページ)] をクリックします。ページ名に「ContactList」と入力します。
- エディターで、任意のマークアップを次のように置き換えます。
<apex:page standardController="Contact" recordSetVar="contacts"> <apex:pageBlock title="Contacts List"> <!-- Contacts List --> <apex:pageBlockTable value="{! contacts }" var="ct"> <apex:column value="{! ct.FirstName }"/> <apex:column value="{! ct.LastName }"/> <apex:column value="{! ct.Email }"/> <apex:column value="{! ct.Account.Name }"/> </apex:pageBlockTable> </apex:pageBlock> </apex:page>
-
[Preview (プレビュー)] をクリックして、変更処理中に確認できるページのプレビューを開きます。標準の Salesforce ページヘッダー、サイドバー要素、取引先責任者のリストが表示された新規ウィンドウが開きます。
標準リストコントローラーの使用方法は、標準コントローラーの使用方法によく似ています。最初に <apex:page>
コンポーネントの standardController
属性を設定し、次に同じコンポーネントの recordSetVar
属性を設定します。standardController
属性では、標準コントローラーと同様に、作業するオブジェクトを設定します。recordSetVar では、レコードのコレクションで作成される変数の名前 (ここでは {!contacts }
) を設定します。命名規則に従って、通常この変数には複数形のオブジェクト名が付けられます。
<apex:pageBlockTable>
は、プラットフォームのスタイル設定が適用されたデータテーブルを生成する反復コンポーネントです。テーブルマークアップでの処理を次に示します。
-
<apex:pageBlockTable>
の value 属性は、標準リストコントローラー{!contacts }
で読み込まれた変数に設定されます。これは<apex:pageBlockTable>
で操作するレコードのリストです。
- そのリストの各レコードに対して 1 レコードずつ、
<apex:pageBlockTable>
はレコードを<apex:pageBlockTable>
の var 属性で指定された変数に割り当てます。この場合、変数にはct
が指定されます。
- 各レコードに対して、
<apex:pageBlockTable>
は<apex:pageBlockTable>
本体内の一連の<apex:column>
コンポーネントで定義された行を使用して、テーブルに新しい行を作成します。<apex:column>
コンポーネントは、現在のレコードを表すct
変数を使用して、そのレコードの項目値を順に取得します。
- ループの外側で、
<apex:pageBlockTable>
は<apex:column>
コンポーネントの項目を使用して、各項目の表示ラベルを調べることで列ヘッダーを作成します。
これは非常に複雑で、最初は反復コンポーネントを理解することは難しいかもしれません。この時点でできることは、自分自身で作成してみることです。テーブルに表示する項目を選択します。<apex:pageBlockTable>
と <apex:column>
の別の属性を調べて、慣れるまで試してみてください。また、<apex:dataList>
や <apex:repeat>
など、その他いくつかの反復コンポーネントも使用してみてください。
リストにリストビューの検索条件を追加する
{!listViewOptions }
を使用して、オブジェクトで使用可能なリストビュー検索条件のリストを取得します。{!filterId }
を使用して、標準リストコントローラーの結果に使用するリストビュー検索条件を設定します。
標準リストコントローラーには、リストの表示を変更できる多数の機能があります。最も強力な機能の 1 つに、リストビュー検索条件があります。リストビュー検索条件は、コードを使用せずにクリックで宣言的に作成します。標準リストコントローラーでは、ページで定義済みのリストビュー検索条件を使用できます。
-
<apex:pageBlock>
全体を<apex:form>
タグでラップします。標準リストコントローラーのリストビュー検索条件を変更するには、新しい値をサーバーに送信する必要があります。この送信を実行する一般的な方法は、<apex:form>
コンポーネントで作成されたフォームを使用することです。
-
<apex:pageBlock>
タグで、次の属性を追加します。
id="contacts_list"
Ajax 効果 (これについては後ほど説明します) に使用できる「名前」が <apex:pageBlock>
に付けられます。
-
<apex:pageBlock>
開始タグの後、<apex:pageBlockTable>
の上に、次のマークアップを追加します。ページの完全なコードは次のようになります。Filter: <apex:selectList value="{! filterId }" size="1"> <apex:selectOptions value="{! listViewOptions }"/> <apex:actionSupport event="onchange" reRender="contacts_list"/> </apex:selectList>
新しい検索条件コントロールがリストの上に表示されます。<apex:page standardController="Contact" recordSetVar="contacts"> <apex:form> <apex:pageBlock title="Contacts List" id="contacts_list"> Filter: <apex:selectList value="{! filterId }" size="1"> <apex:selectOptions value="{! listViewOptions }"/> <apex:actionSupport event="onchange" reRender="contacts_list"/> </apex:selectList> <!-- Contacts List --> <apex:pageBlockTable value="{! contacts }" var="ct"> <apex:column value="{! ct.FirstName }"/> <apex:column value="{! ct.LastName }"/> <apex:column value="{! ct.Email }"/> <apex:column value="{! ct.Account.Name }"/> </apex:pageBlockTable> </apex:pageBlock> </apex:form> </apex:page>
- メニューから異なる検索条件を選択します。取引先責任者リストはどのように変化するでしょうか。
ここで作成した [検索条件] メニューから新しいオプションを選択すると、2 つの変化があります。まず、新しい検索条件を選択したときにレコードのリストが変更されます。(いくつかの異なるオプションの選択が必要な場合があります。標準の DE 組織のサンプルデータを使用している場合、複数のリストビューで同じレコードが表示されます)。
次に、あまり目立ちませんが、ページ全体は再読み込みされることなく、リストが更新されます。この「Ajax」効果は、<apex:actionSupport>
コンポーネントの reRender="contacts_list"
属性で適用されます。コンポーネントと reRender の複合効果によって、reRender 属性で指定されたページの一部のみが更新されます。<apex:pageBlock>
に id="contacts_list"
を追加しているため、アクションの完了時に、ページ全体が再読み込みされることなく <apex:pageBlock>
のみが更新されます。
このページでのこの新機能の完全なライフサイクルは、次のようになります。
- ページが読み込まれると、
{!listViewOptions }
式からリストを取得することで、使用可能な検索条件のメニューが<apex:selectList>
で作成されます。listViewOptions
は、標準リストコントローラーで提供されるプロパティです。
- メニューから新しいオプションを選択すると、
<apex:actionSupport>
コンポーネントによってonchange
イベントが起動されます。
-
onchange
が起動すると、filterId
プロパティに新しい項目を送信することで、新しく選択されたリストビューがページから戻され、<apex:selectList>
に設定されます。
- プロパティが更新されると、contacts 変数の新しい該当レコードのコレクションを含む、サーバーからの新しい応答をページが受信します。
- ただし、
<apex:actionSupport>
ではページの一部のみの表示が指定されているため、ページ全体が再読み込みされる代わりに、Ajax (非同期 JavaScript) を使用してページが更新されます。
正味の効果は、複雑で高度な動作をほんの数行のマークアップの追加で実現できることにあります。
ページネーションをリストに追加する
標準リストコントローラーのページネーション機能を使用すると、ユーザーは長いリストのレコードを 1 「ページ」ずつ表示できます。
これまで開発してきた機能は、Developer Edition 組織のサンプルデータとして提供されている短いリストのレコードでは快適に動作します。ただし、数百や数千、または数百万件のレコードがある実際の組織ではどうでしょうか。すべてのレコードを 1 ページに表示した場合は、うまく機能しません。
実際、デフォルトでは、標準リストコントローラーは検索条件に一致する最初の 20 件 (存在する場合) のレコードのみを表示します。この 20 件以外のレコードにユーザーがアクセスできるようにしたり、1 ページに 20 件を超えるレコードを表示したりするにはどうすればよいでしょうか。
答えはページネーションにあります。ページネーションは標準の Web アプリケーションユーザーインターフェースです。長いリストのレコードを 1 「ページ」ずつ、通常は [Next (次へ)] と [前へ] リンクを使用して前後に移動できます。標準リストコントローラーや進行状況インジケーター、メニューなどのツールを使用してページにページネーションを追加し、ページあたりのレコード数を変更できます。
- 取引先責任者リスト
</apex:pageBlockTable>
の下に、次のマークアップを追加します。
<!-- Pagination --> <table style="width: 100%"> <tr> <td> <!-- Page X of Y --> </td> <td align="center"> <!-- Previous page --> <!-- Next page --> </td> <td align="right"> <!-- Records per page --> </td> </tr> </table>
追加する 3 つのページネーションコントロールを含めることができる HTML テーブルが作成されます。
-
<!-- Page X of Y -->
コメント行を次のマークアップで置き換えます。
Page: <apex:outputText value=" {!PageNumber} of {! CEILING(ResultSize / PageSize) }"/>
ユーザーが表示しているページと残りのページ数を示す、進行状況インジケーターがリストに追加されます。DE 組織で試行している場合は、「Page 1 of 1」 (1/1 ページ) と表示されます。
-
<!-- Previous page -->
と<!-- Next page -->
コメント行を次のマークアップで置き換えます。このマークアップにより、ページに [前へ] と [Next (次へ)] リンクが追加されます。指定方向にさらに表示するレコードがある場合はリンクを有効にし、指定方向にそれ以上ページがない場合はリンクを無効にします。<!-- Previous page --> <!-- active --> <apex:commandLink action="{! Previous }" value="« Previous" rendered="{! HasPrevious }"/> <!-- inactive (no earlier pages) --> <apex:outputText style="color: #ccc;" value="« Previous" rendered="{! NOT(HasPrevious) }"/> <!-- Next page --> <!-- active --> <apex:commandLink action="{! Next }" value="Next »" rendered="{! HasNext }"/> <!-- inactive (no more pages) --> <apex:outputText style="color: #ccc;" value="Next »" rendered="{! NOT(HasNext) }"/>
-
<!-- Records per page -->
コメント行を次のマークアップで置き換えます。このマークアップにより、ページあたりのレコード数を変更するメニューが追加されます。ここでは、1 ページにより少ないレコードを表示するオプションのみ追加されています。リストから「5」を選択し、リストとその他のコントロールがどのように変化するかを確認します。Records per page: <apex:selectList value="{! PageSize }" size="1"> <apex:selectOption itemValue="5" itemLabel="5"/> <apex:selectOption itemValue="20" itemLabel="20"/> <apex:actionSupport event="onchange" reRender="contacts_list"/> </apex:selectList>
標準リストコントローラーで提供される多数の機能が含まれたリストページが表示されます。
進行状況インジケーターでは、何ページあるかを示すために PageNumber
、ResultSize
、PageSize
の 3 つのプロパティが使用されています。最後の 2 つは、最も近い整数に四捨五入する数式で使用されます。これにより、インジケーターに「Page 2 of 1.6」 (2/1.6 ページ) などの不適切な値が表示されることを防ぎます。
ページネーションコントロールの <apex:commmandLink>
コンポーネントは、標準リストコントローラー Previous
と Next
で提供される 2 つのアクションメソッドを参照します。その結果、Previous
または Next
アクションを実行するリンクが表示されます。
では、{!HasPrevious }
などの式が含まれる rendered 属性は何を意味するのでしょうか。Visualforce はこの属性によって、コンポーネントを条件付きで (Boolean 式の結果に応じて) 表示できるようにします。ここでは、ページマークアップは標準リストコントローラー HasPrevious
と HasNext
で提供される Boolean プロパティを参照しています。これらのコントローラーは、指定方向にさらにレコードがあるかどうかを示します。rendered 属性でこの式を使用することで、そのコンポーネントの結果をページに表示または非表示にすることができます。これにより、ページを最初に読み込んだときは [前へ] リンクがグレー表示になり、[Next (次へ)] リンクをクリックして先に進むと有効になります。
ページあたりのレコード数選択メニューでは、以前に使用した <apex:selectList>
を使用しますが、メニューの値を取得するコントローラーメソッドをコールする代わりに、目的の値の <apex:selectOption>
要素を使用します。ここでも、<apex:actionSupport>
タグは選択値が変更された場合にメニューのアクションを実行し、reRender="contacts_list"
を使用して <apex:pageBlock>
のみを更新します。ここで異なる点は、<apex:selectList>
で標準リストコントローラーの PageSize
プロパティを設定していることです。
もうひとこと...
標準リストコントローラーには Web アプリケーションで一般的な多数の機能があり、ここで紹介している機能はほんの一部にすぎません。
たとえば、1 ページごとに前後に移動する Previous
アクションと Next
アクションに加えて、レコードのリストの先頭または最後に移動する First
アクションや Last
アクションもあります。
マークアップで操作する標準リストコントローラーは、StandardSetController
Apex クラスに基づいています。このクラスの詳細とすべての機能については、『Lightning Platform Apex コード開発者ガイド』を参照してください。
ここで作成した例では、並び替えの問題があります。通常はリストのデフォルトの並び替え順を設定し、並び替え順をその場で変更できる列ヘッダーを含めることが望まれます。実際には、Visualforce 単独で並び替え順を変更することはできません。並び替えとクリック可能な列ヘッダーをサポートするために必要な追加の Visualforce マークアップと Apex コードの量はそれほど多くありませんが、カスタムコードが必要になります。その開始点については、その他のリソースを参照してください。
リソース
-
Visualforce 開発者ガイド: Standard Controllers (標準コントローラー)
-
Visualforce 開発者ガイド: Standard List Controllers (標準リストコントローラー)
-
Visualforce 開発者ガイド: Create a Custom List View in Salesforce Classic (Salesforce Classic のカスタムリストビューの作成)
-
Visualforce 開発者ガイド: apex:outputLink コンポーネント
-
Visualforce 開発者ガイド: apex:repeat コンポーネント
-
Apex リファレンスガイド: StandardController クラス
-
Apex リファレンスガイド: StandardSetController クラス
-
Salesforce 開発者ブログ: Twitter Bootstrap and Visualforce in Minutes (Twitter Bootstrap を使用して Visualforce ページを数分で作成)