【Salesforce】Visualforceを高速化した
【Salesforce】Visualforceを高速化した
Visualforceで作られた大量のレコードを表示・更新する画面がありました。
その画面でエラーが出ているという話があったため、修正を行いました。
Apex CPU time limit exceeded
以前書いたような方法でApexでの処理を修正していきます。
http://www.subnetwork.jp/blog/?p=559
Apexの処理をかなり減らすことが出来ましたが、それでもエラーが発生しました。
もう少し調べてみると、Visualforce側の書き方に問題がありました。
例えば、このような記述があったとします。
こちらはVisualforce側です。
<apex:repeat value="{!AccountList}" var="account">
<div class="accInfo">
<select id="Industry" >
<apex:repeat value="{!IndustryOptionList}" var="{!Industry}">
<apex:outputPanel layout="none" rendered="{!account.Industry == Industry.Value}" >
<option value="Industry.Value" selected="selected" >Industry.Label</option>
</apex:outputPanel>
<apex:outputPanel layout="none" rendered="{!account.Industry != Industry.Value}" >
<option value="Industry.Value" >Industry.Label</option>
</apex:outputPanel>
</apex:repeat>
</select>
</div>
</apex:repeat>
こちらはApex側です。
/**
* 取引先のリスト
*/
public List<Account> getAccountList(){
return [select Id, Industry from Account];
}
/**
* 取引先の業種項目選択リスト
*/
public List<SelectOption> getIndustryOptionList(){
List<SelectOption> optionList;
List<Schema.PicklistEntry> industryList;
// 業種のスキーマを取得する
Industry = Account.Industry.getDescribe().getPicklistValues();
// 選択リストを作成する
optionList = new List<SelectOption>();
for(Schema.PicklistEntry industry : industryList){
optionList.add(new SelectOption(industry.getValue(), industry.getLabel()));
}
return optionList;
}
取引先の業種の選択リストを表示しています。
理由があって、inputFieldを使うことができなかったようです。
大体こんな形で処理が行われていました。
パッと見は問題なさそうですが、Visualforce内で選択リストを作成する際に使用する「getIndustryOptionList()」が何度も呼び出されていました。
今回は大量のレコードを表示していたため、そのレコードの数だけ「getIndustryOptionList()」が呼び出されていたわけです。
この選択リストを表示するか、しないかで数秒の差があったため、修正しました。
<apex:variable value="{!StrOptionList}" var="strOptionList" />
<apex:repeat value="{!AccountList}" var="account">
<div class="accInfo">
<select id="Industry" data-accountId="{!account.Id}" >
<apex:outputText value="{!strOptionList}" escape="false" />
</select>
</div>
</apex:repeat>
// 同期用
<div id="syncAccountList" style="display: none;" >
<apex:repeat value="{!AccountList}" var="account">
<input type="hidden" class="{!account.Id}" value="{!account.Industry}" />
</apex:repeat>
</div>
<script>
// 画面表示の際に、選択リストのデフォルト選択値を設定する
$(document).ready(function(){
// 表示する選択リスト
$('.accInfo select').each(function(){
var selector;
var optionSelector;
var selectValue;
// 同期用inputのセレクタを取得する
selector = '.' + $(this).data('accountId');
// 業種を取得する
selectValue = $('#syncAccountList').find(selector).val();
// 選択リストのオプション取得用のセレクタを作成する
optionSelector = 'option[value="' + selectValue + '"]';
// 選択リストのオプションを選択状態にする
$(this).find(optionSelector).prop('selected', 'true');
$(this).find(optionSelector).attr('selected', 'selected');
}):
});
</script>
/**
* 取引先のリスト
*/
public List<Account> getAccountList(){
return [select Id, Industry from Account];
}
/**
* 取引先の業種項目選択リスト
*/
public List<SelectOption> getIndustryOptionList(){
List<SelectOption> optionList;
List<Schema.PicklistEntry> industryList;
// 業種のスキーマを取得する
Industry = Account.Industry.getDescribe().getPicklistValues();
// 選択リストを作成する
optionList = new List<SelectOption>();
for(Schema.PicklistEntry industry : industryList){
optionList.add(new SelectOption(industry.getValue(), industry.getLabel()));
}
return optionList;
}
/**
* 選択リストのオプションをHTMLソースで作成する
*/
public String getStrOptionList(){
String strOptionList;
List<SelectOption> optionList;
// 選択リストを作成する
optionList = this.getIndustryOptionList();
// 文字列でHTMLソースを作成する
strOptionList = '';
for(SelectOption option : optionList){
strOptionList += '<option value="' + option.getValue() + '" >';
strOptionList += option.getLabel();
strOptionList += '</option>';
}
retuen strOptionList;
}
処理としては、Apex側で選択リストのオプションを文字列で作成しています。
Visualforce側の変数に格納し、その変数を使いまわすことで選択リストを作成する処理の呼び出しを減らしています。
そのままでは選択リストの初期値が設定されないため、JavaScriptにて対応する値を選択状態にしています。
Visualforce内での繰り返し処理にも注意しなければいけませんね。
もっと良い方法もあるかと思いますが、今回は時間もなかったのでこの方法で解決しました。
No comments.