この文章は Salesforce 機械翻訳システムを使用して翻訳されました。詳細はこちらをご参照ください。
英語に切り替える

標準オブジェクトでの動的参照の使用

アクセスする項目の既知のセットを使用して単純で再利用可能なページを構築するには、動的 Visualforce バインドを使用します。このアプローチには、どの項目がユーザの処理対象となるのかを容易にカスタマイズできるという利点があります。

次の 2 つの例は、説明のために意図的に簡略化されています。動的 Visualforce を十分に活用した高度な例は、「ユーザがカスタマイズ可能なページでの動的参照の使用」を参照してください。

単純な動的フォーム

次の例は、動的参照を使用する Visualforce ページを構築する最も簡単な方法を示します。

最初に、表示する項目の「動的な」リストを提供するコントローラ拡張を作成します。
1public class DynamicAccountFieldsLister {
2
3    public DynamicAccountFieldsLister(ApexPages.StandardController controller) { 
4        controller.addFields(editableFields);
5    }
6
7    public List<String> editableFields {
8        get {
9            if (editableFields == null) {
10                editableFields = new List<String>();
11                editableFields.add('Industry');
12                editableFields.add('AnnualRevenue');
13                editableFields.add('BillingCity');
14            }
15            return editableFields ;
16        }
17        private set;
18    }
19}
次に、上記のコントローラ拡張を使用する DynamicAccountEditor というページを作成します。
1<apex:page standardController="Account" 
2           extensions="DynamicAccountFieldsLister"> 
3
4    <apex:pageMessages /><br/>
5    
6    <apex:form>
7        <apex:pageBlock title="Edit Account" mode="edit">
8            <apex:pageBlockSection columns="1">
9                <apex:inputField value="{!Account.Name}"/>
10                <apex:repeat value="{!editableFields}" var="f">
11                    <apex:inputField value="{!Account[f]}"/>
12                </apex:repeat>
13           </apex:pageBlockSection>
14        </apex:pageBlock>
15    </apex:form>
16    
17</apex:page>
この例では次のようなことが行われます。
  • DynamicAccountFieldsLister コントローラ拡張は、editableFields という文字列のリストを作成します。各文字列は、Account オブジェクト内の項目名に対応付けられます。
  • editableFields リストはハードコードされていますが、項目はクエリや計算から特定したり、カスタム設定から読み取ったりすることが可能です。ハードコードされていない場合は、より動的な操作が提供されます。動的参照の利点は、このようなことが可能であることです。
  • DynamicAccountEditor マークアップは、<apex:repeat> タグを使用して editableFields から返された文字列をループ処理します。
  • <apex:inputField> タグは、Account の項目名を表す f 反復要素を参照して、editableFields 内の各項目を表示します。動的参照 {!Account[f]} が、実際に値をページに表示します。

標準コントローラによる動的参照の項目の読み込み

Visualforce は、ページの StandardController (または StandardSetController) で実行される SOQL クエリを自動的に最適化し、ページで実際に使用される項目のみを読み込みます。オブジェクトと項目への静的参照を含む Visualforce ページを作成する場合、項目とオブジェクトは前もって知っておく必要があります。ページを保存するときに、Visualforce はどのオブジェクトと項目を SOQL クエリに追加する必要があるかを判別して保存できます。この SOQL クエリを、後でページが要求されたときに StandardController が実行します。

動的参照は実行時に、StandardController が SOQL クエリを実行したに評価されます。動的参照でのみ使用される項目は、自動的には読み込まれません。動的参照が後で評価されたとき、その項目は存在しないデータとして解決され、結果的に SOQL エラーになります。読み込み対象の項目と関連オブジェクトがわかるように、コントローラに追加情報を指定する必要があります。

ページコントローラの addFields() メソッドを使用して読み込む追加項目のリストを渡すことで、StandardController クエリにいくつでも項目を追加できます。前述の例では、これはコントローラ拡張のコンストラクタで次のように実行されます。
1public DynamicAccountFieldsLister(ApexPages.StandardController controller) { 
2        controller.addFields(editableFields);
3    }
コンストラクタは、ページマークアップが使用するプロパティと同じプロパティ editableFields を使用して、コンストラクタが読み込む項目のリストに項目を追加します。

これは、コントローラ拡張のインスタンス化時に読み込むすべての項目のリストがわかるページに適しています。項目のリストが、要求処理における後の時点まで判別できない場合は、コントローラで reset() をコールし、項目を追加できます。これにより、コントローラは修正されたクエリを送信することになります。この技法の例は、「ユーザがカスタマイズ可能なページでの動的参照の使用」を参照してください。

コントローラへの項目追加が必要になるのは、StandardController または StandardSetController にデフォルトクエリを使用する場合のみです。コントローラまたはコントローラ拡張が独自の SOQL クエリを実行する場合、addFields() の使用は不要で、効果がありません。

メモ

これらのメソッドの詳細は、StandardController のドキュメントを参照してください。

関連オブジェクトへの動的参照

この例では、ケースレコードの Visualforce ページを作成し、特定の項目を編集可能にします。表示される項目の一��は関連オブジェクトのもので、動的参照を使用してリレーションをトラバースする方法を示します。

最初に、DynamicCaseLoader という Apex コントローラ拡張を作成します。
1public class DynamicCaseLoader {
2
3    public final Case caseDetails { get; private set; }
4
5    // SOQL query loads the case, with Case fields and related Contact fields
6    public DynamicCaseLoader(ApexPages.StandardController controller) {
7        String qid = ApexPages.currentPage().getParameters().get('id');
8        String theQuery = 'SELECT Id, ' + joinList(caseFieldList, ', ') + 
9                          ' FROM Case WHERE Id = :qid';
10        this.caseDetails = Database.query(theQuery);
11    }
12
13    // A list of fields to show on the Visualforce page
14    public List<String> caseFieldList { 
15        get {
16            if (caseFieldList == null) {
17                caseFieldList = new List<String>();
18                caseFieldList.add('CaseNumber');
19                caseFieldList.add('Origin');
20                caseFieldList.add('Status');
21                caseFieldList.add('Contact.Name');  // related field
22                caseFieldList.add('Contact.Email'); // related field
23                caseFieldList.add('Contact.Phone'); // related field
24            }
25            return caseFieldList;
26        }
27        private set;
28    }
29    
30    // Join an Apex list of fields into a SELECT fields list string
31    private static String joinList(List<String> theList, String separator) {
32
33        if (theList == null) {
34            return null;
35        }
36        if (separator == null) {
37            separator = '';
38        }
39
40        String joined = '';
41        Boolean firstItem = true;
42        for (String item : theList) {
43            if(null != item) {
44                if(firstItem){
45                    firstItem = false;
46                }
47                else {
48                    joined += separator;               
49                }
50                joined += item;
51            }
52        }
53        return joined;
54    }
55}
対応するページ DynamicCaseEditor はこの拡張を使用して特定のケースとそれに関連付けられた取引先責任者に関する情報を取得します。
1<apex:page standardController="Case" extensions="DynamicCaseLoader"> 
2    <br/>
3    <apex:form >
4        <apex:repeat value="{!caseFieldList}" var="cf">
5            <h2>{!cf}</h2>
6            <br/>
7            <!-- The only editable information should be contact information --> 
8            <apex:inputText value="{!caseDetails[cf]}" 
9                rendered="{!IF(contains(cf, "Contact"), true, false)}"/>
10            <apex:outputText value="{!caseDetails[cf]}" 
11                rendered="{!IF(contains(cf, "Contact"), false, true)}"/>
12            <br/><br/>
13        </apex:repeat>
14    </apex:form>
15</apex:page>
このページに、id クエリパラメータとして指定された、有効なケースレコードの ID を使用してアクセスします。たとえば、https://Salesforce_instance/apex/DynamicCaseEditor?id=500D0000003ZtPy です。ページに次のようなフォームが表示されます。ケースの動的 Visualforce ページのサンプル
この例では、次の点に留意してください。
  • コントローラ拡張では、オブジェクトを表示できるようにコンストラクタが独自の SOQL クエリを実行します。これは、ページの StandardController がデフォルトでは関連項目を読み込まないためですが、カスタマイズした SOQL クエリが必要になる使用事例は数多くあります。クエリの結果は、プロパティ caseFieldList によってページで使用できるようになります。コンストラクタでクエリを実行するための要件はありません。プロパティの get メソッドに簡単に追加できます。
  • SOQL クエリは読み込む項目を指定するため、単純な動的フォームでは必要だった addFields() を使用する必要はありません。
  • SOQL クエリは実行時に作成されます。ユーティリティメソッドが項目名のリストを SOQL SELECT ステートメントでの使用に適した文字列に変換します。
  • マークアップでは、フォーム項目は、<apex:repeat> を使用する項目名を反復処理し、項目名変数 cf を動的参照で使用して項目値を取得することで表示されます。各項目は、<apex:outputText><apex:inputText>2 つのコンポーネントで記述される可能性があります。これらのタグ上の表示属性が、2 つのどちらを実際に表示するかを制御します。項目名が文字列「Contact」を含む場合、情報は <apex:inputText> タグに表示され、含まない場合は <apex:outputText> に表示されます。

ユーザがカスタマイズ可能なページでの動的参照の使用

Visualforce 動的バインドの最大の利点は、オブジェクトでどの項目が使用可能かを知らなくてもページを作成できることです。次の例はこの機能を示しています。すべてのオブジェクトで必須の Name 項目を除き、Account オブジェクトの項目を一切 知らずにカスタマイズできる取引先のリストが表示されます。これは、Schema.SobjectType.Account.fields.getMap() を使用してオブジェクトに存在する項目のリストを取得し、Visualforce 動的参照を使用することで可能になります。

この例で示す機能は単純です。メインリストビューには最初、取引先名のみが表示されますが、ユーザは [リストをカスタマイズ] ボタンを使用して、リストに追加する項目を選択できます。ユーザが設定を保存すると、リストビューに戻り、動的に生成された Visualforce ページの追加の列にそれらの項目が表示されます。

項目を知らずにページを作成することは、項目セットによる動的参照を使用する方法でも可能です。

メモ

最初に、DynamicCustomizableListHandler というコントローラ拡張を作成します。
1public class DynamicCustomizableListHandler {
2
3    // Resources we need to hold on to across requests
4    private ApexPages.StandardSetController controller;
5    private PageReference savePage;
6
7    // This is the state for the list "app"
8    private Set<String> unSelectedNames = new Set<String>();
9    private Set<String> selectedNames = new Set<String>();
10    private Set<String> inaccessibleNames = new Set<String>();
11
12    public DynamicCustomizableListHandler(ApexPages.StandardSetController controller) {
13        this.controller = controller;
14        loadFieldsWithVisibility();
15    }
16
17    // Initial load of the fields lists
18    private void loadFieldsWithVisibility() {
19        Map<String, Schema.SobjectField> fields = 
20            Schema.SobjectType.Account.fields.getMap();
21        for (String s : fields.keySet()) {
22            if (s != 'Name') {  // name is always displayed 
23                unSelectedNames.add(s);
24            }
25            if (!fields.get(s).getDescribe().isAccessible()) {
26                inaccessibleNames.add(s);
27            }
28        }
29    }
30
31    // The fields to show in the list
32    // This is what we generate the dynamic references from
33    public List<String> getDisplayFields() { 
34        List<String> displayFields = new List<String>(selectedNames);
35        displayFields.sort();
36        return displayFields;
37    }
38    
39    // Nav: go to customize screen
40    public PageReference customize() {
41        savePage = ApexPages.currentPage();
42        return Page.CustomizeDynamicList;
43    }
44
45    // Nav: return to list view
46    public PageReference show() {
47        // This forces a re-query with the new fields list
48        controller.reset();
49        controller.addFields(getDisplayFields());
50        return savePage; 
51    }
52
53    // Create the select options for the two select lists on the page
54    public List<SelectOption> getSelectedOptions() { 
55        return selectOptionsFromSet(selectedNames);
56    }
57    public List<SelectOption> getUnSelectedOptions() { 
58        return selectOptionsFromSet(unSelectedNames);
59    }
60    
61    private List<SelectOption> selectOptionsFromSet(Set<String> opts) {
62        List<String> optionsList = new List<String>(opts);
63        optionsList.sort();
64        List<SelectOption> options = new List<SelectOption>();
65        for (String s : optionsList) {
66            options.add(new 
67                SelectOption(s, decorateName(s), inaccessibleNames.contains(s)));
68        }
69        return options;
70    }
71
72    private String decorateName(String s) {
73        return inaccessibleNames.contains(s) ? '*' + s : s;
74    }
75
76    // These properties receive the customization form postback data
77    // Each time the [<<] or [>>] button is clicked, these get the contents
78    // of the respective selection lists from the form
79    public transient List<String> selected   { get; set; }
80    public transient List<String> unselected { get; set; }
81
82    // Handle the actual button clicks. Page gets updated via a
83    // rerender on the form
84    public void doAdd() {
85        moveFields(selected, selectedNames, unSelectedNames);
86    }
87    public void doRemove() {
88        moveFields(unselected, unSelectedNames, selectedNames);
89    }
90    
91    private void moveFields(List<String> items, 
92            Set<String> moveTo, Set<String> removeFrom) {
93        for (String s: items) {
94            if( ! inaccessibleNames.contains(s)) {
95                moveTo.add(s);
96                removeFrom.remove(s);
97            }
98        }
99    }
100}

クラスを保存すると、Visualforce ページの欠落に関する指示が表示される場合があります。これは、customize() メソッドでのページ参照が原因です。[クイック修正] リンクをクリックしてページを作成します。コード内の後の個所にあるブロックから Visualforce マークアップが貼り付けられます。

メモ

このクラスでは、次の点に留意してください。
  • 標準コントローラメソッドの addFields() および reset()show() メソッドで使用されます。このメソッドにより、リストビューに戻ります。これらが必要なのは、表示する項目のリストが変更された可能性があり、表示用のデータを読み込むクエリを再実行する必要があるためです。
  • 2 つの action メソッド customize()show() がリストビューからカスタマイズフォームに移動し、再び戻ります。
  • navigation action メソッド後に行われることはすべて、カスタマイズフォーム関連の処理です。これらのメソッドは、コメントに注記されているように、大きく 2 つのグループに分けられます。最初のグループは、カスタマイズフォームで使用される List<SelectOption> リストを提供し、2 つ目のグループは項目をリスト間で移動する 2 つのボタンを処理します。
ここで、次のマークアップで DynamicCustomizableList という Visualforce ページを作成します。
1<apex:page standardController="Account" recordSetVar="accountList"
2           extensions="DynamicCustomizableListHandler">
3    <br/>
4    <apex:form >
5
6    <!-- View selection widget, uses StandardController methods -->
7    <apex:pageBlock>
8        <apex:outputLabel value="Select Accounts View: " for="viewsList"/>
9        <apex:selectList id="viewsList" size="1" value="{!filterId}">
10            <apex:actionSupport event="onchange" rerender="theTable"/>
11            <apex:selectOptions value="{!listViewOptions}"/>
12        </apex:selectList>
13    </apex:pageblock>
14
15    <!-- This list of accounts has customizable columns -->
16    <apex:pageBlock title="Accounts" mode="edit">
17        <apex:pageMessages />
18        <apex:panelGroup id="theTable">
19            <apex:pageBlockTable value="{!accountList}" var="acct">
20                <apex:column value="{!acct.Name}"/>
21                <!-- This is the dynamic reference part -->
22                <apex:repeat value="{!displayFields}" var="f">
23                    <apex:column value="{!acct[f]}"/>
24                </apex:repeat>
25            </apex:pageBlockTable>
26        </apex:panelGroup>
27    </apex:pageBlock>
28
29    <br/>
30    <apex:commandButton value="Customize List" action="{!customize}"/>
31
32    </apex:form>
33</apex:page>
このページには、組織内の取引先のリストが表示されます。上部の <apex:pageBlock> は、取引先用に定義されたビューの標準ドロップダウンリストを提供します。これらは、標準 Salesforce 取引先ページでユーザに表示されるのと同じビューです。このビューウィジェットでは、StandardSetController で提供されるメソッドを使用します。

2 つ目の <apex:pageBlock> には、<apex:repeat> で追加された列が含まれる <apex:pageBlockTable> が保持されます。繰り返しコンポーネントのすべての列は、取引先項目 {!acct[f]} への動的参照を使用して、ユーザがカスタム選択した項目を表示します。

このミニアプリケーションの最後の部分はカスタマイズフォームです。CustomizeDynamicList というページを作成します。このページは、コントローラ拡張の作成時にすでに作成されている場合があります。次に貼り付けます。
1<apex:page standardController="Account" recordSetVar="ignored"
2           extensions="DynamicCustomizableListHandler">
3    <br/>
4    <apex:form >
5
6    <apex:pageBlock title="Select Fields to Display" id="selectionBlock">
7        <apex:pageMessages />
8        <apex:panelGrid columns="3">
9            <apex:selectList id="unselected_list" required="false" 
10                value="{!selected}" multiselect="true" size="20" style="width:250px">
11                <apex:selectOptions value="{!unSelectedOptions}"/>
12            </apex:selectList>
13            <apex:panelGroup >
14                <apex:commandButton value=">>" 
15                    action="{!doAdd}" rerender="selectionBlock"/>
16                <br/>
17                <apex:commandButton value="<<" 
18                    action="{!doRemove}" rerender="selectionBlock"/>
19            </apex:panelGroup>
20            <apex:selectList id="selected_list" required="false" 
21                value="{!unselected}" multiselect="true" size="20" style="width:250px">
22                <apex:selectOptions value="{!selectedOptions}"/>
23            </apex:selectList>
24        </apex:panelGrid>
25        <em>Note: Fields marked <strong>*</strong> are inaccessible to your account</em>
26    </apex:pageBlock>
27    
28    <br/>
29    <apex:commandButton value="Show These Fields" action="{!show}"/>
30
31    </apex:form>
32
33</apex:page>
この単純な設定ページには、2 つのリストが表示されます。ユーザは、左側の使用可能な項目のリストから、右側の表示する項目のリストに項目を移動します。[これらの項目を表示] をクリックすると、リスト自体に戻ります。
このマークアップでは、次の点に留意してください。
  • このページは、取引先が表示されていなくても、リストビューと同じ標準コントローラを使用します。これは、表示する項目のリストが含まれるビューステートを維持するために必要です。このフォームでユーザの設定がカスタム設定のような永続的なものに保存された場合、この操作は不要になります。
  • 最初のリストは、getUnSelectedOptions() メソッドをコールすることで入力されます。フォームが (2 つの <apex:commandButton> コンポーネントのいずれかを経由して) 送信されると、フォーム送信時に選択されたリストの値が selected プロパティに保存されます。対応するコードが、もう一方のリストを処理します。
  • これらの移動する項目の「デルタ」リストは、クリックされたボタンに応じて doAdd() または doRemove() メソッドで処理されます。
コントローラ拡張とこれらのページを作成し、組織内の /apex/DynamicCustomizableList に移動すると、次に似たシーケンスが表示されます。
  1. デフォルト状態のカスタマイズ可能なリストに取引先名項目のみが表示されます。動的 Visualforce: 標準リストコントローラを使用した取引先のリスト[リストをカスタマイズ] をクリックします。
  2. 表示設定画面が表示されます。動的 Visualforce: 表示する項目のリストの表示一部の項目を右側のリストに移動し、[これらの項目を表示] をクリックします。
  3. カスタマイズされたリストビューが表示されます。動的 Visualforce: 選択された取引先のリストと表示する項目、すべて動的に表示