null 値を空文字列として送信するフォームコンポーネントの挙動統一
WebAwesome の各フォームコンポーネントが null の値を送信した際に空文字列を送出するよう統一し、ブラウザ標準 <input> と同等の振る舞いを実現しました。これにより、フォームデータの扱いが一貫し、開発者が意図しない null 送信を防げます。
背景
WebAwesome のフォーム関連コンポーネントは、状態保存やサーバ送信時に null が渡されるケースが散在していましたが、ネイティブ <input> は null の場合空文字列 "" を送信します。この差異は、FormData 生成時に null がそのまま保持されることで、期待しない null 値がサーバへ送られるリスクを生んでいました。PR #2463 は、これらの差異を解消し、WebAwesome 全体でブラウザと同一の送信ロジックを提供することを目的としています。
技術的な変更
基底クラスのロジック統合
WebAwesomeFormAssociatedElement に updateFormValue メソッドを導入し、null 判定と空文字列への変換を一元化しました。従来は各コンポーネントで個別に setValue 呼び出しが行われていたため、今回の変更で null → "" の処理が共通化されました。Array.isArray(value) の特例(<wa-select> の複数選択)も同クラスで保持し、機能的な振る舞いは既存と同等です。
protected updateFormValue(value: unknown) {
// Accounts for the snowflake case on <wa-select>
if (Array.isArray(value)) {
if (this.name) {
const formData = new FormData();
for (const val of value) {
formData.append(this.name, val as string);
}
this.setValue(formData, formData);
}
} else {
this.setValue(value as FormData | string | File | null, value as FormData | string | File | null);
}
}
各コンポーネントでの null → 空文字列実装
WaInput、WaNumberInput、WaTextarea、WaColorPicker、WaSlider などのコンポーネントは、@internal とマークされたオーバーライド版 updateFormValue を追加し、value == null の分岐で this.setValue('', null) を呼び出すよう変更されました。これにより、null が渡された際にブラウザと同様に空文字列がフォームデータに設定されます。変更は 10 行前後のシンプルなガードロジックであり、既存のロジックへは影響しません。
protected updateFormValue(value: string | FormData | File | null) {
if (value == null) {
// null is the fallback value when loading from browser "memory" (also called "state").
// we use an empty string to mimic browser behavior of <input>
this.setValue('', null);
return;
}
super.updateFormValue(value);
}
テストの追加
wa-input に対して null の状態でフォームを生成し、FormData.get('a') が空文字列になることを検証するテストケースが input.test.ts に追加されました。テストは fixture と FormData を利用し、実装の期待通りの振る舞いを自動的に保証します。これにより、将来的なリファクタリング時に回帰が検出しやすくなります。
it('should store on formData as an empty string', async () => {
const form = await fixture<HTMLFormElement>(html`<form><wa-input name="a"></wa-input></form>`);
const formData = new FormData(form);
expect(formData.get('a')).to.equal('');
});
設計判断
後方互換性と API の限定公開
updateFormValue は @internal と明示的に注釈され、外部から直接呼び出すことは想定されていません。既存コンポーネントの公開 API は変更せず、内部ロジックだけを調整することで、利用者側のコードに影響を与えない形で振る舞いを統一しました。これは、ライブラリの安定性を保ちつつ内部改善を行う典型的なパターンです。
例外コンポーネントの除外理由
wa-radio-group、wa-rating、wa-select(単一選択版)や wa-switch などは、null を空文字列に変換しても意味がないか、既存の仕様と合わないため変更対象外としました。特に <wa-select> は空のオプションが無い場合に「送信しない」動作が期待され、ブラウザ <select> のデフォルト挙動と合致することから除外されています。これにより、意図しないデータ送信を防ぎつつ、コンポーネントごとの期待動作を尊重しています。
まとめ
本 PR は null 値を空文字列に置換して送信 するロジックを基底クラスに集約し、主要なフォームコンポーネントで統一的に実装することで、WebAwesome のフォーム送信がブラウザ標準と完全に合致するようになりました。内部的な変更に留めつつテストで保証したため、既存利用者への影響は最小限であり、今後の機能追加やバグ修正の際にも一貫した振る舞いが保たれます。