tamuです。

Apexを使ってリストビューの取得、またそのリストビューで取得できるレコードの取得をやっていきます。

リストビューの取得

リストビューを取得するには ApexPages.StandardSetController を使います。 このコンストラクタに対象のオブジェクトのクエリロケーターを渡すことで、対象のオブジェクトのリストビューを取得する準備ができます。

参考: https://developer.salesforce.com/docs/atlas.ja-jp.232.0.pages.meta/pages/apex_ApexPages_StandardSetController_ctor.htm

1
2
3
ApexPages.StandardSetController stdSetController = new ApexPages.StandardSetController(
    Database.getQueryLocator([SELECT Id FROM Product2 LIMIT 1])
);

このインスタンスで getListViewOptions を呼ぶことでそのユーザが参照できるリストビューを取得することができます。

参考: https://developer.salesforce.com/docs/atlas.ja-jp.232.0.pages.meta/pages/apex_ApexPages_StandardSetController_getListViewOptions.htm

1
List<SelectOption> listViewOptions = stdSetController.getListViewOptions();

List<SelectOption>List<Map<String, String>> で返したいので値を詰め直します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12

List<Map<String, String>> result = new List<Map<String, String>>();

for (SelectOption option: listViewOptions) {
    Id filterId = option.getValue();
    String filterLabel = option.getLabel();

    Map<String, String> m = new Map<String, String>();
    m.put('id', filterId);
    m.put('label', filterLabel);
    result.add(m);
}

これをまとめるとこんなメソッドになります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
@AuraEnabled(cacheable=true)
public static List<Map<String, String>> getListView() {
    List<Map<String, String>> result = new List<Map<String, String>>();
    ApexPages.StandardSetController stdSetController = new ApexPages.StandardSetController(
        Database.getQueryLocator([SELECT Id FROM Product2 LIMIT 1])
    );
    List<SelectOption> listViewOptions = stdSetController.getListViewOptions();
    for (SelectOption option: listViewOptions) {
        String filterId = option.getValue();
        String filterLabel = option.getLabel();

        Map<String, String> m = new Map<String, String>();
        m.put('id', filterId);
        m.put('label', filterLabel);
        result.add(m);
    }

    return result;
}

lightning-comboboxでの表示

Lightning Web Component側(LWC側)から上記のメソッドを呼び出し、 lightning-combobox で選択できるようにします。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<template>
  <div class="c-container">
    <lightning-combobox
       name="products"
       label="商品情報"
       placeholder="Select Listview"
       options={productListView}
       onchange={handleListview}
    ></lightning-combobox>
    <div class="here"></div>
  </div>
</template>

lightning-combobox で参照する productListView に値を突っ込んでいきます。 LWC開発: lightning-comboboxを試してみる で試したように、 options には labelvalue を持った Object の配列が必要です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
@track
productListView = [];

@wire(getListView)
wiredProductListView({error, data}) {
  if (data) {
    this.productListView = data.map(rec => {
      return {
        label: rec.label,
        value: rec.id
      };
    });
  }
}

@wired なのでページがロードされたタイミングでApex経由でリストビューを持ってきて、このように表示されます。

リストビューをcomboboxで表示

ちなみにSalesforce上の商品のリストビューはこうなってます。 (最近参照したデータは取れません)

Salesforce上の商品のリストビュー

リストビューを使ったレコードの検索

lightning-combobox を選択したら、Apex側でそのリストビューの条件にマッチしたレコードを取得します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
private static List<String> getProduct2Objects(String filterId) {
    List<String> result = new List<String>();
    ApexPages.StandardSetController stdSetController = new ApexPages.StandardSetController(
        Database.getQueryLocator([SELECT Id FROM Product2 LIMIT 1])
    );
    stdSetController.setPageSize(1000);
    stdSetController.setFilterId(filterId);

    for (SObject o: stdSetController.getRecords()) {
        String id = (String)o.get('Id');
        result.add(id);
    }

    return result;
}

LWC側から渡された filterId を元に、 ApexPages.StandardSetController にその filterId で取得できるレコードを #getRecords で持ってきます。

#getRecords ですでに商品情報としては取得できているのでそのまま返せばよいのですが、次の理由でレコードIDだけ取得してもう一度SELECTし直しています。

  • 必要のないカラムの情報までフロントとの通信に乗ってしまう
  • 将来的に親-子リレーション情報を取得したいかもしれない

「この商品はどのくらい商談商品になっているのか」とかを見るときに、ApexPages.StandardSetController#getRecords だけでは情報が取得できないので、 後述するSOQL部分で対応する必要があります。

getProduct2Objects で取得したレコードIDのリストをSOQLに突っ込んで結果をフロントに返します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
@AuraEnabled(cacheable=true)
public static List<Product2> getProducts(String filterId) {
    List<String> ids = getProduct2Objects(filterId);
    List<Product2> products = [
        SELECT
          Id,
          Name,
          ProductCode,
         StockKeepingUnit
        FROM
          Product2
        WHERE
          id IN :ids];
    return products;
}

レコード表示の更新

ここまで来たらあとはフロント側の処理です。

Jspreadsheetのコードを参照すると、setDataというメソッドで表示するデータを更新できるようです。

参考: https://github.com/jspreadsheet/ce/blob/b02d487c97221bed1e3ac5f7da502afb636942d4/dist/index.js#L6712

6712
6713
6714
6715
6716
6717
6718
6719
6720
6721
6722
6723
6724
6725
        /**
         * Set data
         * 
         * @param array data In case no data is sent, default is reloaded
         * @return void
         */
        obj.setData = function(data) {
            // Update data
            if (data) {
                if (typeof(data) == 'string') {
                    data = JSON.parse(data);
                }
    
                obj.options.data = data;

これを盛り込むと最終的にこういうコードになりました。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
import { LightningElement, track, wire } from 'lwc';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import { loadScript, loadStyle } from 'lightning/platformResourceLoader';
import SpreadsheetFiles from '@salesforce/resourceUrl/jspreadsheet';
import JsuitesFlies from '@salesforce/resourceUrl/jsuites';

import getListView from '@salesforce/apex/ProductViewer.getListView';
import getProducts from '@salesforce/apex/ProductViewer.getProducts';

export default class Productviewer extends LightningElement {

  /**
   * @type {boolean} initialized 初期化済みかどうかを表す
   */
  initialized = false;

  /**
   * jspreadsheet obj
   * initializeUIで設定する
   */
  spreadsheet = null;


  /**
   * @type {{label:string, value:string}[]} - 商品情報のリストビューで使用する選択肢
   */
  @track
  productListView = [];

  @wire(getListView)
  wiredProductListView({error, data}) {
    if (data) {
      this.productListView = data.map(rec => {
        return {
          label: rec.label,
          value: rec.id
        };
      });
    } else if (error) {
      console.log(error);
    }
  }
  
  /**
   * SalesforceApexからの戻りオブジェクトを展開する
   *
   * @param {Object[]} data - Apexからの戻り値
   * @param {String} data[].Id - 商品レコードのId
   * @param {String} data[].Name - 商品名
   * @param {String} data[].ProductCode - 商品コード
   * @param {String} data[].StockKeepingUnit - SKU
   * @return {Array<Array<String>>}
   */
  unwrap(data) {
    return data.map(p => {
      return [
        p.Id,
        p.Name,
        p.ProductCode,
        p.StockKeepingUnit
      ];
    });
  }

  handleListview(event) {
    const filterId = event.detail.value;
    getProducts({filterId: filterId})
      .then(result => {
        this.spreadsheet.setData(this.unwrap(result));
      })
      .catch(error => {
        console.log(error);
      })
      .finally(onFinally => {
        // nothing
      });
  }

  renderedCallback() {
    if (this.initialized) {
      return;
    }
    this.initialized = true;

    Promise.all([
      loadScript(this, SpreadsheetFiles + '/index.js'),
      loadStyle(this, SpreadsheetFiles + '/jspreadsheet.css'),
      loadScript(this, JsuitesFlies + '/jsuites.js'),
      loadStyle(this, JsuitesFlies + '/jsuites.css'),
    ]).then(() => {
      this.initializeUI();
    }).catch(error => {
      console.log(error);
      this.dispatchEvent(
        new ShowToastEvent({
          title: 'Error Loading Spreadsheet Library',
          message: 'error',
          variant: 'error'
        })
      )
    });
  }

  /**
   * UIの構築
   */
  initializeUI() {
    const ss = this.template.querySelector('.here');

    this.spreadsheet = jspreadsheet(ss, {
      data: [],
      columns: [
        { type: 'text', title: 'Id', width: 120 },
        { type: 'text', title: '商品名', width: 120},
        { type: 'text', title: '商品コード', width: 120},
        { type: 'text', title: 'SKU', width: 120},
      ]
    });
  }
}

これを実行するとこうなります。

すべての商品

すべての商品

家電

家電

サポート

サポート