• Laravel

autoComplete.jsとLaravelでサジェスト機能追加(データベース連携あり)

サジェスト機能を作成したいな〜と思ってライブラリを探していたら、autoComplete.jsが評判良さげだったので使ってみました。

今回は新規登録フォーム時に使用したかったので、データベース連携も含めた使用例を残しておきます。

※使用に際しての準備や基本的な使用方法は公式サイト等をご確認ください。
autoComplete.js公式サイト
https://tarekraafat.github.io/autoComplete.js/#

前提

  • Laravel9
  • Vite
  • CDNではなくnpmでインストール

Laravel側

まずはautoComplete用のルーティングの設定

routes/web.php

Route::get('/autocomplete', [AutocompleteController::class, 'company']);

autoComplete用のコントローラーを作成

php artisan make:controller AutocompleteController

app/Http/Controllers/AutocompleteController.php

// namespace, useは省略

class AutocompleteController extends Controller
{
    // CompanyRepositoryのconstructは省略

    public function company(Request $request)
    {
        $query = $request->get('query', '');
        $results = $this->companyRepository->getAll()
            ->select(['id', 'name'])
            ->where('name', 'like', '%' . $query . '%')
            ->get();
        return response()->json($results);
    }
}

Bladeで入力フォームを作成

<input type="text" id="company" name="company_name" value="{{old('company_name')}}" placeholder="企業名を検索">
<input type="hidden" id="company_id" name="company_id" value="{{ old('company_id') }}">

今回は

  • フォームに入力するのは会社名
  • データベースに保存したいのは、外部キーであるcompany_id

ということをしたかったので、inputタグをそれぞれ用意しています。

JavaScript(autoComplete)側

import autoComplete from "@tarekraafat/autocomplete.js";

document.addEventListener("DOMContentLoaded", () => {  //DOMが読み込まれてから発火
    let companyData = [];  // 一度fetchしたデータ格納用の初期値設定
    const autoCompleteJS = new autoComplete({
        selector: "#company",  // inputダグのid
        data: {
            src: async (query) => {
                try {
                    const source = await fetch(`/autocomplete?query=${query}`);
                    const data = await source.json();
                    companyData = data;  // fetchしたデータを格納
                    return data.map((item) => item.name);
                } catch (error) {
                    console.log(error);
                    return [];
                }
            },
        },
        resultsList: {
            element: (list, data) => {
                if (!data.results.length) {
                    const message = document.createElement("div");
                    message.setAttribute("class", "no_result");
                    message.innerHTML = `<span>該当する結果が見つかりませんでした: "${data.query}"</span>`;
                    list.prepend(message);
                }
            },
            noResults: true,
            id: "company_list",
        },
        events: {
            input: {
                selection: (event) => {
                    const selection = event.detail.selection.value;
                    autoCompleteJS.input.value = selection;

										// fetchで格納したデータから選択した会社名から絞りこみcompany_idを設定
                    const selectedCompany = companyData.find(company => company.name === selection);
                    if (selectedCompany) {
                        document.getElementById('company_id').value = selectedCompany.id;
                    }
                },
            },
        },
    });
});

今回はcompany_idも一緒に設定する必要があるという部分が難しかったですね…

コメントにも書いてありますが、

  • 一旦fetchしたデータ全部を変数に格納
  • サジェストで選択した会社名から変数の中身を絞り込み
  • 絞り込まれたデータのcompany_idをinputタグ(hiddenのやつ)の値に設定

という流れでの実装になってます。

以上、誰かの参考になれれば幸いです。