## 概要 [[Obsidianプラグイン]]の`Setting`では、以下のようにテキストエリアのコンポーネント追加コマンドがある。 ```ts new Setting(containerEl) .setName("名前") .addText((text) => text .setValue(this.plugin.settings.name) .onChange(async (value) => { // 設定が変更されたときの処理 this.plugin.settings.name = value; }) ); ``` ## 問題 上記コードの`onChange`は入力欄に変更があったとき、つまり1文字追加したり削除するごとにイベントが発生する。設定変更時にやや重めの処理を加えたい場合、連続してイベントが発生するので操作感を大きく損ねる。また、フォーカスが外れる処理をする場合は致命的になる。 一方、[[Obsidian API]]には`onChange`以外のIFが存在しない。 ## 期待値 入力欄に変更があったときではなく、入力欄からフォーカスが外れたとき (変更後の入力内容が確定したとき) にイベントを実行したい。 ## 解決方法 HTMLElementの`addEventListener`に直接セットする。 ```ts new Setting(containerEl) .setName("名前") .addText((text) => { const el = text.setValue(this.plugin.settings.name); el.inputEl.addEventListener("change", async (ev) => { if (!(ev.target instanceof HTMLInputElement)) { return; } // 設定が変更されたときの処理 this.plugin.settings.name = value; }); return el; }); ``` ## もっとオシャレに [[メソッドチェーン]]できるようにラッパーを作るといい。 `settings-helper.ts` ```ts import type { TextComponent } from "obsidian"; export namespace TextComponentEvent { export function onChange( component: TextComponent, handler: (value: string) => void ): TextComponent { component.inputEl.addEventListener("change", async (ev) => { if (!(ev.target instanceof HTMLInputElement)) { return; } handler(ev.target.value); }); return component; } } ``` 呼び出し側は以下のように書ける。 ```ts new Setting(containerEl).setName("名前").addText((text) => TextComponentEvent.onChange(text, async (value) => { // 設定が変更されたときの処理 this.plugin.settings.name = value; }) .setValue(this.plugin.settings.name) ); ```