ページネーションとソート含むカスタムデータテーブル

Salesforce の関連リストにデータテーブルの形でデータリストをきれいに表示する方法を紹介します。

背景

お客様から「Salesforce の標準の関連リストに見えるデータが少ないのでテーブルの形でデータを多く見えるようにしたい」という要望がありました。

その要望を対応するためSalesforce のAura コンポーネントを利用してカスタムデータテーブルを作成してみてお客さんを喜ばせることができました。

結果イメージ↓

カスタムデータテーブル

モバイルアプリ結果↓

カスタムデータテーブルモバイルアプリ

作業

Aura コンポーネントと小さいApex Class を使ってカスタムデータテーブルを作ります。

作成するもの

  • テーブルに表示するデータを取得するApex クラス
  • 本番環境にリリースする時検証用の Apex テストクラス
  • データリストを表示するテーブルの Aura コンポーネント
1.テーブルに表示するデータを取得するApex クラス

先ずはapex でデータベースからデータを取得するためapex class を作ります。
apex クラスを作成する前にレコードのURLを設定するため対象オブジェクトにURL項目を作成します。
今回は「取引先」からデータを取得するため取引先の中に追加します。
オブジェクト設定/ 項目とリレーションを開いて「新規」ボタンから新しい数式項目を以下の通り作成します。

  • 項目データ型 →数式
  • 項目表示ラベルとAPI参照名 →AccountUrl
  • 数式の戻り値のデータ型 →テキスト
  • 数式 → '/lightning/r/Account/' +Id+'/view'

次は開発コンソールを開いてファイル / 新規 / Apex class をおして DatatableCtrlClass という名前を付けます。
取得したいデータのobject と項目をこちらのクラスに設定してください。私は取引先名、取引先URL、登録日、営業担当を取得しました。

public class DatatableCtrlClass {
	@AuraEnabled
    public static List<Account> fetchData(ID recordId){
        return [SELECT Id,Name,AccountUrl__c,StartDate__c,SalesRepresentative__c, Campaign__c FROM Account WHERE Campaign__c=:recordId LIMIT 1000];
    }
}
Apex
2.本番環境にリリースする時検証用の Apex テストクラス

例をテストクラスなので必要によって変更してください。

@isTest
private class DatatableCtrlClassTest {
    private static void createTestData() {
        // キャンペーンを作成
        Campaign testCampaign = new Campaign(Name = 'Test Campaign');
        insert testCampaign;

        // キャンペーンに紐づいてる取引先を作成
        List<Account> testAccounts = new List<Account>();
        for (Integer i = 0; i < 5; i++) {
            testAccounts.add(new Account(
                Name = 'Test Account ' + i,
                Campaign__c = testCampaign.Id
            ));
        }
        insert testAccounts;
    }

    @isTest
    static void testFetchData() {
        createTestData();
        ID recordId = [SELECT Id FROM Campaign LIMIT 1].Id;

        Test.startTest();
        List<Account> result = DatatableCtrlClass.fetchData(recordId);
        Test.stopTest();

        // 結果を確認
        System.assertEquals(5, result.size(), 'There should be 5 accounts returned');
        for (Account acc : result) {
            System.assertEquals(recordId, acc.Campaign__c, 'The account should be related to the test campaign');
        }
    }
}
Apex
3.データリストを表示するテーブルの Aura コンポーネント

次はAuraコンポーネントを作ります。
開発コンソール / 新規 / Lightning Component を選択してCmp_Acc_Datatableを入力します。
*** のコメントのところでの設定を変えるだけで動かせると思います。

Cmp_Acc_Datatable.cmp

<!-- *** のコメントで設定できる -->
<aura:component implements="flexipage:availableForAllPageTypes,force:hasRecordId" access="global" controller="LC_DatatableCtrlClass">
    <aura:attribute name="recordId" type="String" />
    <!-- call doInit method on component load -->
    <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
    <!-- aura attributes to store data -->
    <aura:attribute name="listOfRecords" type="list"/> 
    <aura:attribute name="paginationList" type="list"/>
     
    <aura:attribute name="currentPageNumber" type="Integer" default="1"/>
    
    <!-- *** default="<表示しておきたいテーブル行数>" -->
    <aura:attribute name="pageSize" type="Integer" default="10"/>
    <aura:attribute name="totalPages" type="Integer" default="0"/>
    <aura:attribute name="pageList" type="List"/>
    <aura:attribute name="totalRecords" type="Integer" />
    <aura:attribute name="currentPageRecords" type="Integer" />
    <!-- sorting data variables -->
    <aura:attribute name="columns" type="List"/>
    
    <!-- *** default = "Id" のところでデフォルトでソートしたい項目のapi名を設定します。" -->
    <aura:attribute name="sortedBy" type="String" default="Id"/>
    <aura:attribute name="sortedDirection" type="string" default="asc" />

     
    <lightning:card title="Data with Sorting and Pagination" iconName="custom:custom8">
        <p class="slds-var-p-around_small">
            <lightning:layout multipleRows="true" horizontalAlign="center">
                <lightning:layoutItem padding="around-small" size="12">
                    <lightning:datatable keyField="id" data="{!v.paginationList}"
                                         columns="{!v.columns}"
                                         hideCheckboxColumn="true"
                                         onsort="{!c.updateSorting}"
                                         sortedBy="{!v.sortedBy}"
                                         sortedDirection="{!v.sortedDirection}"  />
                </lightning:layoutItem>
                 
                <!--  Pagination Buttons Start -->
                <div class="slds-align_absolute-center"> 
                    <lightning:button label="1"
                                      iconName="utility:left"
                                      iconPosition="left"
                                      onclick="{!c.onFirst}"
                                      disabled="{! v.currentPageNumber == 1}" />
                    <lightning:button label=""
                                      disabled="{!v.currentPageNumber == 1}" 
                                      onclick="{!c.handlePrevious}"
                                      variant="brand"
                                      iconName="utility:back"
                                      name="previous"/>
                     
                    <span class="slds-badge slds-badge_lightest"
                          style="margin-right: 10px;margin-left: 10px;">
                        {!v.currentPageNumber}/{!v.totalPages}
                    </span>
                     
                    <lightning:button label=""
                                      disabled="{!v.currentPageNumber == v.totalPages}"
                                      onclick="{!c.handleNext}"
                                      variant="brand"
                                      iconName="utility:forward"
                                      iconPosition="right"
                                      name="next"/>
                    <lightning:button label="{!v.totalPages}"
                                      iconName="utility:right"
                                      iconPosition="right"
                                      onclick="{!c.onLast}"
                                      disabled="{!v.currentPageNumber == v.totalPages}" />
                </div>        
            </lightning:layout>
        </p>
    </lightning:card>
     
</aura:component>
XML

次は Controller.jsに Javascript コードを書きます。component.set(‘v.columns’… のところに表示したいコラムを設定してください。

Cmp_Acc_DatatableController.js

({
    doInit : function(component, event, helper) {
        
        // *** テーブルのcolumnsを設定する。apex で取得した項目通りに設定できます。
        component.set('v.columns', [
            {label: '取引先名', fieldName: 'AccountUrl__c', type: 'url',sortable:true, typeAttributes: { label: { fieldName: 'Name' }}},
            {label: '登録日', fieldName: 'StartDate__c',type: 'date', sortable:true},
            {label: '営業担当', fieldName: 'SalesRepresentative__c', type: 'text',sortable:true }
        ]);
        helper.fetchRecords(component, helper);
    },
     
    updateSorting: function (cmp, event, helper) {
        var fieldName = event.getParam('fieldName');
        var sortDirection = event.getParam('sortDirection');
        cmp.set("v.sortedBy", fieldName);
        cmp.set("v.sortedDirection", sortDirection);
        helper.sortData(cmp, fieldName, sortDirection);
    },
     
    handleNext: function(component, event, helper){        
        component.set("v.currentPageNumber", component.get("v.currentPageNumber") + 1);
        helper.setPaginateData(component);
    },
     
    handlePrevious: function(component, event, helper){
       component.set("v.currentPageNumber", component.get("v.currentPageNumber") - 1);
       helper.setPaginateData(component);
    },
     
    onFirst: function(component, event, helper) {        
        component.set("v.currentPageNumber", 1);
        helper.setPaginateData(component);
    },
     
    onLast: function(component, event, helper) {        
        component.set("v.currentPageNumber", component.get("v.totalPages"));
        helper.setPaginateData(component);
    },
})
JavaScript

次はHelper.jsにコードを追加します。以下のコードをそのままコピーして利用しても動くと思います。

Cmp_Acc_DatatableHelper.js

({
    fetchRecords : function(component, helper) {
        // コンポーネントからのrecordIdをapex に割り当てデータを取得します。
        var action = component.get("c.fetchData");
        var recordId = component.get('v.recordId');
        action.setParams({
			"recordId": recordId
        });
        
        action.setCallback(this, function(response) {
            var state = response.getState();
            if(state === 'SUCCESS'){
                var result = response.getReturnValue();
                component.set("v.listOfRecords", result);
                helper.preparePagination(component, result);
            }
        });
        $A.enqueueAction(action);
    },
     
    preparePagination: function (component, records) {
        let countTotalPage = Math.ceil(records.length / component.get("v.pageSize"));
        let totalPage = countTotalPage > 0 ? countTotalPage : 1;
        component.set("v.totalPages", totalPage);
        component.set("v.currentPageNumber", 1);
        component.set("v.totalRecords", records.length);
        this.setPaginateData(component);
    },
     
    setPaginateData: function(component){
        let data = [];
        let pageNumber = component.get("v.currentPageNumber");
        let pageSize = component.get("v.pageSize");
        let accountData = component.get('v.listOfRecords');
        let currentPageCount = 0;
        let x = (pageNumber - 1) * pageSize;
        currentPageCount = x;
        for (; x < (pageNumber) * pageSize; x++){
            if (accountData[x]) {
                data.push(accountData[x]);
                currentPageCount++;
            }
        }
        component.set("v.paginationList", data);
        component.set("v.currentPageRecords", currentPageCount);
    },
     
    sortData: function (cmp, fieldName, sortDirection) {
        var fname = fieldName;
        var data = cmp.get("v.listOfRecords");
        var reverse = sortDirection !== 'asc';
        data.sort(this.sortBy(fieldName, reverse))
        cmp.set("v.listOfRecords", data);
        this.setPaginateData(cmp);
    },
     
    sortBy: function (field, reverse) {
        var key = function(x) {return x[field]};
        reverse = !reverse ? 1 : -1;
        return function (a, b) {
            return a = key(a), b = key(b), reverse * ((a > b) - (b > a));
        }
    },
})
JavaScript

作成するものは以上です。次はテーブルをレコード詳細画面の関連リストに交換します。

レコードページにデータテーブル設定

レコードページから「編集ページ」設定に移動して作成したテーブルを追加するだけです。
キャンペーンのレコードを開いてギヤアイコンから編集ページに移動して左バーの Cmp_Acc_Datatable を関連のところにドラッグアンドドロップと保存を押すと直ぐ反映されます。

最後に

Salesforce の関連リストにデータテーブルの形でデータを多く見えるように設定する方法を紹介しました。今回私がキャンペーンの中に取引先リストを表示するためコンポーネントを Cmp_Acc_Datatable って付けましたが自分の作りに関係する名前に変更してください。
取得するデータが多いならページネーションのページ数によってデータを取得することも必要かもしれないです。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です