Appearance
Component-Specific Validation Logic
Overview
Hver form input-komponent håndterer sin egen spesialiserte validering og input-prosessering, mens de deler felles konfigurasjon via useInputValidation.
Input-Specific Logic
FormTextInput & FormEmailInput
typescript
handleInput(value: string) {
// Prevent typing beyond max length (better UX)
let processedValue = value
if (this.maxLength && processedValue.length > this.maxLength) {
processedValue = processedValue.substring(0, this.maxLength)
}
this.$emit('input', processedValue)
}FormPhoneInput
typescript
handleInput(inputValue: string) {
// Remove non-numeric characters
let cleanValue = inputValue.replace(/\D/g, '')
// Apply maxLength constraint
if (this.maxLength && cleanValue.length > this.maxLength) {
cleanValue = cleanValue.substring(0, this.maxLength)
}
this.$emit('input', cleanValue)
}
handleKeydown(event: KeyboardEvent) {
// Block non-numeric input at keyboard level
const allowedKeys = ['Backspace', 'Delete', 'Tab', 'Escape', 'Enter', ...]
if (!/^[0-9]$/.test(event.key) && !allowedKeys.includes(event.key)) {
event.preventDefault()
}
}FormNumberInput
typescript
// Uses HTML5 number input with min/max/step attributes
computed: {
minValue() {
const val = parseFloat(this.validation.min)
return isNaN(val) ? undefined : val
},
maxValue() {
const val = parseFloat(this.validation.max)
return isNaN(val) ? undefined : val
},
stepValue() {
const val = parseFloat(this.validation.step)
return isNaN(val) ? 'any' : val
}
}FormDateInput
typescript
handleChange(inputValue: string) {
if (this.isValidDate(inputValue)) {
this.$emit('input', inputValue)
}
}
isValidDate(dateString: string) {
if (!dateString) return !this.isRequired
const inputDate = new Date(dateString)
// Check if valid date
if (isNaN(inputDate.getTime())) return false
// Check min/max date constraints
if (this.minDate && inputDate < new Date(this.minDate)) return false
if (this.maxDate && inputDate > new Date(this.maxDate)) return false
return true
}FormTextAreaInput
typescript
computed: {
rows() {
const val = parseInt(this.input.rows)
return isNaN(val) ? 3 : Math.max(1, val)
},
resizable() {
return this.input.resizable !== false
}
}
// Template uses dynamic rows and resize styling
:rows="rows"
:style="{ resize: resizable ? 'vertical' : 'none' }"Shared Configuration Pattern
Alle komponenter følger samme mønster:
typescript
import { useInputValidation } from '@/composables/useInputValidation'
setup(props) {
const {
validation,
isRequired,
minLength,
maxLength,
placeholder,
pattern
} = useInputValidation(props.input)
return { validation, isRequired, minLength, maxLength, placeholder, pattern }
}Fordeler
Spesialisert UX per input-type:
- Phone: Kun numeriske tegn
- Email: Pattern validation
- Date: Min/max date constraints
- Number: Step/min/max constraints
Konsistent konfigurasjon:
- Alle bruker samme validation properties
- Felles placeholder/required logic
Fleksibel arkitektur:
- Lett å legge til nye input-typer
- Hver komponent kan ha unik logikk
Bedre vedlikehold:
- Endringer i én input-type påvirker ikke andre
- Klar separasjon av ansvar