Skip to content

Form

Form consists of input, select, radio, checkbox and other controls, for collecting, validating, and submitting data.

NameCategoryDescriptionTypeDefault
inline? StyleWhether to use inline form layoutbooleanfalse
labelWidth? StyleLabel widthnumber | string
labelPosition? StyleLabel position'left' | 'right' | 'top''right'
hideRequiredAsterisk? StyleWhether to hide red asteriskbooleanfalse
requiredAsteriskPosition? StyleAsterisk position'left' | 'right''left'
FormItemlabelWidth? StyleLabel widthnumber | string
disabled? StateWhether to disable formbooleanfalse
FormItemdisabled? StateWhether disabledbooleanfalse
rules? BehaviorForm validation rules (supports Zod schema)FormRules
statusIcon? BehaviorWhether to show status icon after validationbooleanfalse
showMessage? BehaviorWhether to show validation error messagebooleantrue
FormItemrequired? BehaviorWhether requiredbooleanfalse
FormItemrules? BehaviorValidation rules (supports Zod schema or required shorthand)FormItemRule[]
FormItemshowMessage? BehaviorWhether to show validation errorbooleantrue
FormItemfor? BehaviorLabel's for attribute, associates with a specific form controlstring
model? ContentForm data objectRecord<string, any>
labelSuffix? ContentLabel suffixstring
FormItemprop? ContentKey name of modelstring | string[]
FormItemlabel? ContentLabel textstring
FormItemerror? ContentError messagestring
validate? EventTriggered after field validation(prop: string, isValid: boolean, message: string) => void
FormItemdefault? SlotForm item content
FormItemlabel? SlotCustom label
FormItemerror? SlotCustom error message
validate? ExposeValidate the whole form(callback?: FormValidateCallback) => Promise<boolean>
validateField? ExposeValidate specific fields(props?: string[], callback?: FormValidateCallback) => Promise<boolean>
resetFields? ExposeReset form fields(props?: string[]) => void
clearValidate? ExposeClear validation status(props?: string[]) => void

Basic Usage

Basic form data control, including various form items.

<template>
  <px-form :model="form" label-width="80px">
    <px-form-item label="Name">
      <px-input v-model="form.name" placeholder="Enter name" />
    </px-form-item>
    <px-form-item label="Email">
      <px-input v-model="form.email" placeholder="Enter email" />
    </px-form-item>
    <px-form-item label="Active">
      <px-switch v-model="form.active" />
    </px-form-item>
    <px-form-item>
      <px-button type="primary" @click="onSubmit">Submit</px-button>
      <px-button @click="onReset">Reset</px-button>
    </px-form-item>
  </px-form>
</template>

<script setup lang="ts">
import { PxMessage } from 'sakana-element';
import { reactive } from 'vue';

const form = reactive({
  name: '',
  email: '',
  active: false,
});

const onSubmit = () => {
  PxMessage.success('Submit!');
};

const onReset = () => {
  form.name = '';
  form.email = '';
  form.active = false;
};
</script>

Form Validation

Form validation uses Zod schemas. Each rule can provide a schema (Zod type) or use the required shorthand for simple required checks.

<template>
  <px-form ref="formRef" :model="form" :rules="rules" label-width="80px">
    <px-form-item label="Name" prop="name">
      <px-input v-model="form.name" placeholder="Enter name" />
    </px-form-item>
    <px-form-item label="Email" prop="email">
      <px-input v-model="form.email" placeholder="Enter email" />
    </px-form-item>
    <px-form-item>
      <px-button type="primary" @click="onSubmit">Submit</px-button>
      <px-button @click="onReset">Reset</px-button>
    </px-form-item>
  </px-form>
</template>

<script setup lang="ts">
import { PxMessage } from 'sakana-element';
import { reactive, ref } from 'vue';
import { z } from 'zod';

const formRef = ref();

const form = reactive({
  name: '',
  email: '',
});

const rules = {
  name: [
    { schema: z.string().min(1, 'Please input name'), trigger: 'blur' },
    { schema: z.string().min(2).max(20, 'Length should be 2 to 20'), trigger: 'blur' },
  ],
  email: [
    { schema: z.string().min(1, 'Please input email'), trigger: 'blur' },
    { schema: z.string().email('Please input a valid email'), trigger: 'blur' },
  ],
};

const onSubmit = async () => {
  try {
    await formRef.value.validate();
    PxMessage.success('Submit success!');
  } catch {
    PxMessage.danger('Validation failed');
  }
};

const onReset = () => {
  formRef.value.resetFields();
};
</script>

Inline Form

Set inline to display form items in a horizontal row, useful for search forms and filters.

<template>
  <px-form ref="formRef" :model="form" inline label-width="60px">
    <px-form-item label="Name" prop="name">
      <px-input v-model="form.name" placeholder="Name" />
    </px-form-item>
    <px-form-item label="Email" prop="email">
      <px-input v-model="form.email" placeholder="Email" />
    </px-form-item>
    <px-form-item>
      <px-button type="primary" @click="onSearch">Search</px-button>
    </px-form-item>
  </px-form>
</template>

<script setup lang="ts">
import { PxMessage } from 'sakana-element';
import { reactive, ref } from 'vue';

const formRef = ref();
const form = reactive({
  name: '',
  email: '',
});

const onSearch = () => {
  PxMessage.success('Search submitted!');
};
</script>

Label Position

You can change the position of form field labels by setting label-position attribute.

<template>
  <div class="demo-form">
    <div class="buttons">
      <px-button @click="labelPosition = 'left'">Left</px-button>
      <px-button @click="labelPosition = 'right'">Right</px-button>
      <px-button @click="labelPosition = 'top'">Top</px-button>
    </div>
    <px-form :model="form" :label-position="labelPosition" label-width="80px">
      <px-form-item label="Name">
        <px-input v-model="form.name" placeholder="Enter name" />
      </px-form-item>
      <px-form-item label="Email">
        <px-input v-model="form.email" placeholder="Enter email" />
      </px-form-item>
      <px-form-item>
        <px-button type="primary" @click="onSubmit">Submit</px-button>
        <px-button @click="onCancel">Cancel</px-button>
      </px-form-item>
    </px-form>
  </div>
</template>

<script setup lang="ts">
import { reactive, ref } from 'vue';

const labelPosition = ref<'left' | 'right' | 'top'>('right');

const form = reactive({
  name: '',
  email: '',
});

function onSubmit() {}

function onCancel() {
  form.name = '';
  form.email = '';
}
</script>

<style scoped>
.demo-form .buttons {
  margin-bottom: 16px;
  display: flex;
  gap: 8px;
}
</style>

Status Icon

Set status-icon to show check/close icons after field validation.

<template>
  <px-form ref="formRef" :model="form" :rules="rules" status-icon label-width="80px">
    <px-form-item label="Name" prop="name">
      <px-input v-model="form.name" placeholder="Enter name" />
    </px-form-item>
    <px-form-item label="Email" prop="email">
      <px-input v-model="form.email" placeholder="Enter email" />
    </px-form-item>
    <px-form-item>
      <px-button type="primary" @click="onSubmit">Validate</px-button>
      <px-button @click="onReset">Reset</px-button>
    </px-form-item>
  </px-form>
</template>

<script setup lang="ts">
import { reactive, ref } from 'vue';
import { z } from 'zod';

const formRef = ref();
const form = reactive({
  name: '',
  email: '',
});

const rules = {
  name: [{ schema: z.string().min(1, 'Name is required'), trigger: 'blur' }],
  email: [{ schema: z.string().email('Invalid email'), trigger: 'blur' }],
};

const onSubmit = async () => {
  try {
    await formRef.value.validate();
  } catch {
    // validation failed
  }
};

const onReset = () => {
  formRef.value.resetFields();
};
</script>

Disabled

Set disabled on the form to disable all form controls.

<template>
  <px-form :model="form" disabled label-width="80px">
    <px-form-item label="Name" prop="name">
      <px-input v-model="form.name" placeholder="Enter name" />
    </px-form-item>
    <px-form-item label="Email" prop="email">
      <px-input v-model="form.email" placeholder="Enter email" />
    </px-form-item>
    <px-form-item label="Active" prop="active">
      <px-switch v-model="form.active" />
    </px-form-item>
    <px-form-item>
      <px-button type="primary" disabled>Submit</px-button>
    </px-form-item>
  </px-form>
</template>

<script setup lang="ts">
import { reactive } from 'vue';

const form = reactive({
  name: 'Sakana',
  email: 'sakana@example.com',
  active: true,
});
</script>