背景
Lightningのcomboboxのいまいちな点は、プルダウンメニューが多い選択肢だと心が萎えるところ。lightning-comboboxは決められた選択肢の中から選択する必要があるのと、選択肢が多い場合の選択肢へ辿り着くのが面倒でした。(Shiftを押しながらタイプすればというのもありますが)
lightning-comboboxとは別に、slds-comboboxもあり、こちらはより自由度が高く設定ができるようなので、試してみました。
デモ
実装
実装はそこまで難しくなく、選択肢となるコレクションデータを取得して用意しておきます。
SOQL
AggregateResult
getGroupedData
@AuraEnabled
public static List<AggregateResult> getLeadsGroupedResult(String groupBy) {
String query = 'SELECT COUNT(Id)cnt, ' + string.escapeSingleQuotes(groupBy) + ' FROM Lead';
query += ' WHERE Status != \'Unqualified\' AND IsConverted = false';
query += ' GROUP BY ' + groupBy;
query += ' ORDER BY COUNT(Id) DESC';
return Database.query(query);
}
LWC
combobox
combobox (html)
<div class="slds-combobox_container slds-m-horizontal_x-small">
<div class={comboboxClasses} aria-expanded={isOpen} aria-haspopup="listbox" role="combobox">
<div class="slds-combobox__form-element slds-input-has-icon slds-input-has-icon_right" role="none">
<input type="text" class="slds-input slds-combobox__input" aria-controls="listbox-id-1"
autocomplete="off" role="textbox" placeholder="Search Lead Source" oninput={handleInput}
onfocus={handleFocus} onblur={handleBlur} value={leadSource} />
</div>
<div class="slds-dropdown slds-dropdown_length-5 slds-dropdown_fluid">
<ul class="slds-listbox slds-listbox_vertical" role="listbox">
<template for:each={filteredData} for:item="group">
<li key={group.LeadSource} role="presentation" class="slds-listbox__item">
<div data-id={group.LeadSource}
class="slds-listbox__option slds-listbox__option_plain slds-media slds-media_small slds-media_center"
role="option" onclick={handleSelect}>
<span class="slds-media__body">
<span class="slds-truncate" title={group.LeadSource}>{group.LeadSourceName}
({group.cnt})</span>
</span>
</div>
</li>
</template>
</ul>
</div>
</div>
</div>
公式には slds-is-open のclassを挿入しないとできないとは書いてありますが、さらにaria-expandedもtrue, falseで制御する必要もあります。一括で行いために、class={comboboxClasses} で定義しました。
参照: slds combobox - displaying options (salesforce)
集計データの取得
loadGroupedData
connectedCallbackで、集計データ取得を呼びます。
connectedCallback() {
this.loadConnectedData(); // こちらはDatatable用データの取得
this.loadGroupedData(); // こちらは集計データ、Comboboxに使用
}
呼び出されるloadGroupedData()は以下です。
loadGroupedData() {
return new Promise((resolve, reject) => {
getLeadsGroupedResult({groupBy: this.groupBy})
.then(data => {
let groupedData = data && data.length ? data.map(item => {
if (item[this.groupBy] === undefined) {
return {...item, [this.groupBy]: '', [this.groupBy + 'Name']: '---'};
} else {
return {...item, [this.groupBy + 'Name']: item[this.groupBy]};
}
}) : [];
// Sort the groupedData array
groupedData = groupedData.sort((a, b) => {
if (a[this.groupBy] === '' && b[this.groupBy] !== '') {
return 1;
} else if (a[this.groupBy] !== '' && b[this.groupBy] === '') {
return -1;
} else {
return 0;
}
});
this.groupedData = groupedData;
const groupedTotal = groupedData.reduce((current, next) => current + next.cnt, 0);
resolve(data);
})
.catch(error => {
console.error(error);
})
});
}
空の値の取得もありうるのでその空データがASCでは最上部になるので見栄えが悪く、選択肢の一番下へ行くようにmethod内でsortを行っています。
Comboxの制御
handleInput(event)
/* Searchable Combobox */
@track isOpen = false;
@track filteredData = [];
handleInput(event) {
const searchKeyDirect = event.target.value;
if (searchKeyDirect === '') {
this.leadSource = '';
this.stringSet.leadSource = '';
this.filteredData = [...this.groupedData];
this.initializeSearch();
this.loadData();
return;
}
const searchKey = event.target.value.toLowerCase();
this.filteredData = this.groupedData.filter(group => {
return (group.LeadSource || '').toLowerCase().includes(searchKey);
});
this.leadSource = searchKeyDirect;
this.isOpen = true;
}
async handleSelect(event) {
this.isLoading = true;
const selectedLeadSource = event.currentTarget.dataset.id;
this.leadSource = selectedLeadSource ? selectedLeadSource : '';
this.stringSet.leadSource = selectedLeadSource ? selectedLeadSource : '';
this.initializeSearch();
await this.loadData();
this.loadAggregatedResult();
this.isLoading = false;
this.isOpen = false;
}
handleFocus() {
this.filteredData = [...this.groupedData];
this.isOpen = true;
}
handleBlur() {
setTimeout(() => {
this.isOpen = false;
}, 100);
}
get comboboxClasses() {
return `slds-combobox slds-dropdown-trigger slds-dropdown-trigger_click ${this.isOpen ? 'slds-is-open' : ''}`;
}
削除や空文字の場合、ボックスの非表示と選択肢の初期化を行います。
if (searchKeyDirect === '') {
this.leadSource = '';
this.stringSet.leadSource = '';
this.filteredData = [...this.groupedData];
this.initializeSearch();
this.loadData();
return;
}
comboboxの表示非表示のためslds-is-openの有無をこちらで制御します。
get comboboxClasses() {
return `slds-combobox slds-dropdown-trigger slds-dropdown-trigger_click ${this.isOpen ? 'slds-is-open' : ''}`;
}
こちらはフォーカスが外れた際、即行うとcomboboxの取得ができないので、少し遅らせます。
handleBlur() {
setTimeout(() => {
this.isOpen = false;
}, 100);
}
参照
slds combobox (salesforce)
lightning-combobox (salesforce)
関連投稿
Mockarooでダミーデータを作成してみた
DatatableのInfinite Loadingを実装してみた (lwc)
github