mirror of
https://github.com/YunoHost/yunohost-admin.git
synced 2024-09-03 20:06:15 +02:00
improved FormField and form items to display validation errors
This commit is contained in:
parent
8f47849887
commit
e05b8da533
4 changed files with 107 additions and 47 deletions
|
@ -1,12 +1,29 @@
|
||||||
<template>
|
<template>
|
||||||
|
<!-- v-bind="$attrs" allow to pass default attrs not specified in this component slots -->
|
||||||
<b-form-group
|
<b-form-group
|
||||||
label-cols="0" label-class="font-weight-bold" class="mb-4"
|
v-bind="attrs"
|
||||||
:label="label" :label-for="'form-item-' + props.id"
|
:id="id || props.id + '_group'"
|
||||||
|
:label-for="props.id"
|
||||||
|
:state="state"
|
||||||
|
:invalid-feedback="errorMessage"
|
||||||
|
@touch="touch"
|
||||||
|
class="mb-4"
|
||||||
>
|
>
|
||||||
<slot name="default">
|
<!-- Make field props and state available as scoped slot data -->
|
||||||
<component :is="component" v-bind="props" v-model="props.value" />
|
<slot v-bind="{ self: { ...props, state }, touch }">
|
||||||
|
<!-- if no component was passed as slot, render a component from the props -->
|
||||||
|
<component
|
||||||
|
:is="component"
|
||||||
|
v-bind="props"
|
||||||
|
v-on="$listeners"
|
||||||
|
:value="value"
|
||||||
|
:state="state"
|
||||||
|
:required="validation ? 'required' in validation : false"
|
||||||
|
/>
|
||||||
</slot>
|
</slot>
|
||||||
|
|
||||||
|
<!-- {{ validation }} -->
|
||||||
|
|
||||||
<template v-if="description || example || link" v-slot:description>
|
<template v-if="description || example || link" v-slot:description>
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<span v-if="example">{{ $t('form_input_example', { example }) }}</span>
|
<span v-if="example">{{ $t('form_input_example', { example }) }}</span>
|
||||||
|
@ -18,29 +35,83 @@
|
||||||
|
|
||||||
<span v-if="description" v-html="description" />
|
<span v-if="description" v-html="description" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<b-form-invalid-feedback v-if="'isValid' in props" :id="props.id + '-feedback'" :state="props.value.isValid">
|
|
||||||
{{ props.error }}
|
|
||||||
</b-form-invalid-feedback>
|
|
||||||
</b-form-group>
|
</b-form-group>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'FormItemHelper',
|
name: 'FormField',
|
||||||
|
|
||||||
|
inheritAttrs: false,
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
component: { type: String, default: 'InputItem' },
|
// Component props (other <form-group> related attrs are passed thanks to $attrs)
|
||||||
props: { type: Object, required: true },
|
id: { type: String, default: null },
|
||||||
label: { type: String, required: true },
|
|
||||||
description: { type: String, default: null },
|
description: { type: String, default: null },
|
||||||
example: { type: String, default: null },
|
example: { type: String, default: null },
|
||||||
link: { type: Object, default: null }
|
link: { type: Object, default: null },
|
||||||
|
// rendered field component props
|
||||||
|
component: { type: String, default: 'InputItem' },
|
||||||
|
value: { type: null, default: null },
|
||||||
|
props: { type: Object, default: () => ({}) },
|
||||||
|
validation: { type: Object, default: null }
|
||||||
},
|
},
|
||||||
|
|
||||||
data () {
|
computed: {
|
||||||
return {
|
attrs () {
|
||||||
content: this.value
|
const attrs = { ...this.$attrs }
|
||||||
|
const defaultAttrs = {
|
||||||
|
'label-cols-md': 4,
|
||||||
|
'label-cols-lg': 2,
|
||||||
|
'label-class': 'font-weight-bold'
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const attr in defaultAttrs) {
|
||||||
|
if (!(attr in attrs)) attrs[attr] = defaultAttrs[attr]
|
||||||
|
}
|
||||||
|
|
||||||
|
return attrs
|
||||||
|
},
|
||||||
|
|
||||||
|
state () {
|
||||||
|
// Need to set state as null if no error, else component turn green
|
||||||
|
if (this.validation) {
|
||||||
|
return this.validation.$anyError === true ? false : null
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
},
|
||||||
|
|
||||||
|
errorMessage () {
|
||||||
|
const validation = this.validation
|
||||||
|
if (validation && validation.$anyError) {
|
||||||
|
const [type, errData] = this.findError(validation.$params, validation)
|
||||||
|
return this.$i18n.t('form_errors.' + type, errData)
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
touch (name) {
|
||||||
|
if (this.validation) {
|
||||||
|
// For fields that have multiple elements
|
||||||
|
if (name) {
|
||||||
|
this.validation[name].$touch()
|
||||||
|
} else {
|
||||||
|
this.validation.$touch()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
findError (params, obj, parent = obj) {
|
||||||
|
for (const key in params) {
|
||||||
|
if (!obj[key]) {
|
||||||
|
return [key, obj.$params[key]]
|
||||||
|
}
|
||||||
|
if (obj[key].$anyError) {
|
||||||
|
return this.findError(obj[key].$params, obj[key], parent)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<b-checkbox
|
<b-checkbox
|
||||||
v-model="checked"
|
v-model="checked"
|
||||||
@input="$emit('input', checked)"
|
v-on="$listeners"
|
||||||
:id="id"
|
:id="id"
|
||||||
:aria-describedby="id + '-feedback'"
|
:aria-describedby="$parent.id + '__BV_description_'"
|
||||||
switch
|
switch
|
||||||
>
|
>
|
||||||
{{ $t(checked ? 'yes' : 'no') }}
|
{{ $t(checked ? 'yes' : 'no') }}
|
||||||
|
@ -16,7 +16,7 @@ export default {
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
value: { type: Boolean, required: true },
|
value: { type: Boolean, required: true },
|
||||||
id: { type: String, required: true }
|
id: { type: String, default: null }
|
||||||
},
|
},
|
||||||
|
|
||||||
data () {
|
data () {
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
<template>
|
<template>
|
||||||
<b-input
|
<b-input
|
||||||
v-model="input"
|
:value="value"
|
||||||
@input="$emit('input', input)"
|
|
||||||
:id="id"
|
:id="id"
|
||||||
|
v-on="$listeners"
|
||||||
:placeholder="placeholder"
|
:placeholder="placeholder"
|
||||||
:aria-describedby="id + '-feedback'"
|
|
||||||
:type="type"
|
:type="type"
|
||||||
:state="isValid"
|
:state="state"
|
||||||
:required="required"
|
:required="required"
|
||||||
|
@blur="$parent.$emit('touch', name)"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -16,19 +16,13 @@ export default {
|
||||||
name: 'InputItem',
|
name: 'InputItem',
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
value: { type: [String, Number, null], default: null },
|
value: { type: [String, Number], default: null },
|
||||||
id: { type: String, required: true },
|
id: { type: String, default: null },
|
||||||
placeholder: { type: String, default: null },
|
placeholder: { type: String, default: null },
|
||||||
isValid: { type: [Boolean, null], default: null },
|
|
||||||
error: { type: String, default: '' },
|
|
||||||
type: { type: String, default: 'text' },
|
type: { type: String, default: 'text' },
|
||||||
required: { type: Boolean, default: true }
|
required: { type: Boolean, default: false },
|
||||||
},
|
state: { type: Boolean, default: null },
|
||||||
|
name: { type: String, default: null }
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
input: this.value
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
<b-select
|
<b-select
|
||||||
v-model="selected"
|
:value="value"
|
||||||
:options="choices"
|
|
||||||
@input="$emit('input', selected)"
|
|
||||||
:required="required"
|
|
||||||
:id="id"
|
:id="id"
|
||||||
:aria-describedby="id + '-feedback'"
|
:options="choices"
|
||||||
|
:required="required"
|
||||||
|
v-on="$listeners"
|
||||||
|
@blur="$parent.$emit('touch', name)"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -15,15 +15,10 @@ export default {
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
value: { type: [String, null], default: null },
|
value: { type: [String, null], default: null },
|
||||||
|
id: { type: String, default: null },
|
||||||
choices: { type: [Array, Object], required: true },
|
choices: { type: [Array, Object], required: true },
|
||||||
required: { type: Boolean, default: true },
|
required: { type: Boolean, default: false },
|
||||||
id: { type: String, required: true }
|
name: { type: String, default: null }
|
||||||
},
|
|
||||||
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
selected: this.value
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
Loading…
Add table
Reference in a new issue