TL;DR
apex:attribute
の値をコントローラのコンストラクタで使用することはできません。
ページ読み込み時にapex:attribute
の値をコントローラで使用する場合はgetter/setterまたはプロパティを使用します。
はじめに
Visualforceコンポーネントにapex:attribute
要素を追加すると、Visualforceから呼び出すときに属性に値を設定できます。
下記の例ではVisualforceコンポーネントにカスタムのlogoLinkUrl属性を定義しています。
<apex:page>
<c:sampleHeader logoLinkUrl="https://www.google.com/"/>
</apex:page>
<apex:component>
<apex:attribute name="logoLinkUrl" type="String" description="ヘッダーアイコンのリンク先" />
<header>
<a href="{!logoLinkUrl}" class="logo"><img src="/img/logo214.svg"/></a>
</header>
</apex:component>
このapex:attribute
は、assignTo属性によってコントローラの変数に値を代入1することができます。
しかし、コンストラクタでこの値を使うと値が null となっていしまいます。
また、apex:component要素にはApexメソッドを呼び出すaction属性はありません。
Visualforceページにアクセスしたときの実行順序について
Visualforceページにアクセスした時、下記の順で処理が行われます。
Visualforceコンポーネントのコンストラクタ(2)は、apex:attribute
のassignTo
(3)より先に実行されます。
- Visualforceページのコントローラのコンストラクタ。
- Visualforceコンポーネントのコンストラクタ。
- Visualforceコンポーネントの
apex:attribute
のassignTo属性(setterメソッド)。 - apex:pageコンポーネントのaction属性
- Visualforceページ・Visualforceコンポーネントのすべての式、getterメソッド、 setterメソッド
- (apex:formコンポーネントがある場合) ビューステートの作成
- HTMLの返却および、クライアントサイドの処理(javascriptなど)
Visualforceページ読み込み時にattributeの値をコントローラで使用する
attributeの値を使用するにはassignTo属性で指定したsetterを使用するか、getterで使用します。
「Visualforceページにアクセスしたときの実行順序について」でみたとおり、
assignTo
により割り当てられている変数のsetterメソッドが呼び出され、
assignTo
のあとにgetterメソッドが使用されるからです。
下記では、選択リスト項目をラジオボタンで表示するVisualforceコンポーネントで例をあげます。
getterメソッドの例 (選択リスト項目をラジオボタンで表示)
getterメソッド、getアクセス機構をもつプロパティの実行は、apex:attributeのassignToよりあとに行われます。
なのでgetterメソッドを使用すれば、ページ読み込み時にapex:attributeの値を使用することができます。
<apex:page standardController="Account">
<apex:form>
<apex:pageBlock title="取引先" mode="edit">
<apex:pageBlockButtons>
<apex:commandButton action="{!quicksave}" value="保存" />
</apex:pageBlockButtons>
<apex:pageBlockSection title="ラジオボタンテスト" columns="1">
<apex:outputField value="{!account.name}" />
<apex:pageBlockSectionItem>
<apex:outputLabel value="区分" for="radio" />
<!-- ↓ ここでVisualforceコンポーネントを呼び出しています。 -->
<c:InputRadio value="{!Account.Type__c}" object="Account" field="Type__c" addNotSelect="true" id="radio"/>
</apex:pageBlockSectionItem>
<!-- ↓ 比較のための選択リストのinputFieldです。 -->
<apex:inputField value="{!account.Industry}" />
</apex:pageBlockSection>
</apex:pageBlock>
</apex:form>
</apex:page>
<!-- 選択リスト項目 ラジオボタン表示 -->
<apex:component controller="InputRadioCtrl">
<apex:attribute name="value" type="String" assignTo="{!selectedValue}" description="" required="true"/>
<apex:attribute name="addNotSelect" type="Boolean" assignTo="{!isAddNotSelect}" description="選択肢に未選択を表示する" default="false"/>
<!-- ↓ ここで指定したSObjectField(選択リスト)の選択リスト値をコントローラで取得し、 apex:selectOptions で使用します。 -->
<apex:attribute name="object" type="String" assignTo="{!objectStr}" description="SObject名をAPI参照名で指定" required="true"/>
<apex:attribute name="field" type="String" assignTo="{!fieldStr}" description="選択リストの項目名をAPI参照名で指定" required="true"/>
<apex:selectRadio value="{!value}" disabled="false" rendered="{!options.size!=0}" id="radio">
<!-- ↓ apex:attributeの値をもとにコントローラで取得した値を使用しています -->
<apex:selectOptions value="{!options}"/>
</apex:selectRadio>
</apex:component>
public with sharing class InputRadioCtrl {
public String selectedValue { get; set; }
public Boolean isAddNotSelect { get; set; }
public String objectStr { get; set; }
public String fieldStr { get; set; }
// ↓ このgetterメソッドで、Visualforceページからapex:attributeを通して設定した値を使用して、選択肢を返しています。
/** 選択肢の取得 */
public List<SelectOption> getOptions() {
Schema.SObjectField field = toSObjectField(objectStr, fieldStr);
List<SelectOption> soList = new List<SelectOption>();
if (field.getDescribe().isUpdateable()) {
soList = getPickListOptions(field);
if (String.isNotBlank(selectedValue) && !containsSelectOption(soList, selectedValue)) {
// 無効な値が設定されているときは表示させる
soList.add(new SelectOption(selectedValue, selectedValue));
}
if (isAddNotSelect) {
soList.add(new SelectOption('', '未選択'));
}
}
return soList;
}
/** SObjectField変換メソッド */
private Schema.SObjectField toSObjectField(String objectName, String fieldName) {
Schema.DescribeSObjectResult dsr = Schema.describeSObjects(new List<String>{objectName})[0];
return dsr.fields.getMap().get(fieldName);
}
/** 選択リスト取得メソッド */
private List<SelectOption> getPickListOptions(Schema.SObjectField field) {
List<SelectOption> opList = new List<SelectOption>();
for (Schema.PicklistEntry pe : field.getDescribe().getPickListValues()) {
if (!pe.isActive()) { continue; }
opList.add(new SelectOption(pe.getValue(),pe.getLabel()));
}
return opList;
}
/** 値が選択肢に含まれているか */
private Boolean containsSelectOption(List<SelectOption> pOptions, String pValue) {
List<String> values = new List<String>();
for (SelectOption so : pOptions) {
values.add(so.getValue());
}
return values.contains(pValue);
}
}
setterメソッドの例 (選択リスト項目をラジオボタンで表示)
assignToで指定している変数のsetterメソッド、あるいはsetをもつプロパティを使用することで、ページ読み込み時にapex:attribute
の値を使用できます。
下記ではsetをもつプロパティの例です。
getterメソッドの例とおなじ機能のものをsetをもつプロパティを使用して書き換えています。
<apex:page standardController="Account">
<apex:form>
<apex:pageBlock title="取引先" mode="edit">
<apex:pageBlockButtons>
<apex:commandButton action="{!quicksave}" value="保存" />
</apex:pageBlockButtons>
<apex:pageBlockSection title="ラジオボタンテスト" columns="1">
<apex:outputField value="{!account.name}" />
<apex:pageBlockSectionItem>
<apex:outputLabel value="区分" for="radio" />
<!-- ↓ ここでVisualforceコンポーネントを呼び出しています。 -->
<c:InputRadio value="{!Account.Type__c}" field="Account.Type__c" addNotSelect="true" id="radio"/>
</apex:pageBlockSectionItem>
<!-- ↓ 比較のための選択リストのinputFieldです。 -->
<apex:inputField value="{!account.Industry}" />
</apex:pageBlockSection>
</apex:pageBlock>
</apex:form>
</apex:page>
<!-- 選択リスト項目 ラジオボタン表示 -->
<apex:component controller="InputRadioCtrl">
<apex:attribute name="value" type="String" assignTo="{!selectedValue}" description="" required="true"/>
<apex:attribute name="addNotSelect" type="Boolean" assignTo="{!isAddNotSelect}" description="選択肢に未選択を表示する" default="false"/>
<!-- ↓ ここで指定したSObjectField(選択リスト)の選択リスト値をコントローラで取得し、 apex:selectOptions で使用します。 -->
<apex:attribute name="field" type="String" assignTo="{!fieldStr}" description="項目を「ObjectApiName.FieldApiName」の形式で指定" required="true"/>
<apex:selectRadio value="{!value}" disabled="false" rendered="{!options.size!=0}" id="radio">
<!-- ↓ apex:attributeの値をもとにコントローラで取得した値を使用しています -->
<apex:selectOptions value="{!options}"/>
</apex:selectRadio>
</apex:component>
public with sharing class InputRadioCtrl {
public String selectedValue { get; set; }
public Boolean isAddNotSelect { get; set; }
public List<SelectOption> options { get; private set; }
// ↓ assignToで代入されるプロパティです。
// ここのsetアクセス機構で、設定された値(value)をつかって他の変数に値を設定しています。
public String fieldStr { get; set {
fieldStr = value;
Schema.SObjectField field = toSObjectField(value);
setSelectOption(field);
}}
/** 選択肢の設定 */
private void setSelectOption(Schema.SObjectField field) {
List<SelectOption> soList = new List<SelectOption>();
if (field.getDescribe().isUpdateable()) {
soList = getPickListOptions(field);
if (String.isNotBlank(selectedValue) && !containsSelectOption(soList, selectedValue)) {
// 無効な値が設定されているときは表示させる
soList.add(new SelectOption(selectedValue, selectedValue));
}
if (isAddNotSelect) {
soList.add(new SelectOption('', '未選択'));
}
}
this.options = soList;
}
/** SObjectField変換メソッド */
private Schema.SObjectField toSObjectField(String value) {
List<String> objAndField = value.split('\\.');
String objectName = objAndField[0];
String fieldName = objAndField[1];
Schema.DescribeSObjectResult dsr = Schema.describeSObjects(new List<String>{objectName})[0];
return dsr.fields.getMap().get(fieldName);
}
/** 選択リスト取得メソッド */
private List<SelectOption> getPickListOptions(Schema.SObjectField field) {
List<SelectOption> opList = new List<SelectOption>();
for (Schema.PicklistEntry pe : field.getDescribe().getPickListValues()) {
if (!pe.isActive()) { continue; }
opList.add(new SelectOption(pe.getValue(),pe.getLabel()));
}
return opList;
}
/** 値が選択肢に含まれているか */
private Boolean containsSelectOption(List<SelectOption> pOptions, String pValue) {
List<String> values = new List<String>();
for (SelectOption so : pOptions) {
values.add(so.getValue());
}
return values.contains(pValue);
}
}
参考
- Visualforce ページ内の実行順序 | Visualforce 開発者ガイド
- Apexの実行順序 - TECH BLOG | 株式会社テラスカイ
- [salesforce]Visualforceページ表示処理の実行順序 – deferloader
- apex:attribute | Visualforce 開発者ガイド | Salesforce Developers
-
Visualforceコンポーネントからコントローラに apex:attribute の value を渡す。その逆はできません。 ↩