本部長は管理ができない

Salesforceに関わっているエンジニアの技術メモ。ときどきそれ以外。

一覧をソートしてみる

f:id:gren_dken:20140925132128j:plain
ガストロバル パロマ+1のハンバーグランチ。
どう食べれば良いか1分ほど悩んだ。


一覧のヘッダをクリックしたときに、クリックした項目でソートする処理を作ってみた。

f:id:gren_dken:20141108182852p:plain
姓項目を昇順でソートした状態


  • ソート結果はクエリを実行し取得
  • ヘッダの文字列部分は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>
    &nbsp;
    <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を使える