一覧をソートしてみる
ガストロバル パロマ+1のハンバーグランチ。
どう食べれば良いか1分ほど悩んだ。
一覧のヘッダをクリックしたときに、クリックした項目でソートする処理を作ってみた。
姓項目を昇順でソートした状態
- ソート結果はクエリを実行し取得
- ヘッダの文字列部分はApexコンポーネントを使用
- どの項目を、昇順or降順でソートしているかの状態を保持するクラスを作成
- これを、項目のAPI参照名をキーにしたMapで管理
ページ
<apex:page controller="SortListController" action="{!doInit}"> <apex:form > <apex:pageBlock title="取引先責任者一覧"> <apex:pageBlockTable value="{!contacts}" var="cont" id="dataTable"> <apex:column> <apex:facet name="header"> <c:SortHeader headerLabel="{!$ObjectType.Contact.Fields.LastName.Label}" sortMethod="{!doSort}" sortColumn="LastName" sortIcon="{!sortStatusMap['LastName'].sortIcon}" rerenderId="dataTable" /> </apex:facet> <apex:outputField value="{!cont.LastName}" /> </apex:column> <apex:column> <apex:facet name="header"> <c:SortHeader headerLabel="{!$ObjectType.Contact.Fields.FirstName.Label}" sortMethod="{!doSort}" sortColumn="FirstName" sortIcon="{!sortStatusMap['FirstName'].sortIcon}" rerenderId="dataTable" /> </apex:facet> <apex:outputField value="{!cont.FirstName}" /> </apex:column> <apex:column> <apex:facet name="header"> <c:SortHeader headerLabel="{!$ObjectType.Contact.Fields.Title.Label}" sortMethod="{!doSort}" sortColumn="Title" sortIcon="{!sortStatusMap['Title'].sortIcon}" rerenderId="dataTable" /> </apex:facet> <apex:outputField value="{!cont.Title}" /> </apex:column> <apex:column> <apex:facet name="header"> <c:SortHeader headerLabel="{!$ObjectType.Contact.Fields.Phone.Label}" sortMethod="{!doSort}" sortColumn="Phone" sortIcon="{!sortStatusMap['Phone'].sortIcon}" rerenderId="dataTable" /> </apex:facet> <apex:outputField value="{!cont.Phone}" /> </apex:column> <apex:column> <apex:facet name="header"> <c:SortHeader headerLabel="{!$ObjectType.Contact.Fields.Email.Label}" sortMethod="{!doSort}" sortColumn="Email" sortIcon="{!sortStatusMap['Email'].sortIcon}" rerenderId="dataTable" /> </apex:facet> <apex:outputField value="{!cont.Email}" /> </apex:column> </apex:pageBlockTable> </apex:pageBlock> </apex:form> </apex:page>
コンポーネント
<apex:component > <apex:attribute name="headerLabel" type="String" description="ヘッダに表示する文字列" /> <apex:attribute name="sortMethod" type="ApexPages.Action" description="ヘッダ文字列押下時に実行するメソッド" required="true" /> <apex:attribute name="sortColumn" type="String" description="ソート対象のカラム" required="true" /> <apex:attribute name="sortIcon" type="String" description="項目の右側に表示するソート方向を示す文字" required="true" /> <apex:attribute name="rerenderId" type="String" description="ヘッダ文字列押下時の再描画領域" /> <apex:commandLink value="{!headerLabel}" action="{!sortMethod}" reRender="{!rerenderId}" > <apex:param value="{!sortColumn}" name="sortColumn" /> </apex:commandLink> <apex:outputText value="{!sortIcon}" style="font-size: 80%; font-weight: normal;" /> </apex:component>
コントローラ
public with sharing class SortListController { /** ベースのSELECT文 */ private String baseQuery = 'SELECT Id, FirstName, LastName, Title, Phone, Email FROM Contact '; /** 表示データ */ public List<Contact> contacts {get; set;} /** カラムごとのソート状態 */ public Map<String, SortStatus> sortStatusMap {get; set;} /** * コンストラクタ */ public SortListController() { initSortStatus(); } /** * 初期処理 */ public PageReference doInit() { this.contacts = Database.query(this.baseQuery); return null; } /** * ソート(ヘッダ押下時) */ public PageReference doSort() { // ソート対象のカラムを取得 String sortColumn = ApexPages.currentPage().getParameters().get('sortColumn'); // ソート状態を変更 changeSortStatus(sortColumn); // ソート条件を付加してデータ取得 String query = this.baseQuery; query += 'ORDER BY ' + sortColumn + ' ' + this.sortStatusMap.get(sortColumn).sortWord; this.contacts = Database.query(query); return null; } /** * ソート状態の初期化 */ private void initSortStatus() { this.sortStatusMap = new Map<String, SortStatus> { 'FirstName' => new SortStatus(), 'LastName' => new SortStatus(), 'Title' => new SortStatus(), 'Phone' => new SortStatus(), 'Email' => new SortStatus() }; } /** * ソート状態の変更 */ private void changeSortStatus(String sortColumn){ for (String key : this.sortStatusMap.keySet()) { if (key == sortColumn) { this.sortStatusMap.get(key).change(); } else { this.sortStatusMap.get(key).init(); } } } }
ソート状態を保持するクラス
/** * ソート状態を保持するクラス */ public class SortStatus { /** ソート順 定義:ソートなし */ public static Integer SORT_ORDER_NONE = 0; /** ソート順 定義:昇順ソート */ public static Integer SORT_ORDER_ASC = 1; /** ソート順 定義:降順ソート */ public static Integer SORT_ORDER_DESC = 2; /** ソート順 */ public Integer sortOrder {get; set;} /** 一覧のヘッダに表示するソート方向を示す文字(↑↓など) */ public String sortIcon {get; set;} /** SOQLのソート句 */ public String sortWord {get; set;} /** * コンストラクタ */ public SortStatus() { init(); } /** * メンバ変数初期化 */ public void init() { this.sortOrder = SORT_ORDER_NONE; this.sortIcon = null; this.sortWord = null; } /** * ソート順の変更 */ public void change() { if (this.sortOrder == SORT_ORDER_NONE || this.sortOrder == SORT_ORDER_DESC) { this.sortOrder = SORT_ORDER_ASC; this.sortIcon = '↑'; this.sortWord = 'ASC'; } else if (this.sortOrder == SORT_ORDER_ASC) { this.sortOrder = SORT_ORDER_DESC; this.sortIcon = '↓'; this.sortWord = 'DESC'; } else { this.sortOrder = SORT_ORDER_NONE; this.sortIcon = null; this.sortWord = null; } } }
実装時の驚き
- apex:attributeにメソッドを渡せる
- VisualforceでMapを使える
EclipseでコーディングしたApexコードが文字化け
Eclipse、Force.comIDEを新規インストールし、Apexクラスをコーディングしていたら動作がおかしい。
原因は文字列の記述。
Eclipseでは文字化けしていないが、Salesforceのエディタでは文字化けしている。
以下の「あいうえお」が文字化けする。
String tmp = 'あいうえお';
対応策は、eclipse.iniの「-vmargs」以降の行に下記のパラメータを追加。
-Dfile.encoding=utf-8
以上、メモ。
apex:inputTextでカレンダーと日付リンクを表示
ステーキくにのランチハンバーグ。美味い。
日付型、日付/時間型の入力の際に表示されるカレンダーとリンクは、Visualforceページだとapex:inputFieldの場合に表示される。
apex:inputTextでコントローラのDate型変数をvalueに設定してもカレンダーとリンクは表示されない。
日付型のinputFieldのHTMLを確認すると、カレンダーはDatePicker#pickDate、リンクはDatePicker#insertDateを使用している。
- ページ
<apex:inputText id="fooDate" value="{!fooDate}" size="12" onfocus="javascript:DatePicker.pickDate(true, '{!$Component.fooDate}', false)" /> [ <apex:outputLink value="javascript:DatePicker.insertDate('{!fooDate}', '{!$Component.fooDate}', true);"> {!fooDate} </apex:outputLink> ] <p style="display: none;"><apex:inputField value="{!obj.Date__c}"/></p>
- コントローラ
// 今日日付をセット this.fooDate = Datetime.now().format('yyyy/MM/dd', 'JST');
ただしカレンダーのHTMLは、ページ内に日付型か日付/時間型の入力フィールドがないとページにロードされないため、下記のように非表示でinputFieldを置く必要がある。
<p style="display: none;"><apex:inputField value="{!obj.Date__c}"/></p>
ちょっとしたバッチ
すた丼。
※本文とまったく関係ありません。
作業中に使っているファイルやフォルダに関するバッチ。
Windows用。
バッチファイルにD&Dして使っている。
現在日時のフォルダを作成
@echo off rem 日付と時刻の取得 set currentDate=%DATE:/=% set currentTime=%TIME::=% set currentTime=%currentTime: =0% set currentTime=%currentTime:~0,6% mkdir "%~1\%currentDate%_%currentTime%"
今日日付に連番を付与したフォルダを作成
@echo off set /A dirNo=0 set MAX_DIR_NO=9 set basePath=%~1\%DATE:/=% :LOOP_START rem フォルダ名生成 if %dirNo% equ 0 ( set dirPath="%basePath%" ) else ( set dirPath="%basePath%_0%dirNo%" ) rem フォルダがなければ作成 if not exist %dirPath% ( mkdir %dirPath% goto LOOP_END ) rem インクリメント if %dirNo%==%MAX_DIR_NO% (goto LOOP_END) set /A dirNo=dirNo+1 goto LOOP_START :LOOP_END
ファイルに現在日時を付与してコピー
複数ファイルにも対応。
「copy」を「move」にすればリネームになる。
@echo off rem 日付と時刻の取得 set currentDate=%DATE:/=% set currentTime=%TIME::=% set currentTime=%currentTime: =0% set currentTime=%currentTime:~0,6% for %%i in (%*) do ( copy "%%~i" "%%~dpni_%currentDate%_%currentTime%%%~xi" )
フォルダに現在日時を付与してコピー
@echo off rem 日付と時刻の取得 set currentDate=%DATE:/=% set currentTime=%TIME::=% set currentTime=%currentTime: =0% set currentTime=%currentTime:~0,6% rem コピー xcopy "%~1" "%~1_%currentDate%_%currentTime%\" /E /H /K
コレクションの文字列化
一風堂 赤丸新味。
※本文と何ら関係ありません。
コレクションの中身を、ループを使わず展開出来ないか試したら、String#valueOfでさっくり出来た。
system#debugで出力される文字列と同じになる。
List
- コード
List<String> tmpList = new List<String>{'Red', 'Yellow', 'Pink', 'Green', 'Purple'}; String strList = String.valueOf(tmpList); system.debug('★List 1:' + tmpList); system.debug('★List 2:' + strList);
- 結果
★List 1:(Red, Yellow, Pink, Green, Purple) ★List 2:(Red, Yellow, Pink, Green, Purple)
Set
- コード
Set<String> tmpSet = new Set<String>{'Red', 'Yellow', 'Pink', 'Green', 'Purple'}; String strSet = String.valueOf(tmpSet); system.debug('★Set 1:' + tmpSet); system.debug('★Set 2:' + strSet);
- 結果
★Set 1:{Green, Pink, Purple, Red, Yellow} ★Set 2:{Green, Pink, Purple, Red, Yellow}
Map
- コード
Map<Integer, String> tmpMap = new Map<Integer, String>{1 => 'Red', 2 => 'Yellow', 3 => 'Pink', 4 => 'Green', 5 => 'Purple'}; String strMap = String.valueOf(tmpMap); system.debug('★Map 1:' + tmpMap); system.debug('★Map 2:' + strMap);
- 結果
★Map 1:{1=Red, 2=Yellow, 3=Pink, 4=Green, 5=Purple} ★Map 2:{1=Red, 2=Yellow, 3=Pink, 4=Green, 5=Purple}
コレクションのサイズが大きいと途中から省略されるので、出力サイズの検討が必要。
(~~Green, Purple, ...)
ファイルアップロード時のビューステートエラー対応
Apexでメモ&添付ファイルに135KB以上のファイルをアップロードした際に、ビューステートエラーになったので、対応方法をメモ。
public class uploadFileController{ public Attachment attach {get; set;} public uploadFileController(){ attach = new Attachment(); } public void uploadFile(){ attach.Parentid = 'XXXXXXX'; try { insert attach; System.debug('アップロード完了'); } catch (Exception e) { System.debug('アップロードエラー'); System.debug(e.getMessage()); } finally { // 135.0KB以上のファイルを指定した場合、nullにしないとビューステートエラーになる attach.Body = null; } return; } }
- insertが完了したら、Bodyをnullにする。
- ドキュメントも同じと思われる(未実験)。
ページネーション
ApexとVisualforceでページネーション(ページング、ページ送り)を作ってみた。
※この記事を参考にさせていただきました。
StandardSetController | Official Blog of CloudClickware
表示するリンク
- 最初のページ
- 最後のページ
- 前のページ
- 次のページ
- 現在ページを中心としたページ番号
ページ番号の表示数は定数で指定。
ページリンクのデザイン
<< 最初 | < 前 | 1 | 2 | 3 | 4 | 5 | 次 > | 最後 >>
コントローラ
public with sharing class paginationController{ // 1ページあたりの表示件数 private static final Integer ROW_PER_PAGE = 3; // 表示するリンクの数(現在ページを中心に前後に表示する数) private static final Integer NUMBER_OF_LINKS = 2; private ApexPages.StandardSetController setCon; // クリックしたページ番号 public Integer clickPageNumber {get; set;} // コンストラクタ public paginationController(){ setCon = new ApexPages.StandardSetController([SELECT Id, FirstName, LastName, Title, Phone, Email FROM Contact]); this.setCon.setPageSize(ROW_PER_PAGE); } // 指定のページへ移動 public void doPageNumber(){ this.setCon.setPageNumber(this.clickPageNumber); } // 前のページへ移動 public void doPrevious(){ this.setCon.previous(); } // 次のページへ移動 public void doNext(){ this.setCon.next(); } // 最初のページへ移動 public void doFirst(){ this.setCon.first(); } // 最後のページへ移動 public void doLast(){ this.setCon.last(); } // 前のページが存在するか public Boolean getHasPrevious(){ return this.setCon.getHasPrevious(); } // 次のページが存在するか public Boolean getHasNext(){ return this.setCon.getHasNext(); } // 最初のページか public Boolean getIsFirst(){ return this.setCon.getPageNumber() == 1; } // 最後のページか public Boolean getIsLast(){ return this.setCon.getPageNumber() == this.getTotalPages(); } // 現在のページを取得 public Integer getPageNumber(){ return this.setCon.getPageNumber(); } // ページ数を取得 public Integer getTotalPages(){ Decimal pages = Decimal.valueOf(this.setCon.getResultSize()) / Decimal.valueOf(this.setCon.getPageSize()); return Integer.valueOf(pages.round(System.RoundingMode.CEILING)); } // ページ番号のリンクを取得 public List<Integer> getPageNumberLinks(){ Integer startPageNumber = this.setCon.getPageNumber() - NUMBER_OF_LINKS; if(startPageNumber <= 0){ startPageNumber = 1; } Integer endPageNumber = this.setCon.getPageNumber() + NUMBER_OF_LINKS; if(endPageNumber > this.getTotalPages()){ endPageNumber = this.getTotalPages(); } List<Integer> links = new List<Integer>(); for(Integer i = startPageNumber; i <= endPageNumber; i++){ links.add(i); } return links; } // 表示するレコードを取得 public List<Contact> getContacts(){ return (List<Contact>)setCon.getRecords(); } }
ページ
<apex:page controller="paginationController"> <apex:form > <apex:pageBlock > <apex:pageBlockTable value="{!contacts}" var="cont"> <apex:column value="{!cont.FirstName}"/> <apex:column value="{!cont.LastName}"/> <apex:column value="{!cont.Title}"/> <apex:column value="{!cont.Phone}"/> <apex:column value="{!cont.Email}"/> </apex:pageBlockTable> <br/> <!-- ページネーション --> <apex:commandLink value="<< 最初" rendered="{!!isFirst}" action="{!doFirst}" /> <apex:outputLabel value="<< 最初" rendered="{!isFirst}" /> <apex:outputLabel value=" | " /> <apex:commandLink value="< 前" rendered="{!hasPrevious}" action="{!doPrevious}" /> <apex:outputLabel value="< 前" rendered="{!!hasPrevious}" /> <apex:outputLabel value=" | " /> <apex:repeat value="{!pageNumberLinks}" var="links"> <apex:commandLink value="{!links}" rendered="{!links != pageNumber}" action="{!doPageNumber}" > <apex:param value="{!links}" name="clickPageNumber" assignTo="{!clickPageNumber}" /> </apex:commandLink> <apex:outputLabel value="{!links}" rendered="{!links == pageNumber}" /> <apex:outputLabel value=" | " /> </apex:repeat> <apex:commandLink value="次 >" rendered="{!hasNext}" action="{!doNext}" /> <apex:outputLabel value="次 >" rendered="{!!hasNext}" /> <apex:outputLabel value=" | " /> <apex:commandLink value="最後 >>" rendered="{!!isLast}" action="{!doLast}" /> <apex:outputLabel value="最後 >>" rendered="{!isLast}" /> </apex:pageBlock> </apex:form> </apex:page>