LoginSignup
0
2

Searchable Combobox 検索型選択リストを作ってみた (lwc)

Last updated at Posted at 2024-01-27

背景

Lightningのcomboboxのいまいちな点は、プルダウンメニューが多い選択肢だと心が萎えるところ。lightning-comboboxは決められた選択肢の中から選択する必要があるのと、選択肢が多い場合の選択肢へ辿り着くのが面倒でした。(Shiftを押しながらタイプすればというのもありますが)

lightning-comboboxとは別に、slds-comboboxもあり、こちらはより自由度が高く設定ができるようなので、試してみました。

デモ

searchable-combobox.gif

実装

実装はそこまで難しくなく、選択肢となるコレクションデータを取得して用意しておきます。

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);
    }
こちらで任意のgroupBy項目で集計したデータが返ってきます。今回はデフォルトでLeadSouceを取得します。

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

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2