Validator
A lightweight inline validation wrapper for individual form elements. Unlike Form + FormItem which requires a full form model, PxValidator wraps any input and shows pixel-styled hint text when Zod validation fails. Inspired by DaisyUI's Validator pattern.
| Name | Category | Description | Type |
|---|---|---|---|
rules | Behavior | Validation rules (supports Zod schema or required shorthand) | ValidatorRule[] |
modelValue | Content | The value to validate | unknown |
validate | Event | Triggered after validation completes | (isValid: boolean, message: string) => void |
default | Slot | Form control content | |
hint | Slot | Custom error hint content | { message: string } |
validate | Expose | Manually trigger validation | (trigger?: string) => Promise<boolean> |
reset | Expose | Reset validation state | () => void |
validateStatus | Expose | Current validation status | Ref<'init' | 'error' | 'success'> |
validateMessage | Expose | Current error message | Ref<string> |
Basic Usage
Wrap a form element with PxValidator, bind v-model and provide rules. Use required: true as a shorthand for non-empty string validation.
<template>
<div class="demo-validator">
<px-validator v-model="name" :rules="rules">
<px-input v-model="name" placeholder="Enter your name" />
</px-validator>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const name = ref('');
const rules = [{ required: true, trigger: 'change' }];
</script>
<style scoped>
.demo-validator {
display: flex;
flex-direction: column;
gap: 12px;
max-width: 320px;
}
</style>Zod Schema Validation
Use Zod schemas for rich validation — email format, minimum length, regex patterns, and more.
<template>
<div class="demo-validator">
<px-validator v-model="email" :rules="emailRules">
<px-input v-model="email" placeholder="Email address" />
</px-validator>
<px-validator v-model="password" :rules="passwordRules">
<px-input v-model="password" type="password" placeholder="Password (min 6 chars)" />
</px-validator>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { z } from 'zod';
const email = ref('');
const password = ref('');
const emailRules = [{ schema: z.string().email('Please enter a valid email'), trigger: 'change' }];
const passwordRules = [
{ schema: z.string().min(6, 'Password must be at least 6 characters'), trigger: 'change' },
];
</script>
<style scoped>
.demo-validator {
display: flex;
flex-direction: column;
gap: 12px;
max-width: 320px;
}
</style>Custom Error Message
Override Zod's default error with a custom message in the rule.
<template>
<div class="demo-validator">
<px-validator v-model="age" :rules="rules">
<px-input v-model="age" placeholder="Enter your age" />
</px-validator>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { z } from 'zod';
const age = ref('');
const rules = [
{
schema: z.string().regex(/^\d+$/).pipe(z.coerce.number().min(1).max(150)),
message: 'Please enter a valid age (1-150)',
trigger: 'change',
},
];
</script>
<style scoped>
.demo-validator {
display: flex;
flex-direction: column;
gap: 12px;
max-width: 320px;
}
</style>Trigger Modes
Control when validation runs using trigger: 'change', 'blur', or 'input'.
<template>
<div class="demo-validator">
<p class="demo-validator__label">Validates on change:</p>
<px-validator v-model="val1" :rules="[{ required: true, trigger: 'change' }]">
<px-input v-model="val1" placeholder="Type and clear..." />
</px-validator>
<p class="demo-validator__label">Validates on blur:</p>
<px-validator v-model="val2" :rules="[{ required: true, trigger: 'blur' }]">
<px-input v-model="val2" placeholder="Focus and leave empty..." />
</px-validator>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const val1 = ref('');
const val2 = ref('');
</script>
<style scoped>
.demo-validator {
display: flex;
flex-direction: column;
gap: 12px;
max-width: 320px;
}
.demo-validator__label {
font-family: var(--px-font-family);
font-size: 14px;
color: var(--px-text-color-secondary);
margin: 0;
}
</style>Programmatic Validation
Access validate() and reset() via template ref for manual control.
<template>
<div class="demo-validator">
<px-validator ref="validatorRef" v-model="username" :rules="rules">
<px-input v-model="username" placeholder="Username" />
</px-validator>
<div class="demo-validator__actions">
<px-button type="primary" @click="handleValidate">Validate</px-button>
<px-button @click="handleReset">Reset</px-button>
</div>
<p class="demo-validator__result">
Status: {{ validatorRef?.validateStatus ?? 'init' }}
</p>
</div>
</template>
<script setup lang="ts">
import type { ValidatorInstance } from 'sakana-element';
import { ref } from 'vue';
import { z } from 'zod';
const username = ref('');
const validatorRef = ref<ValidatorInstance>();
const rules = [{ schema: z.string().min(3, 'Username must be at least 3 characters') }];
const handleValidate = async () => {
try {
await validatorRef.value?.validate();
} catch {
// validation failed
}
};
const handleReset = () => {
validatorRef.value?.reset();
};
</script>
<style scoped>
.demo-validator {
display: flex;
flex-direction: column;
gap: 12px;
max-width: 320px;
}
.demo-validator__actions {
display: flex;
gap: 8px;
}
.demo-validator__result {
font-family: var(--px-font-family);
font-size: 14px;
color: var(--px-text-color-secondary);
margin: 0;
}
</style>