Appearance
Form
概要
フォーム全体を管理するコンポーネント。FormItem の登録・アンレジスト管理、フィールド値の一元管理、バリデーション実行、onFinish / onFinishFailed コールバック。
デモ
Props
onFinish?: (values: Record<string, any>) => void | Promise<void>- 検証成功時のコールバックonFinishFailed?: (errors: {name: string; error: string}[]) => void | Promise<void>- 検証失敗時のコールバックlayout?: "vertical" | "horizontal"- レイアウト("vertical" | "horizontal")class?: string- 追加 CSS クラスinitialValues?: Record<string, any>- フォーム全体の初期値(Form インスタンス作成時に一括設定)
使用方法(推奨:インスタンスベース)
Form コンポーネントを使用する際は、必ず事前にインスタンスを作成し、再利用してください。JSX で直接 <Form> と記述してはいけません。
tsx
import { Form, FormItem, Input } from 'mithril-ui-kit';
export default class MyComponent implements m.Component {
// ✅ 正しい方法:コンポーネント外でインスタンスを作成
public formRef = new Form();
public view() {
const ThisFormRef: any = this.formRef;
return (
<ThisFormRef
onFinish={async (values) => {
console.log("フォーム送信:", values);
}}
layout="vertical"
initialValues={{
username: "太郎",
email: "taro@example.com"
}}
>
<FormItem name="username" label="ユーザー名" formRef={this.formRef}>
<Input placeholder="名前を入力" />
</FormItem>
<FormItem name="email" label="メールアドレス" formRef={this.formRef}>
<Input type="email" placeholder="メールアドレスを入力" />
</FormItem>
<FormItem>
<button type="submit" class="btn btn-primary">送信</button>
</FormItem>
</ThisFormRef>
);
}
}なぜインスタンスベースが必須なのか?
❌ JSX で直接記述した場合(ダメな例)
tsx
export default class MyComponent implements m.Component {
public view() {
return (
<Form // ❌ 直接記述
onFinish={(values) => console.log(values)}
layout="vertical"
>
<FormItem name="username" label="ユーザー名" formRef={new Form()}>
<Input />
</FormItem>
</Form>
);
}
}このように JSX で <Form> を直接記述すると、以下の問題が発生します:
問題 1: インスタンスの再生成によるステート喪失
- Mithril.js の仕組み: JSX で
<Form>と記述すると、render のたびに新しいインスタンスが生成される - 結果: Form の内部状態(
this.state.fields)が初期化されるため、フォームに入力した値がリセットされてしまう - 現象:
- ユーザーがフィールドに値を入力しても、画面が再度 render されると値が消える
- キー入力のたびに値が消えてしまう
問題 2: formRef の参照不整合
- 原因: FormItem が登録するするフィールドの formRef と、Form インスタンスが異なる
- 結果: FormItem が Form に正しくフィールドを登録できず、バリデーションが機能しない
- 現象:
- FormItem の rules バリデーションが実行されない
- フォーム送信時に onFinish が呼ばれない
- エラーメッセージが表示されない
問題 3: 複数行テーブルでのデータ破損
- 状況: ExpandableTable や複数行を展開/非展開する場合
- 原因: 展開するたびに新しい Form インスタンスが生成される
- 結果: 前に入力したデータが失われ、フォームが初期状態にリセットされる
- 現象:
- テーブル行を展開してフォームに値を入力
- テーブル行を非展開にしてから再度展開
- 入力した値が全て消えている
✅ インスタンスベースが正しい理由
tsx
export default class MyComponent implements m.Component {
// ✅ クラスプロパティで Form インスタンスを一度だけ作成
public formRef = new Form();
public view() {
const ThisFormRef: any = this.formRef;
return (
<ThisFormRef // ✅ JSX では同じインスタンスを再利用
onFinish={(values) => console.log(values)}
layout="vertical"
>
{/* ... */}
</ThisFormRef>
);
}
}メリット:
- ステートの永続化: Form インスタンスが再利用されるため、
state.fieldsが保持される - 参照の一貫性: FormItem と Form が同じインスタンスを参照するため、フィールド登録が完全に機能する
- バリデーション機能: rules が正しく実行され、エラーメッセージが表示される
- 複数行対応: テーブルの各行に異なる Form インスタンスを割り当てることで、独立したフォーム状態を管理できる
initialValues の使用方法
Form コンポーネントで初期値を設定する場合、FormItem の initialValue ではなく、Form の initialValues Props を使用してください:
tsx
<ThisFormRef
initialValues={{
username: "太郎",
email: "taro@example.com",
age: 30
}}
onFinish={(values) => console.log(values)}
>
<FormItem name="username" label="ユーザー名" formRef={this.formRef}>
<Input />
</FormItem>
<FormItem name="email" label="メールアドレス" formRef={this.formRef}>
<Input />
</FormItem>
</ThisFormRef>注意点
- FormItem の
initialValueProps は非推奨です。代わりに Form のinitialValuesで一括設定してください initialValuesはフォーム作成時の初期値であり、後から変更したい場合は Form インスタンスのsetStateメソッドを使用できます
複数行テーブルでの使用例
各行に独立したフォームを持たせたい場合、行ごとに異なる Form インスタンスを管理します:
tsx
export default class TableDemo implements m.Component {
data: Record[];
formRefs: Map<string, Form> = new Map();
view() {
return (
<ExpandableTable
dataSource={this.data}
expandable={{
expandedRowRender: (record, index) => {
// 行ごとに Form インスタンスを管理
if (!this.formRefs.has(record.id)) {
this.formRefs.set(record.id, new Form());
}
const formRef = this.formRefs.get(record.id)!;
const ThisFormRef: any = formRef;
return (
<ThisFormRef
initialValues={{
name: record.name,
email: record.email
}}
onFinish={(values) => {
this.data[index] = { ...this.data[index], ...values };
m.redraw();
}}
>
<FormItem name="name" label="名前" formRef={formRef}>
<Input />
</FormItem>
{/* ... */}
</ThisFormRef>
);
}
}}
/>
);
}
}ポイント:
Map<string, Form>で行ごとの Form インスタンスを保持- 展開時に既存のインスタンスを取得(存在しない場合のみ新規作成)
- 各行が独立したフォーム状態を保有