null 値を空文字列として送信するフォームコンポーネントの挙動統一

shoelace-style/webawesome

WebAwesome の各フォームコンポーネントが null の値を送信した際に空文字列を送出するよう統一し、ブラウザ標準 <input> と同等の振る舞いを実現しました。これにより、フォームデータの扱いが一貫し、開発者が意図しない null 送信を防げます。

背景

WebAwesome のフォーム関連コンポーネントは、状態保存やサーバ送信時に null が渡されるケースが散在していましたが、ネイティブ <input>null の場合空文字列 "" を送信します。この差異は、FormData 生成時に null がそのまま保持されることで、期待しない null 値がサーバへ送られるリスクを生んでいました。PR #2463 は、これらの差異を解消し、WebAwesome 全体でブラウザと同一の送信ロジックを提供することを目的としています。

技術的な変更

基底クラスのロジック統合

WebAwesomeFormAssociatedElementupdateFormValue メソッドを導入し、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 → 空文字列実装

WaInputWaNumberInputWaTextareaWaColorPickerWaSlider などのコンポーネントは、@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 に追加されました。テストは fixtureFormData を利用し、実装の期待通りの振る舞いを自動的に保証します。これにより、将来的なリファクタリング時に回帰が検出しやすくなります。

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-groupwa-ratingwa-select(単一選択版)や wa-switch などは、null を空文字列に変換しても意味がないか、既存の仕様と合わないため変更対象外としました。特に <wa-select> は空のオプションが無い場合に「送信しない」動作が期待され、ブラウザ <select> のデフォルト挙動と合致することから除外されています。これにより、意図しないデータ送信を防ぎつつ、コンポーネントごとの期待動作を尊重しています。

まとめ

本 PR は null 値を空文字列に置換して送信 するロジックを基底クラスに集約し、主要なフォームコンポーネントで統一的に実装することで、WebAwesome のフォーム送信がブラウザ標準と完全に合致するようになりました。内部的な変更に留めつつテストで保証したため、既存利用者への影響は最小限であり、今後の機能追加やバグ修正の際にも一貫した振る舞いが保たれます。

記事メタデータ

Generated by:
gpt-oss-120b for DiffDaily
LLM Trace:
29233e3d

この記事はAIによって自動生成されています。内容の正確性については、必ずソースコードやPRを確認してください。

品質レビュー結果

Review Status:
承認済み
Review Count:
1回
Reviewed by:
gpt-oss-120b for DiffDaily

Review Criteria:

記事構成 ✓ PASS

Title, Context, Technical Detailの存在と明確さ

リード文・背景・技術的詳細・設計判断(任意)・まとめの全てが揃っており、総論→各論→結論の流れが明確です。

カスタムMarkdown構文 ✓ PASS

シンタックスハイライト・GitHubリンク記法の正確性

コードブロックは ```typescript:ファイルパス の形式で正しく記述され、PRリンクも正しい Markdown 記法です。

対象読者への適合性 ✓ PASS

エンジニア向けの適切な技術レベルと表現

内容は Web コンポーネント開発者向けの専門的な記述で、初心者向けの余計な説明はありません。

パラグラフ・ライティング ✓ PASS

トピックセンテンス・1段落1トピック・段落長

各セクションは総論パラグラフ→各論パラグラフ→結論パラグラフの構成になり、段落はトピックセンテンスで始まり 1 トピックに絞られ、長さも適切です。

Diff内容との照合 ✓ PASS

コードブロックとDiff内容の一致

記事中のコードブロックは Diff に記載された追加内容と一致しており、ファイル名・ロジックともに正確に反映されています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

使用されている用語(updateFormValue、FormData、@internal 等)は PR と Diff に合わせて正確です。

説明の技術的正確性 ✓ PASS

技術的主張の正確性と論理性

技術的説明は Diff と PR の内容に根拠があり、誤りや不整合はありません。

事実の突合 ✓ PASS

PR情報による主張の裏付け(ハルシネーション検出)

全ての主張は PR のタイトル・説明・Diff で裏付けられており、外部知識や推測は含まれていません。

数値・固有名詞の確認 ✓ PASS

PR番号・コミットID・バージョン等の正確性

PR 番号 #2463 が正しく記載され、他の数値・固有名詞に誤りはありません。

タイトル・説明との一致 ✓ PASS

記事タイトル・説明とPR内容の一致

記事タイトルは PR タイトル「submit empty strings for null form values」の意味を日本語で適切に表現しています。

外部知識の正確性 ✓ PASS

PRに記載のない外部知識(LTS、サポート状況など)の不使用

バージョンサポートやリリース情報など、PR に記載のない外部知識は含まれていません。

時間表現の正確性 ✓ PASS

時間表現がPR情報と一致しているか

時間表現はなく、PR の記述と矛盾する表現もありません。