了解、「タグ選択UI構築モード」起動。
今回は家や家族の登録・更新フォームで使える タグ選択モジュール tagSelect.js を作成します。
| 機能 | 説明 |
|---|---|
| 🔍 動的読み込み | サーバーからタグ候補を取得(全件 or カテゴリ単位) |
| ✅ 選択型UI | <select multiple> 形式 or チップUI |
| 🔄 保存時 | [{ name, category }] の形式でフォームから取得できる構成 |
| 💾 候補が無いとき | 自由入力(任意)も将来的に可能な設計で |
public/
└── javascripts/
└── admin/
└── members/
└── tagSelect.js ✅
tagSelect.js(ひな形)// public/javascripts/admin/members/tagSelect.js
/**
* タグセレクトUIを指定のDOMに描画する
* @param {HTMLElement} container - 埋め込む先のDOM
* @param {Array} selectedTags - [{ name: "檀家", category: "分類" }, ...]
* @param {String} [label] - ラベルテキスト
*/
export async function initTagSelect(container, selectedTags = [], label = "タグ") {
try {
// 全タグ取得
const res = await fetch("/admin/tags");
if (!res.ok) throw new Error("タグ取得に失敗しました");
const tags = await res.json(); // [{ name, category }, ...]
// <select multiple>
const select = document.createElement("select");
select.setAttribute("multiple", "multiple");
select.classList.add("browser-default"); // materialize独自のスタイルを外す
// タグをカテゴリごとにまとめる
const categories = ["分類", "通信", "お参り"];
categories.forEach((category) => {
const group = document.createElement("optgroup");
group.label = category;
tags
.filter((t) => t.category === category)
.forEach((tag) => {
const option = document.createElement("option");
option.value = `${tag.name}|${tag.category}`;
option.textContent = tag.name;
if (
selectedTags.some(
(sel) => sel.name === tag.name && sel.category === tag.category
)
) {
option.selected = true;
}
group.appendChild(option);
});
select.appendChild(group);
});
const labelEl = document.createElement("label");
labelEl.textContent = label;
container.innerHTML = ""; // 初期化
container.appendChild(labelEl);
container.appendChild(select);
} catch (err) {
console.error("タグ初期化失敗:", err);
container.innerHTML = "<p class='red-text'>タグの読み込みに失敗しました</p>";
}
}
/**
* セレクトから選ばれたタグを取得する
* @param {HTMLSelectElement} selectEl
* @returns {Array} [{ name, category }]
*/
export function getSelectedTags(selectEl) {
return Array.from(selectEl.selectedOptions).map((opt) => {
const [name, category] = opt.value.split("|");
return { name, category };
});
}
<div> を設置しておくconst tagArea = document.createElement("div");
tagArea.id = "tag-container";
modal.querySelector(".modal-content").appendChild(tagArea);
import { initTagSelect, getSelectedTags } from "./tagSelect.js";
await initTagSelect(tagArea, member?.tags || []);
const selectEl = tagArea.querySelector("select");
const tags = getSelectedTags(selectEl); // ← 保存時に取得
タグ候補取得APIは以下のように設計されている前提:
// GET /admin/tags
res.json([
{ name: "檀家", category: "分類" },
{ name: "永代", category: "お参り" },
...
]);
カテゴリ分けなどを加えたい場合はタグモデルと一緒にカスタマイズ可能
chips UI に置き換えたい場合は Materialize の <div class="chips"> に変更可input + API を組み合わせて対応可次は「タグ新規追加UI」や「戒名編集フォーム(kaimyouForm.js)」へ進みますか?