> ## Documentation Index
> Fetch the complete documentation index at: https://inertiajs-vi.tuantq.online/llms.txt
> Use this file to discover all available pages before exploring further.

# Form

export const VueSpecific = ({children}) => {
  const [code, setCode] = useState(() => {
    if (typeof window === "undefined") {
      return "Vue";
    }
    return localStorage.getItem("code")?.replace(/"/g, "") || "Vue";
  });
  useEffect(() => {
    const handler = event => {
      if (event.detail?.key === "code") {
        setCode(event.detail.value?.replace(/"/g, ""));
      }
    };
    window.addEventListener("localStorageUpdate", handler);
    return () => window.removeEventListener("localStorageUpdate", handler);
  }, []);
  if (code !== "Vue") {
    return null;
  }
  return children;
};

export const SvelteSpecific = ({children}) => {
  const [code, setCode] = useState(() => {
    if (typeof window === "undefined") {
      return null;
    }
    return localStorage.getItem("code")?.replace(/"/g, "") || null;
  });
  useEffect(() => {
    const handler = event => {
      if (event.detail?.key === "code") {
        setCode(event.detail.value?.replace(/"/g, ""));
      }
    };
    window.addEventListener("localStorageUpdate", handler);
    return () => window.removeEventListener("localStorageUpdate", handler);
  }, []);
  if (!code?.includes("Svelte")) {
    return null;
  }
  return children;
};

export const ReactSpecific = ({children}) => {
  const [code, setCode] = useState(() => {
    if (typeof window === "undefined") {
      return null;
    }
    return localStorage.getItem("code")?.replace(/"/g, "") || null;
  });
  useEffect(() => {
    const handler = event => {
      if (event.detail?.key === "code") {
        setCode(event.detail.value?.replace(/"/g, ""));
      }
    };
    window.addEventListener("localStorageUpdate", handler);
    return () => window.removeEventListener("localStorageUpdate", handler);
  }, []);
  if (code !== "React") {
    return null;
  }
  return children;
};

export const ClientSpecific = ({children}) => {
  const [nada, setNada] = useState();
  return children;
};

Inertia cung cấp hai cách chính để xây dựng form: component `<Form>` và helper `useForm`. Cả hai tích hợp với validation của framework phía máy chủ và xử lý submit form mà không tải lại toàn bộ trang.

## Component Form

Inertia cung cấp component `<Form>` hoạt động gần giống form HTML truyền thống nhưng sử dụng Inertia bên dưới để tránh tải lại toàn bộ trang. Đây là cách đơn giản nhất để bắt đầu làm việc với form trong Inertia.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <script setup>
  import { Form } from "@inertiajs/vue3";
  </script>

  <template>
    <Form action="/users" method="post">
      <input type="text" name="name" />
      <input type="email" name="email" />
      <button type="submit">Create User</button>
    </Form>
  </template>
  ```

  ```jsx React icon="react" theme={null}
  import { Form } from "@inertiajs/react";

  export default () => (
    <Form action="/users" method="post">
      <input type="text" name="name" />
      <input type="email" name="email" />
      <button type="submit">Create User</button>
    </Form>
  );
  ```

  ```svelte Svelte icon="s" theme={null}
  <script>
      import { Form } from '@inertiajs/svelte'
  </script>

  <Form action="/users" method="post">
      <input type="text" name="name" />
      <input type="email" name="email" />
      <button type="submit">Create User</button>
  </Form>
  ```
</CodeGroup>

<ClientSpecific>
  Giống form HTML truyền thống, không cần gắn{" "}
  <VueSpecific>`v-model`</VueSpecific>
  <ReactSpecific>handler `onChange`</ReactSpecific>
  <SvelteSpecific>`bind:`</SvelteSpecific> vào input field; chỉ cần đặt
  thuộc tính `name` cho mỗi input{" "}
  <ReactSpecific>và `defaultValue` (nếu phù hợp) </ReactSpecific>, sau đó
  component `Form` sẽ xử lý việc gửi dữ liệu cho bạn.
</ClientSpecific>

Component cũng hỗ trợ cấu trúc dữ liệu lồng nhau, tải file và dotted key notation.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <template>
    <Form action="/reports" method="post">
      <input type="text" name="name" />
      <textarea name="report[description]"></textarea>
      <input type="text" name="report[tags][]" />
      <input type="file" name="documents" multiple />
      <button type="submit">Create Report</button>
    </Form>
  </template>
  ```

  ```jsx React icon="react" theme={null}
  <Form action="/reports" method="post">
    <input type="text" name="name" />
    <textarea name="report[description]"></textarea>
    <input type="text" name="report[tags][]" />
    <input type="file" name="documents" multiple />
    <button type="submit">Create Report</button>
  </Form>
  ```

  ```svelte Svelte icon="s" theme={null}
  <Form action="/reports" method="post">
      <input type="text" name="name" />
      <textarea name="report[description]"></textarea>
      <input type="text" name="report[tags][]" />
      <input type="file" name="documents" multiple />
      <button type="submit">Create Report</button>
  </Form>
  ```
</CodeGroup>

Bạn có thể truyền prop `transform` để sửa dữ liệu form trước khi submit. Điều này hữu ích khi cần chèn field bổ sung hoặc transform dữ liệu hiện có, dù hidden input cũng có thể dùng được.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <template>
    <Form
      action="/posts"
      method="post"
      :transform="(data) => ({ ...data, user_id: 123 })"
    >
      <input type="text" name="title" />
      <button type="submit">Create Post</button>
    </Form>
  </template>
  ```

  ```jsx React icon="react" theme={null}
  <Form
    action="/posts"
    method="post"
    transform={(data) => ({ ...data, user_id: 123 })}
  >
    <input type="text" name="title" />
    <button type="submit">Create Post</button>
  </Form>
  ```

  ```svelte Svelte icon="s" theme={null}
  <Form
      action="/posts"
      method="post"
      transform={data => ({ ...data, user_id: 123 })}
  >
      <input type="text" name="title" />
      <button type="submit">Create Post</button>
  </Form>
  ```
</CodeGroup>

### Wayfinder

Khi dùng [Wayfinder](https://github.com/laravel/wayfinder), bạn có thể truyền object kết quả trực tiếp vào prop `action`. Component Form sẽ tự suy ra HTTP method và URL từ object Wayfinder.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <script setup>
  import { Form } from "@inertiajs/vue3";
  import { store } from "App/Http/Controllers/UserController";
  </script>

  <template>
    <Form :action="store()">
      <input type="text" name="name" />
      <input type="email" name="email" />
      <button type="submit">Create User</button>
    </Form>
  </template>
  ```

  ```jsx React icon="react" theme={null}
  import { Form } from "@inertiajs/react";
  import { store } from "App/Http/Controllers/UserController";

  export default () => (
    <Form action={store()}>
      <input type="text" name="name" />
      <input type="email" name="email" />
      <button type="submit">Create User</button>
    </Form>
  );
  ```

  ```svelte Svelte icon="s" theme={null}
  <script>
      import { Form } from '@inertiajs/svelte'
      import { store } from 'App/Http/Controllers/UserController'
  </script>

  <Form action={store()}>
      <input type="text" name="name" />
      <input type="email" name="email" />
      <button type="submit">Create User</button>
  </Form>
  ```
</CodeGroup>

### Giá trị mặc định

<ClientSpecific>
  Bạn có thể đặt giá trị mặc định cho form input bằng thuộc tính HTML tiêu chuẩn. Dùng{" "}
  <ReactSpecific>`defaultValue`</ReactSpecific>
  <VueSpecific>`defaultValue`</VueSpecific>
  <SvelteSpecific>`value`</SvelteSpecific>{" "}cho text input và textarea, và{" "}
  <ReactSpecific>`defaultChecked`</ReactSpecific>
  <VueSpecific>`defaultChecked`</VueSpecific>
  <SvelteSpecific>`checked`</SvelteSpecific>{" "}cho checkbox và radio.
</ClientSpecific>

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <template>
    <Form action="/users" method="post">
      <input type="text" name="name" defaultValue="John Doe" />

      <select name="country">
        <option value="us">United States</option>
        <option value="ca">Canada</option>
        <option value="uk" selected>United Kingdom</option>
      </select>

      <input type="checkbox" name="subscribe" value="yes" defaultChecked />

      <button type="submit">Submit</button>
    </Form>
  </template>
  ```

  ```jsx React icon="react" theme={null}
  <Form action="/users" method="post">
    <input type="text" name="name" defaultValue="John Doe" />

    <select name="country" defaultValue="uk">
      <option value="us">United States</option>
      <option value="ca">Canada</option>
      <option value="uk">United Kingdom</option>
    </select>

    <input type="checkbox" name="subscribe" value="yes" defaultChecked />

    <button type="submit">Submit</button>
  </Form>
  ```

  ```svelte Svelte icon="s" theme={null}
  <Form action="/users" method="post">
      <input type="text" name="name" value="John Doe" />

      <select name="country" value="uk">
          <option value="us">United States</option>
          <option value="ca">Canada</option>
          <option value="uk">United Kingdom</option>
      </select>

      <input type="checkbox" name="subscribe" value="yes" checked />

      <button type="submit">Submit</button>
  </Form>
  ```
</CodeGroup>

### Input checkbox

Khi làm việc với checkbox, bạn có thể muốn thêm attribute `value` rõ ràng như `value="1"`. Nếu không có value, checkbox được chọn sẽ submit thành `"on"`, giá trị mà một số validation rule phía máy chủ có thể không nhận diện là boolean hợp lệ.

### Slot props

Component `<Form>` expose reactive state và các helper method qua default slot, cho phép truy cập trạng thái xử lý form, lỗi và utility function.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <template>
    <Form
      action="/users"
      method="post"
      #default="{
        errors,
        hasErrors,
        processing,
        progress,
        wasSuccessful,
        recentlySuccessful,
        setError,
        clearErrors,
        resetAndClearErrors,
        defaults,
        isDirty,
        reset,
        submit,
        cancel,
      }"
    >
      <input type="text" name="name" />

      <div v-if="errors.name">
        {{ errors.name }}
      </div>

      <button type="submit" :disabled="processing">
        {{ processing ? "Creating..." : "Create User" }}
      </button>

      <div v-if="wasSuccessful">User created successfully!</div>
    </Form>
  </template>
  ```

  ```jsx React icon="react" theme={null}
  <Form action="/users" method="post">
      {({
          errors,
          hasErrors,
          processing,
          progress,
          wasSuccessful,
          recentlySuccessful,
          setError,
          clearErrors,
          resetAndClearErrors,
          defaults,
          isDirty,
          reset,
          submit,
          cancel,
      }) => (
          <>
              <input type="text" name="name" />

              {errors.name && <div>{errors.name}</div>}

              <button type="submit" disabled={processing}>
                  {processing ? 'Creating...' : 'Create User'}
              </button>

              {wasSuccessful && <div>User created successfully!</div>}
          </>
      )}
  </Form>
  ```

  ```svelte Svelte icon="s" theme={null}
  <Form action="/users" method="post">
      {#snippet children({
          errors,
          hasErrors,
          processing,
          progress,
          wasSuccessful,
          recentlySuccessful,
          setError,
          clearErrors,
          resetAndClearErrors,
          defaults,
          isDirty,
          reset,
          submit,
          cancel,
      })}
          <input type="text" name="name" />

          {#if errors.name}
              <div>{errors.name}</div>
          {/if}

          <button type="submit" disabled={processing}>
              {processing ? 'Creating...' : 'Create User'}
          </button>

          {#if wasSuccessful}
              <div>User created successfully!</div>
          {/if}
      {/snippet}
  </Form>
  ```
</CodeGroup>

Method `defaults` cho phép cập nhật giá trị mặc định của form theo giá trị field hiện tại. Sau khi gọi, các lần `reset()` tiếp theo sẽ khôi phục field về các mặc định mới này và property `isDirty` sẽ theo dõi thay đổi so với chúng. Khác với `useForm`, method này không nhận đối số và luôn dùng toàn bộ giá trị hiện tại của form.

Object `errors` dùng dotted notation cho field lồng nhau, cho phép hiển thị validation message cho cấu trúc form phức tạp.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <Form action="/users" method="post" #default="{ errors }">
      <input type="text" name="user.name" />
      <div v-if="errors['user.name']">{{ errors['user.name'] }}</div>
  </Form>
  ```

  ```jsx React icon="react" theme={null}
  <Form action="/users" method="post">
      {({ errors }) => (
          <>
              <input type="text" name="user.name" />
              {errors['user.name'] && <div>{errors['user.name']}</div>}
          </>
      )}
  </Form>
  ```

  ```svelte Svelte icon="s" theme={null}
  <Form action="/users" method="post">
      {#snippet children({ errors })}
          <input type="text" name="user.name" />
          {#if errors['user.name']}
              <div>{errors['user.name']}</div>
          {/if}
      {/snippet}
  </Form>
  ```
</CodeGroup>

### Props và options

Ngoài `action` và `method`, component `<Form>` nhận thêm nhiều prop. Nhiều prop giống các tùy chọn có trong [visit options](/v3/the-basics/manual-visits) của Inertia.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <template>
    <Form
      action="/profile"
      method="put"
      error-bag="profile"
      query-string-array-format="indices"
      :headers="{ 'X-Custom-Header': 'value' }"
      :show-progress="false"
      :transform="(data) => ({ ...data, timestamp: Date.now() })"
      :optimistic="(props, data) => ({ ...props })"
      :invalidate-cache-tags="['users', 'dashboard']"
      disable-while-processing
      cancel-on-unmount
      :options="{
        preserveScroll: true,
        preserveState: true,
        preserveUrl: true,
        replace: true,
        only: ['users', 'flash'],
        except: ['secret'],
        reset: ['page'],
      }"
    >
      <input type="text" name="name" />
      <button type="submit">Update</button>
    </Form>
  </template>
  ```

  ```jsx React icon="react" theme={null}
  <Form
    action="/profile"
    method="put"
    errorBag="profile"
    queryStringArrayFormat="indices"
    headers={{ "X-Custom-Header": "value" }}
    showProgress={false}
    transform={(data) => ({ ...data, timestamp: Date.now() })}
    optimistic={(props, data) => ({ ...props })}
    invalidateCacheTags={["users", "dashboard"]}
    disableWhileProcessing
    cancelOnUnmount
    options={{
      preserveScroll: true,
      preserveState: true,
      preserveUrl: true,
      replace: true,
      only: ["users", "flash"],
      except: ["secret"],
      reset: ["page"],
    }}
  >
    <input type="text" name="name" />
    <button type="submit">Update</button>
  </Form>
  ```

  ```svelte Svelte icon="s" theme={null}
  <Form
      action="/profile"
      method="put"
      errorBag="profile"
      queryStringArrayFormat="indices"
      headers={{ 'X-Custom-Header': 'value' }}
      showProgress={false}
      transform={data => ({ ...data, timestamp: Date.now() })}
      optimistic={(props, data) => ({ ...props })}
      invalidateCacheTags={['users', 'dashboard']}
      disableWhileProcessing
      cancelOnUnmount
      options={{
          preserveScroll: true,
          preserveState: true,
          preserveUrl: true,
          replace: true,
          only: ['users', 'flash'],
          except: ['secret'],
          reset: ['page'],
      }}
  >
      <input type="text" name="name" />
      <button type="submit">Update</button>
  </Form>
  ```
</CodeGroup>

Một số prop được chủ ý nhóm dưới `options` thay vì đặt top-level để tránh nhầm lẫn. Ví dụ `only`, `except` và `reset` liên quan tới *partial reload*, không phải *partial submission*. Quy tắc chung: top-level prop dành cho chính việc gửi form, còn `options` kiểm soát cách Inertia xử lý visit tiếp theo.

<ClientSpecific>
  Khi đặt prop <ReactSpecific>`disableWhileProcessing`</ReactSpecific>
  <SvelteSpecific>`disableWhileProcessing`</SvelteSpecific>
  <VueSpecific>`disable-while-processing`</VueSpecific>, component `Form`
  sẽ thêm thuộc tính `inert` vào thẻ HTML `form` trong lúc form đang
  xử lý để ngăn người dùng tương tác.
</ClientSpecific>

Để style form trong lúc đang xử lý, bạn có thể target form inert theo các cách sau.

<CodeGroup>
  ```jsx Tailwind 4 theme={null}
  <Form
    action="/profile"
    method="put"
    disableWhileProcessing
    className="inert:opacity-50 inert:pointer-events-none"
  >
    {/* Your form fields here */}
  </Form>
  ```

  ```css CSS theme={null}
  form[inert] {
    opacity: 0.5;
    pointer-events: none;
  }
  ```
</CodeGroup>

### Sự kiện

Component `<Form>` phát toàn bộ [event](/v3/advanced/events) visit tiêu chuẩn khi gửi form.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <template>
    <Form
      action="/users"
      method="post"
      @before="handleBefore"
      @start="handleStart"
      @progress="handleProgress"
      @success="handleSuccess"
      @error="handleError"
      @finish="handleFinish"
      @cancel="handleCancel"
      @cancelToken="handleCancelToken"
    >
      <input type="text" name="name" />
      <button type="submit">Create User</button>
    </Form>
  </template>
  ```

  ```jsx React icon="react" theme={null}
  <Form
    action="/users"
    method="post"
    onCancelToken={handleCancelToken}
    onBefore={handleBefore}
    onStart={handleStart}
    onProgress={handleProgress}
    onCancel={handleCancel}
    onSuccess={handleSuccess}
    onError={handleError}
    onFinish={handleFinish}
  >
    <input type="text" name="name" />
    <button type="submit">Create User</button>
  </Form>
  ```

  ```svelte Svelte icon="s" theme={null}
  <Form
      action="/users"
      method="post"
      onCancelToken={handleCancelToken}
      onBefore={handleBefore}
      onStart={handleStart}
      onProgress={handleProgress}
      onCancel={handleCancel}
      onSuccess={handleSuccess}
      onError={handleError}
      onFinish={handleFinish}
  >
      <input type="text" name="name" />
      <button type="submit">Create User</button>
  </Form>
  ```
</CodeGroup>

### Reset form

Component `Form` cung cấp một số attribute cho phép reset form sau khi submit.

Có thể dùng `resetOnSuccess` để reset form sau khi submit thành công.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <template>
    <!-- Reset the entire form on success -->
    <Form action="/users" method="post" resetOnSuccess>
      <input type="text" name="name" />
      <input type="email" name="email" />
      <button type="submit">Submit</button>
    </Form>

    <!-- Reset specific fields on success -->
    <Form action="/users" method="post" :resetOnSuccess="['name']">
      <input type="text" name="name" />
      <input type="email" name="email" />
      <button type="submit">Submit</button>
    </Form>
  </template>
  ```

  ```jsx React icon="react" theme={null}
  // Reset the entire form on success
  <Form action="/users" method="post" resetOnSuccess>
      <input type="text" name="name" />
      <input type="email" name="email" />
      <button type="submit">Submit</button>
  </Form>

  // Reset specific fields on success
  <Form action="/users" method="post" resetOnSuccess={['name']}>
      <input type="text" name="name" />
      <input type="email" name="email" />
      <button type="submit">Submit</button>
  </Form>
  ```

  ```svelte Svelte icon="s" theme={null}
  <!-- Reset the entire form on success -->
  <Form action="/users" method="post" resetOnSuccess>
      <input type="text" name="name" />
      <input type="email" name="email" />
      <button type="submit">Submit</button>
  </Form>

  <!-- Reset specific fields on success -->
  <Form action="/users" method="post" resetOnSuccess={['name']}>
      <input type="text" name="name" />
      <input type="email" name="email" />
      <button type="submit">Submit</button>
  </Form>
  ```
</CodeGroup>

Có thể dùng `resetOnError` để reset form sau khi xảy ra lỗi.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <template>
    <!-- Reset the entire form on success -->
    <Form action="/users" method="post" resetOnError>
      <input type="text" name="name" />
      <input type="email" name="email" />
      <button type="submit">Submit</button>
    </Form>

    <!-- Reset specific fields on success -->
    <Form action="/users" method="post" :resetOnError="['name']">
      <input type="text" name="name" />
      <input type="email" name="email" />
      <button type="submit">Submit</button>
    </Form>
  </template>
  ```

  ```jsx React icon="react" theme={null}
  // Reset the entire form on success
  <Form action="/users" method="post" resetOnError>
      <input type="text" name="name" />
      <input type="email" name="email" />
      <button type="submit">Submit</button>
  </Form>

  // Reset specific fields on success
  <Form action="/users" method="post" resetOnError={['name']}>
      <input type="text" name="name" />
      <input type="email" name="email" />
      <button type="submit">Submit</button>
  </Form>
  ```

  ```svelte Svelte icon="s" theme={null}
  <!-- Reset the entire form on success -->
  <Form action="/users" method="post" resetOnError>
      <input type="text" name="name" />
      <input type="email" name="email" />
      <button type="submit">Submit</button>
  </Form>

  <!-- Reset specific fields on success -->
  <Form action="/users" method="post" resetOnError={['name']}>
      <input type="text" name="name" />
      <input type="email" name="email" />
      <button type="submit">Submit</button>
  </Form>
  ```
</CodeGroup>

### Thiết lập giá trị mặc định mới

Component `Form` cung cấp attribute `setDefaultsOnSuccess` để đặt các giá trị hiện tại của form làm mặc định mới sau khi submit thành công.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <template>
    <Form action="/users" method="post" setDefaultsOnSuccess>
      <input type="text" name="name" />
      <input type="email" name="email" />
      <button type="submit">Submit</button>
    </Form>
  </template>
  ```

  ```jsx React icon="react" theme={null}
  <Form action="/users" method="post" setDefaultsOnSuccess>
    <input type="text" name="name" />
    <input type="email" name="email" />
    <button type="submit">Submit</button>
  </Form>
  ```

  ```svelte Svelte icon="s" theme={null}
  <Form action="/users" method="post" setDefaultsOnSuccess>
      <input type="text" name="name" />
      <input type="email" name="email" />
      <button type="submit">Submit</button>
  </Form>
  ```
</CodeGroup>

### Hủy submit form

Phương thức `cancel` hủy submission đang chạy. Nó có sẵn qua [slot props](#slot-props), [component ref](#programmatic-access) và [form context](#form-context).

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <template>
    <Form action="/users" method="post" #default="{ processing, cancel }">
      <input type="text" name="name" />

      <button v-if="processing" type="button" @click="cancel">Cancel</button>
      <button v-else type="submit">Submit</button>
    </Form>
  </template>
  ```

  ```jsx React icon="react" theme={null}
  <Form action="/users" method="post">
    {({ processing, cancel }) => (
      <>
        <input type="text" name="name" />

        {processing ? (
          <button type="button" onClick={cancel}>Cancel</button>
        ) : (
          <button type="submit">Submit</button>
        )}
      </>
    )}
  </Form>
  ```

  ```svelte Svelte icon="s" theme={null}
  <Form action="/users" method="post">
      {#snippet children({ processing, cancel })}
          <input type="text" name="name" />

          {#if processing}
              <button type="button" onclick={cancel}>Cancel</button>
          {:else}
              <button type="submit">Submit</button>
          {/if}
      {/snippet}
  </Form>
  ```
</CodeGroup>

Submission tiếp tục chạy ngay cả khi form khởi tạo nó bị gỡ khỏi page. Điều hướng đi nơi khác sẽ hủy submission, nhưng đóng modal chứa form thì không, vì vậy upload bị bỏ dở vẫn hoàn tất. Bạn có thể dùng thuộc tính `cancelOnUnmount` để hủy submission đang chạy ngay khi form unmount.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <template>
    <Form action="/avatar" method="post" cancel-on-unmount>
      <input type="file" name="avatar" />
      <button type="submit">Upload</button>
    </Form>
  </template>
  ```

  ```jsx React icon="react" theme={null}
  <Form action="/avatar" method="post" cancelOnUnmount>
    <input type="file" name="avatar" />
    <button type="submit">Upload</button>
  </Form>
  ```

  ```svelte Svelte icon="s" theme={null}
  <Form action="/avatar" method="post" cancelOnUnmount>
      <input type="file" name="avatar" />
      <button type="submit">Upload</button>
  </Form>
  ```
</CodeGroup>

### Dotted key notation

Component `<Form>` hỗ trợ dotted key notation để tạo object lồng nhau từ tên input phẳng. Đây là cách thuận tiện để tổ chức dữ liệu form.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <template>
    <Form action="/users" method="post">
      <input type="text" name="user.name" />
      <input type="text" name="user.skills[]" />
      <input type="text" name="address.street" />
      <button type="submit">Submit</button>
    </Form>
  </template>
  ```

  ```jsx React icon="react" theme={null}
  <Form action="/users" method="post">
    <input type="text" name="user.name" />
    <input type="text" name="user.skills[]" />
    <input type="text" name="address.street" />
    <button type="submit">Submit</button>
  </Form>
  ```

  ```svelte Svelte icon="s" theme={null}
  <Form action="/users" method="post">
      <input type="text" name="user.name" />
      <input type="text" name="user.skills[]" />
      <input type="text" name="address.street" />
      <button type="submit">Submit</button>
  </Form>
  ```
</CodeGroup>

Ví dụ trên sẽ tạo ra cấu trúc dữ liệu sau.

```json theme={null}
{
  "user": {
    "name": "John Doe",
    "skills": ["JavaScript"]
  },
  "address": {
    "street": "123 Main St"
  }
}
```

Nếu cần dấu chấm literal trong tên field (không dùng làm dấu phân tách object lồng nhau), bạn có thể escape bằng dấu gạch chéo ngược.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <template>
    <Form action="/config" method="post">
      <input type="text" name="app\.name" />
      <input type="text" name="settings.theme\.mode" />
      <button type="submit">Save</button>
    </Form>
  </template>
  ```

  ```jsx React icon="react" theme={null}
  <Form action="/config" method="post">
    <input type="text" name="app\.name" />
    <input type="text" name="settings.theme\.mode" />
    <button type="submit">Save</button>
  </Form>
  ```

  ```svelte Svelte icon="s" theme={null}
  <Form action="/config" method="post">
      <input type="text" name="app\.name" />
      <input type="text" name="settings.theme\.mode" />
      <button type="submit">Save</button>
  </Form>
  ```
</CodeGroup>

Ví dụ trên sẽ tạo ra cấu trúc dữ liệu sau.

```json theme={null}
{
  "app.name": "My Application",
  "settings": {
    "theme.mode": "dark"
  }
}
```

### Truy cập bằng code

Bạn có thể truy cập các method của form bằng code thông qua ref. Đây là lựa chọn thay thế cho [slot props](#slot-props) khi cần kích hoạt action của form từ bên ngoài form.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <script setup>
  import { ref } from "vue";
  import { Form } from "@inertiajs/vue3";

  const formRef = ref();

  const handleSubmit = () => {
    formRef.value.submit();
  };
  </script>

  <template>
    <Form ref="formRef" action="/users" method="post">
      <input type="text" name="name" />
      <button type="submit">Submit</button>
    </Form>

    <button @click="handleSubmit">Submit Programmatically</button>
  </template>
  ```

  ```jsx React icon="react" theme={null}
  import { useRef } from 'react'
  import { Form } from '@inertiajs/react'

  export default function CreateUser() {
      const formRef = useRef()

      const handleSubmit = () => {
          formRef.current.submit()
      }

      return (
          <Form ref={formRef} action="/users" method="post">
              <input type="text" name="name" />
              <button type="submit">Submit</button>
          </Form>

          <button onClick={handleSubmit}>Submit Programmatically</button>
      )
  }
  ```

  ```svelte Svelte icon="s" theme={null}
  <script>
  import { Form } from '@inertiajs/svelte'

  let formRef

  function handleSubmit() {
      formRef.submit()
  }
  </script>

  <Form bind:this={formRef} action="/users" method="post">
      <input type="text" name="name" />
      <button type="submit">Submit</button>
  </Form>

  <button onclick={handleSubmit}>Submit Programmatically</button>
  ```
</CodeGroup>

Trong React và Vue, ref cho phép truy cập toàn bộ method và reactive state của form. Trong Svelte, ref chỉ expose method, vì vậy reactive state như `isDirty` và `errors` nên được truy cập qua [slot props](#slot-props).

### Form context

Child component lồng sâu có thể cần truy cập form state hoặc method mà không truyền prop qua nhiều tầng. Hook `useFormContext` cung cấp quyền truy cập state và method của component `<Form>` cha từ bất kỳ child component nào.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <script setup>
  import { useFormContext } from "@inertiajs/vue3";

  const form = useFormContext();
  </script>

  <template>
    <div v-if="form">
      <span v-if="form.isDirty">Unsaved changes</span>
      <span v-if="form.errors.name">{{ form.errors.name }}</span>
      <button type="button" @click="form.submit()">Submit</button>
      <button type="button" @click="form.reset()">Reset</button>
    </div>
  </template>
  ```

  ```jsx React icon="react" theme={null}
  import { useFormContext } from "@inertiajs/react";

  export default function FormActions() {
    const form = useFormContext();

    if (!form) {
      return null;
    }

    return (
      <div>
        {form.isDirty && <span>Unsaved changes</span>}
        {form.errors.name && <span>{form.errors.name}</span>}
        <button type="button" onClick={() => form.submit()}>
          Submit
        </button>
        <button type="button" onClick={() => form.reset()}>
          Reset
        </button>
      </div>
    );
  }
  ```

  ```svelte Svelte icon="s" theme={null}
  <script>
  import { useFormContext } from '@inertiajs/svelte'

  const form = useFormContext()
  </script>

  {#if form}
      {#if form.isDirty}<span>Unsaved changes</span>{/if}
      {#if form.errors.name}<span>{form.errors.name}</span>{/if}
      <button type="button" onclick={() => form.submit()}>Submit</button>
      <button type="button" onclick={() => form.reset()}>Reset</button>
  {/if}
  ```
</CodeGroup>

Context cung cấp đầy đủ property và method giống như [slot props](#slot-props).

<Note>
  Cả component `<Form>` và `useFormContext` đều nhận generic type parameter để có error và slot prop type-safe. Xem tài liệu [TypeScript](/v3/advanced/typescript#form-component) để biết chi tiết.
</Note>

### Precognition

Component `<Form>` tích hợp sẵn hỗ trợ [Laravel Precognition](https://laravel.com/docs/precognition), cho phép validation form theo thời gian thực mà không cần sao chép validation rule phía máy chủ sang client.

<Note>
  Precognition yêu cầu hỗ trợ phía server. Người dùng Laravel nên xem
  [tài liệu Laravel Precognition](https://laravel.com/docs/precognition)
  để biết cách thiết lập. Với framework khác, xem [trang protocol
  ](/v3/core-concepts/the-protocol#request-headers) để biết chi tiết implementation.
  chi tiết.
</Note>

Sau khi cấu hình máy chủ, gọi `validate()` với tên field để kích hoạt validation cho field đó. Helper `invalid()` kiểm tra field có lỗi validation hay không, còn `validating` cho biết request đang được xử lý.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <template>
    <Form
      action="/users"
      method="post"
      #default="{ errors, invalid, validate, validating }"
    >
      <label for="name">Name:</label>
      <input id="name" name="name" @change="validate('name')" />
      <p v-if="invalid('name')">{{ errors.name }}</p>

      <label for="email">Email:</label>
      <input id="email" name="email" @change="validate('email')" />
      <p v-if="invalid('email')">{{ errors.email }}</p>

      <p v-if="validating">Validating...</p>

      <button type="submit">Create User</button>
    </Form>
  </template>
  ```

  ```jsx React icon="react" theme={null}
  <Form action="/users" method="post">
    {({ errors, invalid, validate, validating }) => (
      <>
        <label htmlFor="name">Name:</label>
        <input id="name" name="name" onChange={() => validate("name")} />
        {invalid("name") && <p>{errors.name}</p>}

        <label htmlFor="email">Email:</label>
        <input id="email" name="email" onChange={() => validate("email")} />
        {invalid("email") && <p>{errors.email}</p>}

        {validating && <p>Validating...</p>}

        <button type="submit">Create User</button>
      </>
    )}
  </Form>
  ```

  ```svelte Svelte icon="s" theme={null}
  <Form action="/users" method="post">
      {#snippet children({ errors, invalid, validate, validating })}
          <label for="name">Name:</label>
          <input id="name" name="name" onchange={() => validate('name')} />
          {#if invalid('name')}
              <p>{errors.name}</p>
          {/if}

          <label for="email">Email:</label>
          <input id="email" name="email" onchange={() => validate('email')} />
          {#if invalid('email')}
              <p>{errors.email}</p>
          {/if}

          {#if validating}
              <p>Validating...</p>
          {/if}

          <button type="submit">Create User</button>
      {/snippet}
  </Form>
  ```
</CodeGroup>

Bạn cũng có thể dùng helper `valid()` để kiểm tra field đã pass validation hay chưa.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <Form
    action="/users"
    method="post"
    #default="{ errors, invalid, valid, validate }"
  >
      <input name="email" @change="validate('email')" />
      <p v-if="valid('email')">Valid email address</p>
      <p v-if="invalid('email')">{{ errors.email }}</p>
  </Form>
  ```

  ```jsx React icon="react" theme={null}
  <Form action="/users" method="post">
    {({ errors, invalid, valid, validate }) => (
      <>
        <input name="email" onChange={() => validate("email")} />
        {valid("email") && <p>Valid email address</p>}
        {invalid("email") && <p>{errors.email}</p>}
      </>
    )}
  </Form>
  ```

  ```svelte Svelte icon="s" theme={null}
  <Form action="/users" method="post">
      {#snippet children({ errors, invalid, valid, validate })}
          <input name="email" onchange={() => validate('email')} />
          {#if valid('email')}
              <p>Valid email address</p>
          {/if}
          {#if invalid('email')}
              <p>{errors.email}</p>
          {/if}
      {/snippet}
  </Form>
  ```
</CodeGroup>

<Warning>
  Form input chỉ xuất hiện trạng thái valid hoặc invalid sau khi giá trị đã thay đổi và đã nhận
  validation response. Gọi `validate('field')` sẽ không gửi validation request cho tới khi giá trị field khác dữ liệu ban đầu.
</Warning>

#### Validation nhiều field

Bạn có thể validation nhiều field cùng lúc bằng tùy chọn `only`. Điều này đặc biệt hữu ích khi xây dựng form dạng wizard, nơi bạn muốn validation toàn bộ field đang hiển thị trước khi sang bước tiếp theo.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <Form action="/users" method="post" #default="{ validate }">
      <!-- Step 1 fields -->
      <input name="name" />
      <input name="email" />

      <button
          type="button"
          @click="validate({
              only: ['name', 'email'],
              onSuccess: () => goToNextStep(),
              onValidationError: () => showErrors(),
          })"
      >
          Next Step
      </button>
  </Form>
  ```

  ```jsx React icon="react" theme={null}
  <Form action="/users" method="post">
    {({ validate }) => (
      <>
        {/* Step 1 fields */}
        <input name="name" />
        <input name="email" />

        <button
          type="button"
          onClick={() =>
            validate({
              only: ["name", "email"],
              onSuccess: () => goToNextStep(),
              onValidationError: () => showErrors(),
            })
          }
        >
          Next Step
        </button>
      </>
    )}
  </Form>
  ```

  ```svelte Svelte icon="s" theme={null}
  <Form action="/users" method="post">
      {#snippet children({ validate })}
          <!-- Step 1 fields -->
          <input name="name" />
          <input name="email" />

          <button
              type="button"
              onclick={() => validate({
                  only: ['name', 'email'],
                  onSuccess: () => goToNextStep(),
                  onValidationError: () => showErrors(),
              })}
          >
              Next Step
          </button>
      {/snippet}
  </Form>
  ```
</CodeGroup>

#### Touch và validate

Method `touch()` đánh dấu các field là "touched" mà không kích hoạt validation. Sau đó bạn có thể validation tất cả field đã touched bằng cách gọi `validate()` không có đối số.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <Form action="/users" method="post" #default="{ validate, touch, touched }">
      <input name="name" @blur="touch('name')" />
      <input name="email" @blur="touch('email')" />
      <input name="phone" @blur="touch('phone')" />

      <button type="button" @click="validate()">Validate Touched Fields</button>

      <p v-if="touched('name')">Name has been touched</p>
  </Form>
  ```

  ```jsx React icon="react" theme={null}
  <Form action="/users" method="post">
    {({ validate, touch, touched }) => (
      <>
        <input name="name" onBlur={() => touch("name")} />
        <input name="email" onBlur={() => touch("email")} />
        <input name="phone" onBlur={() => touch("phone")} />

        <button type="button" onClick={() => validate()}>
          Validate Touched Fields
        </button>

        {touched("name") && <p>Name has been touched</p>}
      </>
    )}
  </Form>
  ```

  ```svelte Svelte icon="s" theme={null}
  <Form action="/users" method="post">
      {#snippet children({ validate, touch, touched })}
          <input name="name" onblur={() => touch('name')} />
          <input name="email" onblur={() => touch('email')} />
          <input name="phone" onblur={() => touch('phone')} />

          <button type="button" onclick={() => validate()}>Validate Touched Fields</button>

          {#if touched('name')}
              <p>Name has been touched</p>
          {/if}
      {/snippet}
  </Form>
  ```
</CodeGroup>

Helper `touched()` cũng có thể được gọi không đối số để kiểm tra có field nào đã touched hay chưa. Method `reset()` xóa touched state của các field được reset.

#### Options

Method `validate()` nhận object options chứa callback và cấu hình.

```js theme={null}
validate("username", {
  onSuccess: () => {
    // Validation passed...
  },
  onValidationError: (response) => {
    // Validation failed (422 response)...
  },
  onBeforeValidation: (newRequest, oldRequest) => {
    // Return false to prevent validation...
  },
  onFinish: () => {
    // Always runs after validation...
  },
});
```

Bạn cũng có thể gọi `validate()` chỉ với object options để validation các field cụ thể.

```js theme={null}
validate({
  only: ["name", "email"],
  onSuccess: () => goToNextStep(),
});
```

Request validation tự động được debounce. Request đầu tiên chạy ngay lập tức, các thay đổi tiếp theo được debounce (mặc định 1500ms). Bạn có thể tùy chỉnh timeout này.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <Form action="/users" method="post" :validation-timeout="500">
      <!-- ... -->
  </Form>
  ```

  ```jsx React icon="react" theme={null}
  <Form action="/users" method="post" validationTimeout={500}>
    {/* ... */}
  </Form>
  ```

  ```svelte Svelte icon="s" theme={null}
  <Form action="/users" method="post" validationTimeout={500}>
      <!-- ... -->
  </Form>
  ```
</CodeGroup>

Mặc định, file bị loại khỏi request validation để tránh upload không cần thiết. Bạn có thể bật validation file khi cần kiểm tra input file như kích thước hoặc MIME type.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <Form action="/users" method="post" validate-files>
      <!-- ... -->
  </Form>
  ```

  ```jsx React icon="react" theme={null}
  <Form action="/users" method="post" validateFiles>
    {/* ... */}
  </Form>
  ```

  ```svelte Svelte icon="s" theme={null}
  <Form action="/users" method="post" validateFiles>
      <!-- ... -->
  </Form>
  ```
</CodeGroup>

Mặc định, lỗi validation được đơn giản hóa thành chuỗi (message lỗi đầu tiên). Bạn có thể giữ lỗi dưới dạng array để hiển thị toàn bộ message cho field có nhiều validation rule.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <Form action="/users" method="post" with-all-errors>
      <!-- ... -->
  </Form>
  ```

  ```jsx React icon="react" theme={null}
  <Form action="/users" method="post" withAllErrors>
    {/* ... */}
  </Form>
  ```

  ```svelte Svelte icon="s" theme={null}
  <Form action="/users" method="post" withAllErrors>
      <!-- ... -->
  </Form>
  ```
</CodeGroup>

## Form helper

Ngoài component `<Form>`, Inertia còn cung cấp helper `useForm` khi bạn cần kiểm soát bằng code đối với dữ liệu form và hành vi submit.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <script setup>
  import { useForm } from "@inertiajs/vue3";

  const form = useForm({
    email: null,
    password: null,
    remember: false,
  });
  </script>

  <template>
    <form @submit.prevent="form.post('/login')">
      <input type="text" v-model="form.email" />
      <div v-if="form.errors.email">{{ form.errors.email }}</div>
      <input type="password" v-model="form.password" />
      <div v-if="form.errors.password">{{ form.errors.password }}</div>
      <input type="checkbox" v-model="form.remember" /> Remember Me
      <button type="submit" :disabled="form.processing">Login</button>
    </form>
  </template>
  ```

  ```jsx React icon="react" theme={null}
  import { useForm } from "@inertiajs/react";

  const { data, setData, post, processing, errors } = useForm({
    email: "",
    password: "",
    remember: false,
  });

  function submit(e) {
    e.preventDefault();
    post("/login");
  }

  return (
    <form onSubmit={submit}>
      <input
        type="text"
        value={data.email}
        onChange={(e) => setData("email", e.target.value)}
      />
      {errors.email && <div>{errors.email}</div>}
      <input
        type="password"
        value={data.password}
        onChange={(e) => setData("password", e.target.value)}
      />
      {errors.password && <div>{errors.password}</div>}
      <input
        type="checkbox"
        checked={data.remember}
        onChange={(e) => setData("remember", e.target.checked)}
      />{" "}
      Remember Me
      <button type="submit" disabled={processing}>
        Login
      </button>
    </form>
  );
  ```

  ```svelte Svelte icon="s" theme={null}
  <script>
  import { useForm } from '@inertiajs/svelte'

  const form = useForm({
      email: null,
      password: null,
      remember: false,
  })

  function submit(e) {
      e.preventDefault()
      form.post('/login')
  }
  </script>

  <form onsubmit={submit}>
      <input type="text" bind:value={form.email} />
      {#if form.errors.email}
          <div class="form-error">{form.errors.email}</div>
      {/if}
      <input type="password" bind:value={form.password} />
      {#if form.errors.password}
          <div class="form-error">{form.errors.password}</div>
      {/if}
      <input type="checkbox" bind:checked={form.remember} /> Remember Me
      <button type="submit" disabled={form.processing}>Submit</button>
  </form>
  ```
</CodeGroup>

Để gửi form, bạn có thể dùng các phương thức `get`, `post`, `put`, `patch` và `delete`.

<CodeGroup>
  ```js Vue icon="vuejs" theme={null}
  form.submit(method, url, options);
  form.get(url, options);
  form.post(url, options);
  form.put(url, options);
  form.patch(url, options);
  form.delete(url, options);
  ```

  ```js React icon="react" theme={null}
  const { submit, get, post, put, patch, delete: destroy } = useForm({ ... })

  submit(method, url, options)
  get(url, options)
  post(url, options)
  put(url, options)
  patch(url, options)
  destroy(url, options)
  ```

  ```js Svelte icon="s" theme={null}
  form.submit(method, url, options);
  form.get(url, options);
  form.post(url, options);
  form.put(url, options);
  form.patch(url, options);
  form.delete(url, options);
  ```
</CodeGroup>

Các submit method hỗ trợ mọi [visit option](/v3/the-basics/manual-visits) thông thường như `preserveState`, `preserveScroll` và event callback, hữu ích để thực hiện tác vụ sau khi gửi form thành công. Ví dụ, bạn có thể dùng callback `onSuccess` để reset input về state ban đầu.

<CodeGroup>
  ```js Vue icon="vuejs" theme={null}
  form.post("/profile", {
    preserveScroll: true,
    onSuccess: () => form.reset("password"),
  });
  ```

  ```js React icon="react" theme={null}
  const { post, reset } = useForm({ ... })

  post('/profile', {
      preserveScroll: true,
      onSuccess: () => reset('password'),
  })
  ```

  ```js Svelte icon="s" theme={null}
  form.post("/profile", {
    preserveScroll: true,
    onSuccess: () => form.reset("password"),
  });
  ```
</CodeGroup>

Bạn có thể truyền HTTP method và URL làm hai đối số đầu tiên cho `useForm()`, sau đó gọi `submit()` không đối số để gửi request. Cách này cũng mở khóa validation theo thời gian thực. Xem [Precognition](#precognition-2) để biết chi tiết.

Nếu cần thay đổi dữ liệu form trước khi gửi đến máy chủ, bạn có thể thực hiện thông qua phương thức `transform()`.

<CodeGroup>
  ```js Vue icon="vuejs" theme={null}
  form
    .transform((data) => ({
      ...data,
      remember: data.remember ? "on" : "",
    }))
    .post("/login");
  ```

  ```js React icon="react" theme={null}
  const { transform } = useForm({ ... })

  transform((data) => ({
      ...data,
      remember: data.remember ? 'on' : '',
  }))
  ```

  ```js Svelte icon="s" theme={null}
  form
    .transform((data) => ({
      ...data,
      remember: data.remember ? "on" : "",
    }))
    .post("/login");
  ```
</CodeGroup>

Bạn có thể dùng thuộc tính `processing` để theo dõi form có đang được submit hay không. Điều này hữu ích để ngăn submit hai lần bằng cách disable nút submit.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <button type="submit" :disabled="form.processing">Submit</button>
  ```

  ```jsx React icon="react" theme={null}
  const { processing } = useForm({ ... })

  <button type="submit" disabled={processing}>Submit</button>
  ```

  ```svelte Svelte icon="s" theme={null}
  <button type="submit" disabled={form.processing}>Submit</button>
  ```
</CodeGroup>

Nếu form đang tải file lên, sự kiện tiến trình hiện tại có sẵn thông qua thuộc tính `progress`, giúp bạn dễ dàng hiển thị tiến độ tải lên.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <progress v-if="form.progress" :value="form.progress.percentage" max="100">
      {{ form.progress.percentage }}%
  </progress>
  ```

  ```jsx React icon="react" theme={null}
  const { progress } = useForm({ ... })

  {progress && (
      <progress value={progress.percentage} max="100">
          {progress.percentage}%
      </progress>
  )}
  ```

  ```svelte Svelte icon="s" theme={null}
  {#if form.progress}
      <progress value={form.progress.percentage} max="100">
          {form.progress.percentage}%
      </progress>
  {/if}
  ```
</CodeGroup>

### Lỗi form

Nếu có form validation error, chúng có sẵn qua thuộc tính `errors`. Khi xây ứng dụng Inertia dùng Laravel, form error tự động được điền khi ứng dụng ném instance `ValidationException`, chẳng hạn khi dùng `{'$request->validate()'}`.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <div v-if="form.errors.email">{{ form.errors.email }}</div>
  ```

  ```jsx React icon="react" theme={null}
  const { errors } = useForm({ ... })

  {errors.email && <div>{errors.email}</div>}
  ```

  ```svelte Svelte icon="s" theme={null}
  {#if form.errors.email}
      <div>{form.errors.email}</div>
  {/if}
  ```
</CodeGroup>

Để tìm hiểu kỹ hơn về form validation và error, xem [tài liệu validation](/v3/the-basics/validation).

Để xác định form có lỗi hay không, bạn có thể dùng thuộc tính `hasErrors`. Để xóa lỗi form, dùng phương thức `clearErrors()`.

<CodeGroup>
  ```js Vue icon="vuejs" theme={null}
  // Clear all errors...
  form.clearErrors();

  // Clear errors for specific fields...
  form.clearErrors("field", "anotherfield");
  ```

  ```js React icon="react" theme={null}
  const { clearErrors } = useForm({ ... })

  // Clear all errors...
  clearErrors()

  // Clear errors for specific fields...
  clearErrors('field', 'anotherfield')
  ```

  ```js Svelte icon="s" theme={null}
  // Clear all errors...
  form.clearErrors();

  // Clear errors for specific fields...
  form.clearErrors("field", "anotherfield");
  ```
</CodeGroup>

Nếu dùng thư viện validation input phía client hoặc tự validation phía client, bạn có thể tự đặt lỗi cho form bằng method `setErrors()`.

<CodeGroup>
  ```js Vue icon="vuejs" theme={null}
  // Set a single error...
  form.setError("field", "Your error message.");

  // Set multiple errors at once...
  form.setError({
    foo: "Your error message for the foo field.",
    bar: "Some other error for the bar field.",
  });
  ```

  ```js React icon="react" theme={null}
  const { setError } = useForm({ ... })

  // Set a single error...
  setError('field', 'Your error message.');

  // Set multiple errors at once...
  setError({
      foo: 'Your error message for the foo field.',
      bar: 'Some other error for the bar field.'
  });
  ```

  ```js Svelte icon="s" theme={null}
  // Set a single error
  form.setError("field", "Your error message.");

  // Set multiple errors at once
  form.setError({
    foo: "Your error message for the foo field.",
    bar: "Some other error for the bar field.",
  });
  ```
</CodeGroup>

Khác với một lần submit form thực tế, props của trang không thay đổi khi bạn tự đặt lỗi trên một instance form.

Khi form được submit thành công, thuộc tính `wasSuccessful` sẽ là `true`. Ngoài ra, form còn có thuộc tính `recentlySuccessful`, được đặt thành `true` trong hai giây sau một lần submit thành công. Bạn có thể dùng thuộc tính này để hiển thị thông báo thành công tạm thời.

Bạn có thể tùy chỉnh thời lượng state `recentlySuccessful` bằng cách đặt tùy chọn `form.recentlySuccessfulDuration` trong [cấu hình mặc định của ứng dụng](/v3/installation/client-side-setup#configuring-defaults). Giá trị mặc định là `2000` millisecond.

### Reset form

Để reset các giá trị của form về giá trị mặc định, bạn có thể dùng phương thức `reset()`.

<CodeGroup>
  ```js Vue icon="vuejs" theme={null}
  // Reset the form...
  form.reset();

  // Reset specific fields...
  form.reset("field", "anotherfield");
  ```

  ```js React icon="react" theme={null}
  const { reset } = useForm({ ... })

  // Reset the form...
  reset()

  // Reset specific fields...
  reset('field', 'anotherfield')
  ```

  ```js Svelte icon="s" theme={null}
  // Reset the form...
  form.reset();

  // Reset specific fields...
  form.reset("field", "anotherfield");
  ```
</CodeGroup>

Đôi khi bạn muốn vừa khôi phục field về giá trị mặc định vừa xóa mọi lỗi validation. Thay vì gọi riêng `reset()` và `clearErrors()`, có thể dùng method `resetAndClearErrors()` để kết hợp cả hai hành động trong một lần gọi.

<CodeGroup>
  ```js Vue icon="vuejs" theme={null}
  // Reset the form and clear all errors...
  form.resetAndClearErrors();

  // Reset specific fields and clear their errors...
  form.resetAndClearErrors("field", "anotherfield");
  ```

  ```js React icon="react" theme={null}
  const { resetAndClearErrors } = useForm({ ... })

  // Reset the form and clear all errors...
  resetAndClearErrors()

  // Reset specific fields and clear their errors...
  resetAndClearErrors('field', 'anotherfield')
  ```

  ```js Svelte icon="s" theme={null}
  // Reset the form and clear all errors...
  form.resetAndClearErrors();

  // Reset specific fields and clear their errors...
  form.resetAndClearErrors("field", "anotherfield");
  ```
</CodeGroup>

### Thiết lập giá trị mặc định mới

Nếu các giá trị mặc định của form không còn phù hợp, bạn có thể dùng phương thức `defaults()` để cập nhật chúng. Sau đó, form sẽ được reset về đúng các giá trị này trong lần tiếp theo phương thức `reset()` được gọi.

<CodeGroup>
  ```js Vue icon="vuejs" theme={null}
  // Set the form's current values as the new defaults...
  form.defaults();

  // Update the default value of a single field...
  form.defaults("email", "updated-default@example.com");

  // Update the default value of multiple fields...
  form.defaults({
    name: "Updated Example",
    email: "updated-default@example.com",
  });
  ```

  ```js React icon="react" theme={null}
  const { setDefaults } = useForm({ ... })

  // Set the form's current values as the new defaults...
  setDefaults()

  // Update the default value of a single field...
  setDefaults('email', 'updated-default@example.com')

  // Update the default value of multiple fields...
  setDefaults({
      name: 'Updated Example',
      email: 'updated-default@example.com',
  })
  ```

  ```js Svelte icon="s" theme={null}
  // Set the form's current values as the new defaults...
  form.defaults();

  // Update the default value of a single field...
  form.defaults("email", "updated-default@example.com");

  // Change the default value of multiple fields...
  form.defaults({
    name: "Updated Example",
    email: "updated-default@example.com",
  });
  ```
</CodeGroup>

### Theo dõi thay đổi field của form

Để xác định form có thay đổi nào hay không, bạn có thể dùng thuộc tính `isDirty`.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <div v-if="form.isDirty">There are unsaved form changes.</div>
  ```

  ```jsx React icon="react" theme={null}
  const { isDirty } = useForm({ ... })

  {isDirty && <div>There are unsaved form changes.</div>}
  ```

  ```svelte Svelte icon="s" theme={null}
  {#if form.isDirty}
      <div>There are unsaved form changes.</div>
  {/if}
  ```
</CodeGroup>

### Hủy submit form

Để hủy một lần submit form, dùng phương thức `cancel()`.

<CodeGroup>
  ```js Vue icon="vuejs" theme={null}
  form.cancel();
  ```

  ```js React icon="react" theme={null}
  const { cancel } = useForm({ ... })

  cancel()
  ```

  ```js Svelte icon="s" theme={null}
  form.cancel();
  ```
</CodeGroup>

### Dữ liệu form và history state

Để yêu cầu Inertia lưu data và error của form vào [history state](/v3/data-props/remembering-state), bạn có thể cung cấp form key duy nhất làm đối số đầu tiên khi tạo form.

<CodeGroup>
  ```js Vue icon="vuejs" theme={null}
  import { useForm } from '@inertiajs/vue3'

  const form = useForm('CreateUser', data)
  const form = useForm(`EditUser:${user.id}`, data)
  ```

  ```js React icon="react" theme={null}
  import { useForm } from '@inertiajs/react'

  const form = useForm('CreateUser', data)
  const form = useForm(`EditUser:${user.id}`, data)
  ```

  ```js Svelte icon="s" theme={null}
  import { useForm } from '@inertiajs/svelte'

  const form = useForm('CreateUser', data)
  const form = useForm(`EditUser:${user.id}`, data)
  ```
</CodeGroup>

#### Loại trừ field

Các field nhạy cảm như password có thể được loại khỏi history state bằng phương thức `dontRemember()`.

<CodeGroup>
  ```js Vue icon="vuejs" theme={null}
  import { useForm } from "@inertiajs/vue3";

  const form = useForm("LoginForm", {
    email: "",
    password: "",
  }).dontRemember("password");
  ```

  ```js React icon="react" theme={null}
  import { useForm } from "@inertiajs/react";

  const form = useForm("LoginForm", {
    email: "",
    password: "",
  }).dontRemember("password");
  ```

  ```js Svelte icon="s" theme={null}
  import { useForm } from "@inertiajs/svelte";

  const form = useForm("LoginForm", {
    email: "",
    password: "",
  }).dontRemember("password");
  ```
</CodeGroup>

Có thể loại trừ nhiều field bằng cách truyền thêm đối số.

```js theme={null}
form.dontRemember("password", "password_confirmation");
```

<Note>
  Một số trình duyệt kích hoạt hộp thoại "save password" bất cứ khi nào giá trị password field
  được ghi vào history state, kể cả không gửi form. Loại password
  field giúp tránh vấn đề này.
</Note>

### Wayfinder

Khi dùng [Wayfinder](https://github.com/laravel/wayfinder) cùng form helper, bạn chỉ cần truyền object kết quả trực tiếp vào method `form.submit`. Form helper sẽ suy ra HTTP method và URL từ object Wayfinder.

<CodeGroup>
  ```js Vue icon="vuejs" theme={null}
  import { useForm } from "@inertiajs/vue3";
  import { store } from "App/Http/Controllers/UserController";

  const form = useForm({
    name: "John Doe",
    email: "john.doe@example.com",
  });

  form.submit(store());
  ```

  ```js React icon="react" theme={null}
  import { useForm } from "@inertiajs/react";
  import { store } from "App/Http/Controllers/UserController";

  const form = useForm({
    name: "John Doe",
    email: "john.doe@example.com",
  });

  form.submit(store());
  ```

  ```js Svelte icon="s" theme={null}
  import { useForm } from "@inertiajs/svelte";
  import { store } from "App/Http/Controllers/UserController";

  const form = useForm({
    name: "John Doe",
    email: "john.doe@example.com",
  });

  form.submit(store());
  ```
</CodeGroup>

### Precognition

Tương tự component `<Form>`, helper `useForm` hỗ trợ [Precognition](#precognition) cho validation thời gian thực. Bạn có thể bật bằng cách chain method `withPrecognition()` với HTTP method và endpoint dành cho request validation.

<Note>
  Precognition yêu cầu hỗ trợ phía server. Người dùng Laravel nên xem
  [tài liệu Laravel Precognition](https://laravel.com/docs/precognition)
  để biết cách thiết lập. Với framework khác, xem [trang protocol
  ](/v3/core-concepts/the-protocol#request-headers) để biết chi tiết implementation.
  chi tiết.
</Note>

<CodeGroup>
  ```js Vue icon="vuejs" theme={null}
  import { useForm } from "@inertiajs/vue3";

  const form = useForm({
    name: "",
    email: "",
  }).withPrecognition("post", "/users");
  ```

  ```js React icon="react" theme={null}
  import { useForm } from "@inertiajs/react";

  const form = useForm({
    name: "",
    email: "",
  }).withPrecognition("post", "/users");
  ```

  ```js Svelte icon="s" theme={null}
  import { useForm } from "@inertiajs/svelte";

  const form = useForm({
    name: "",
    email: "",
  }).withPrecognition("post", "/users");
  ```
</CodeGroup>

Để tương thích ngược với package `laravel-precognition`, bạn cũng có thể truyền method và URL làm các đối số đầu tiên cho `useForm()`.

```js theme={null}
const form = useForm("post", "/users", {
  name: "",
  email: "",
});
```

<Tip>
  Vì Precognition giờ đã tích hợp sẵn, bạn có thể gỡ package `laravel-precognition`
  và import `useForm` từ Inertia adapter thay thế.
</Tip>

Bạn cũng có thể dùng [Wayfinder](https://github.com/laravel/wayfinder) khi bật Precognition.

```js theme={null}
import { store } from "App/Http/Controllers/UserController";

const form = useForm({
  name: "",
  email: "",
}).withPrecognition(store());

// Or passing Wayfinder as the first argument...
const form = useForm(store(), {
  name: "",
  email: "",
});
```

Sau khi bật Precognition, gọi `validate()` với tên field để kích hoạt validation cho field đó. Helper `invalid()` kiểm tra field có lỗi validation hay không, còn `validating` cho biết request đang chạy.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <script setup>
  import { useForm } from "@inertiajs/vue3";

  const form = useForm("post", "/users", {
    name: "",
    email: "",
  });
  </script>

  <template>
    <form @submit.prevent="form.submit()">
      <input v-model="form.name" @change="form.validate('name')" />
      <p v-if="form.invalid('name')">{{ form.errors.name }}</p>

      <input v-model="form.email" @change="form.validate('email')" />
      <p v-if="form.invalid('email')">{{ form.errors.email }}</p>

      <p v-if="form.validating">Validating...</p>

      <button type="submit">Create User</button>
    </form>
  </template>
  ```

  ```jsx React icon="react" theme={null}
  import { useForm } from "@inertiajs/react";

  const { data, setData, submit, errors, validating, validate, invalid } = useForm(
    "post",
    "/users",
    {
      name: "",
      email: "",
    },
  );

  function handleSubmit(e) {
    e.preventDefault();
    submit();
  }

  return (
    <form onSubmit={handleSubmit}>
      <input
        value={data.name}
        onChange={(e) => setData("name", e.target.value)}
        onBlur={() => validate("name")}
      />
      {invalid("name") && <p>{errors.name}</p>}

      <input
        value={data.email}
        onChange={(e) => setData("email", e.target.value)}
        onBlur={() => validate("email")}
      />
      {invalid("email") && <p>{errors.email}</p>}

      {validating && <p>Validating...</p>}

      <button type="submit">Create User</button>
    </form>
  );
  ```

  ```svelte Svelte icon="s" theme={null}
  <script>
  import { useForm } from '@inertiajs/svelte'

  const form = useForm('post', '/users', {
      name: '',
      email: '',
  })
  </script>

  <form onsubmit={(e) => { e.preventDefault(); form.submit() }}>
      <input bind:value={form.name} onchange={() => form.validate('name')} />
      {#if form.invalid('name')}
          <p>{form.errors.name}</p>
      {/if}

      <input bind:value={form.email} onchange={() => form.validate('email')} />
      {#if form.invalid('email')}
          <p>{form.errors.email}</p>
      {/if}

      {#if form.validating}
          <p>Validating...</p>
      {/if}

      <button type="submit">Create User</button>
  </form>
  ```
</CodeGroup>

Bạn cũng có thể dùng helper `valid()` để kiểm tra field đã pass validation hay chưa.

#### Touch và validate

Method `touch()` đánh dấu field là "touched" mà không kích hoạt validation. Sau đó bạn có thể validation toàn bộ field touched bằng cách gọi `validate()` không đối số. Helper `touched()` kiểm tra field đã touched hay chưa. Method `reset()` xóa touched state cho các field được reset.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <input v-model="form.name" @blur="form.touch('name')" />
  <input v-model="form.email" @blur="form.touch('email')" />

  <button type="button" @click="form.validate()">Validate Touched Fields</button>

  <p v-if="form.touched('name')">Name has been touched</p>
  ```

  ```jsx React icon="react" theme={null}
  <input value={data.name} onChange={e => setData('name', e.target.value)} onBlur={() => touch('name')} />
  <input value={data.email} onChange={e => setData('email', e.target.value)} onBlur={() => touch('email')} />

  <button type="button" onClick={() => validate()}>Validate Touched Fields</button>

  {touched('name') && <p>Name has been touched</p>}
  ```

  ```svelte Svelte icon="s" theme={null}
  <input bind:value={form.name} onblur={() => form.touch('name')} />
  <input bind:value={form.email} onblur={() => form.touch('email')} />

  <button type="button" onclick={() => form.validate()}>Validate Touched Fields</button>

  {#if form.touched('name')}
      <p>Name has been touched</p>
  {/if}
  ```
</CodeGroup>

#### Options

Request validation tự động được debounce. Request đầu tiên chạy ngay, các thay đổi tiếp theo được debounce (mặc định 1500ms). Bạn có thể tùy chỉnh timeout bằng `setValidationTimeout()`.

<CodeGroup>
  ```js Vue icon="vuejs" theme={null}
  const form = useForm("post", "/users", {
    name: "",
  }).setValidationTimeout(500);
  ```

  ```js React icon="react" theme={null}
  const form = useForm("post", "/users", {
    name: "",
  });

  form.setValidationTimeout(500);
  ```

  ```js Svelte icon="s" theme={null}
  const form = useForm("post", "/users", {
    name: "",
  });

  form.setValidationTimeout(500);
  ```
</CodeGroup>

Mặc định, file bị loại khỏi request validation để tránh upload không cần thiết. Bạn có thể bật validation file bằng `validateFiles()`.

<CodeGroup>
  ```js Vue icon="vuejs" theme={null}
  const form = useForm("post", "/users", {
    avatar: null,
  }).validateFiles();
  ```

  ```js React icon="react" theme={null}
  const form = useForm("post", "/users", {
    avatar: null,
  });

  form.validateFiles();
  ```

  ```js Svelte icon="s" theme={null}
  const form = useForm("post", "/users", {
    avatar: null,
  });

  form.validateFiles();
  ```
</CodeGroup>

Mặc định, lỗi validation được đơn giản hóa thành chuỗi (message lỗi đầu tiên). Bạn có thể yêu cầu nhận toàn bộ lỗi dưới dạng array bằng `withAllErrors()`.

<CodeGroup>
  ```js Vue icon="vuejs" theme={null}
  const form = useForm("post", "/users", {
    name: "",
  }).withAllErrors();
  ```

  ```js React icon="react" theme={null}
  const form = useForm("post", "/users", {
    name: "",
  });

  form.withAllErrors();
  ```

  ```js Svelte icon="s" theme={null}
  const form = useForm("post", "/users", {
    name: "",
  });

  form.withAllErrors();
  ```
</CodeGroup>

Khi Precognition được bật, bạn có thể gọi `submit()` không cần đối số để gửi request tới endpoint đã cấu hình.

## Response phía máy chủ

Khi dùng Inertia, bạn thường không kiểm tra form response ở phía client như với request XHR/fetch truyền thống. Thay vào đó, route hoặc controller phía server trả [redirect](/v3/the-basics/redirects) sau khi xử lý form, thường redirect tới trang thành công.

```php theme={null}
class UsersController extends Controller
{
    public function index()
    {
        return Inertia::render('Users/Index', [
            'users' => User::all(),
        ]);
    }

    public function store(Request $request)
    {
        User::create($request->validate([
            'first_name' => ['required', 'max:50'],
            'last_name' => ['required', 'max:50'],
            'email' => ['required', 'max:50', 'email'],
        ]));

        return to_route('users.index');
    }
}
```

Cách tiếp cận dựa trên redirect này hoạt động với mọi phương thức submit form: component `<Form>`, helper `useForm` và submit thủ công bằng router. Nó khiến việc xử lý form Inertia rất giống submit form truyền thống phía máy chủ.

## Validation phía máy chủ

Cả component `<Form>` và helper `useForm` đều tự động xử lý lỗi validation phía máy chủ. Khi máy chủ trả lỗi validation, chúng tự động có trong object `errors` mà không cần cấu hình thêm.

Khác với request XHR/fetch truyền thống nơi bạn có thể kiểm tra status code `422`, Inertia xử lý lỗi validation như một phần của flow dựa trên redirect, giống submit form truyền thống phía máy chủ nhưng không tải lại toàn bộ trang.

Để xem hướng dẫn đầy đủ về xử lý validation error, bao gồm error bag và các trường hợp nâng cao, xem [tài liệu validation](/v3/the-basics/validation).

## Submit form thủ công

Bạn cũng có thể submit form thủ công bằng trực tiếp các method `router` của Inertia mà không dùng component `<Form>` hoặc helper `useForm`:

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <script setup>
  import { reactive } from "vue";
  import { router } from "@inertiajs/vue3";

  const form = reactive({
    first_name: null,
    last_name: null,
    email: null,
  });

  function submit() {
    router.post("/users", form);
  }
  </script>

  <template>
    <form @submit.prevent="submit">
      <label for="first_name">First name:</label>
      <input id="first_name" v-model="form.first_name" />
      <label for="last_name">Last name:</label>
      <input id="last_name" v-model="form.last_name" />
      <label for="email">Email:</label>
      <input id="email" v-model="form.email" />
      <button type="submit">Submit</button>
    </form>
  </template>
  ```

  ```jsx React icon="react" theme={null}
  import { useState } from "react";
  import { router } from "@inertiajs/react";

  export default function Edit() {
    const [values, setValues] = useState({
      first_name: "",
      last_name: "",
      email: "",
    });

    function handleChange(e) {
      const key = e.target.id;
      const value = e.target.value;
      setValues((values) => ({
        ...values,
        [key]: value,
      }));
    }

    function handleSubmit(e) {
      e.preventDefault();
      router.post("/users", values);
    }

    return (
      <form onSubmit={handleSubmit}>
        <label htmlFor="first_name">First name:</label>
        <input
          id="first_name"
          value={values.first_name}
          onChange={handleChange}
        />
        <label htmlFor="last_name">Last name:</label>
        <input id="last_name" value={values.last_name} onChange={handleChange} />
        <label htmlFor="email">Email:</label>
        <input id="email" value={values.email} onChange={handleChange} />
        <button type="submit">Submit</button>
      </form>
    );
  }
  ```

  ```svelte Svelte icon="s" theme={null}
  <script>
  import { router } from '@inertiajs/svelte'

  let values = {
      first_name: null,
      last_name: null,
      email: null,
  }

  function submit(e) {
      e.preventDefault()
      router.post('/users', values)
  }
  </script>

  <form onsubmit={submit}>
      <label for="first_name">First name:</label>
      <input id="first_name" bind:value={values.first_name}>

      <label for="last_name">Last name:</label>
      <input id="last_name" bind:value={values.last_name}>

      <label for="email">Email:</label>
      <input id="email" bind:value={values.email}>

      <button type="submit">Submit</button>
  </form>
  ```
</CodeGroup>

## Tải file lên

Khi gửi request hoặc form có file, Inertia tự động chuyển request data thành object `FormData`. Cơ chế này hoạt động với component `<Form>`, helper `useForm` và manual router submission.

Để biết thêm về upload file, bao gồm theo dõi tiến trình, xem [tài liệu file uploads](/v3/the-basics/file-uploads).

## Optimistic updates

Cả component `<Form>` và helper `useForm` đều hỗ trợ optimistic update, cho phép cập nhật UI ngay trước khi server phản hồi. Xem [tài liệu optimistic updates](/v3/the-basics/optimistic-updates) để biết thêm.

## Submission không qua Inertia

Dùng Inertia để gửi form phù hợp với phần lớn tình huống. Với HTTP request độc lập không kích hoạt page visit, bạn có thể dùng hook [`useHttp`](/v3/the-basics/http-requests), cung cấp trải nghiệm phát triển tương tự `useForm`. Bạn cũng có thể tự do dùng XHR hoặc `fetch` thông thường với thư viện tùy chọn.

***

## Tài liệu chính thức

Bài dịch này được đối chiếu từ [tài liệu Inertia.js v3 chính thức](https://inertiajs.com/docs/v3/the-basics/forms). Nếu có khác biệt do phiên bản hoặc cập nhật mới, hãy ưu tiên tài liệu chính thức làm nguồn tham chiếu.
