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
には label
と value
を持った 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経由でリストビューを持ってきて、このように表示されます。
ちなみに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},
]
});
}
}
|
これを実行するとこうなります。