1
0
mirror of synced 2024-11-30 00:56:09 +03:00
vue-formulario/src/FormulateInput.vue

254 lines
6.1 KiB
Vue
Raw Normal View History

<template>
<div
class="formulate-input"
:data-classification="classification"
:data-has-errors="hasErrors"
:data-is-showing-errors="hasErrors && showFieldErrors"
:data-type="type"
>
<div class="formulate-input-wrapper">
<slot
2020-01-28 20:12:08 +03:00
v-if="context.hasLabel && context.labelPosition === 'before'"
name="label"
v-bind="context"
>
<label
class="formulate-input-label formulate-input-label--before"
:for="context.attributes.id"
v-text="context.label"
/>
</slot>
2020-01-28 20:12:08 +03:00
<slot
name="element"
v-bind="context"
>
<component
:is="context.component"
:context="context"
2020-01-28 20:12:08 +03:00
>
<slot v-bind="context" />
</component>
</slot>
<slot
2020-01-28 20:12:08 +03:00
v-if="context.hasLabel && context.labelPosition === 'after'"
name="label"
v-bind="context.label"
>
<label
class="formulate-input-label formulate-input-label--after"
:for="context.attributes.id"
v-text="context.label"
/>
</slot>
</div>
<div
v-if="help"
class="formulate-input-help"
v-text="help"
/>
<FormulateInputErrors
v-if="showFieldErrors"
:errors="mergedErrors"
/>
</div>
</template>
<script>
import context from './libs/context'
import { shallowEqualObjects, parseRules } from './libs/utils'
import nanoid from 'nanoid'
export default {
name: 'FormulateInput',
inheritAttrs: false,
2019-10-09 06:54:16 +03:00
inject: {
formulateFormSetter: { default: undefined },
formulateFormRegister: { default: undefined },
getFormValues: { default: () => () => ({}) }
2019-10-09 06:54:16 +03:00
},
model: {
prop: 'formulateValue',
event: 'input'
},
props: {
type: {
type: String,
default: 'text'
},
2019-10-09 06:54:16 +03:00
name: {
type: [String, Boolean],
2019-10-09 06:54:16 +03:00
default: true
},
/* eslint-disable */
formulateValue: {
2019-10-30 06:33:31 +03:00
default: ''
},
value: {
default: false
},
2019-10-09 06:54:16 +03:00
/* eslint-enable */
options: {
type: [Object, Array, Boolean],
default: false
},
optionGroups: {
type: [Object, Boolean],
default: false
},
id: {
type: [String, Boolean, Number],
default: false
},
label: {
type: [String, Boolean],
default: false
},
labelPosition: {
type: [String, Boolean],
default: false
},
help: {
type: [String, Boolean],
default: false
},
debug: {
type: Boolean,
default: false
},
errors: {
type: [String, Array, Boolean],
default: false
},
validation: {
type: [String, Boolean, Array],
default: false
},
validationName: {
type: [String, Boolean],
default: false
},
error: {
type: [String, Boolean],
default: false
},
errorBehavior: {
type: String,
default: 'blur',
validator: function (value) {
return ['blur', 'live'].includes(value)
}
},
showErrors: {
type: Boolean,
default: false
},
imageBehavior: {
type: String,
default: 'preview'
},
uploadUrl: {
type: [String, Boolean],
default: false
},
uploader: {
type: [Function, Object, Boolean],
default: false
},
uploadBehavior: {
type: String,
default: 'live'
},
preventWindowDrops: {
type: Boolean,
default: true
}
},
data () {
return {
2020-01-28 20:12:08 +03:00
/**
* @todo consider swapping out nanoid for this._uid
*/
defaultId: nanoid(9),
2019-10-30 06:33:31 +03:00
localAttributes: {},
internalModelProxy: this.formulateValue || this.value,
behavioralErrorVisibility: (this.errorBehavior === 'live'),
formShouldShowErrors: false,
validationErrors: [],
pendingValidation: Promise.resolve()
}
},
computed: {
...context,
classification () {
const classification = this.$formulate.classify(this.type)
return (classification === 'box' && this.options) ? 'group' : classification
},
component () {
return (this.classification === 'group') ? 'FormulateInputGroup' : this.$formulate.component(this.type)
}
},
watch: {
'$attrs': {
handler (value) {
this.updateLocalAttributes(value)
},
deep: true
2019-10-30 06:33:31 +03:00
},
internalModelProxy (newValue, oldValue) {
this.performValidation()
2019-10-30 06:33:31 +03:00
if (!this.isVmodeled && !shallowEqualObjects(newValue, oldValue)) {
this.context.model = newValue
}
},
formulateValue (newValue, oldValue) {
if (this.isVmodeled && !shallowEqualObjects(newValue, oldValue)) {
this.context.model = newValue
}
}
},
created () {
2019-10-09 06:54:16 +03:00
if (this.formulateFormRegister && typeof this.formulateFormRegister === 'function') {
2019-10-30 06:33:31 +03:00
this.formulateFormRegister(this.nameOrFallback, this)
2019-10-09 06:54:16 +03:00
}
this.updateLocalAttributes(this.$attrs)
this.performValidation()
},
methods: {
updateLocalAttributes (value) {
if (!shallowEqualObjects(value, this.localAttributes)) {
this.localAttributes = value
}
},
performValidation () {
const rules = parseRules(this.validation, this.$formulate.rules())
this.pendingValidation = Promise.all(
rules.map(([rule, args]) => {
return rule({
value: this.context.model,
getFormValues: this.getFormValues.bind(this),
name: this.context.name
}, ...args)
.then(res => res ? false : this.$formulate.validationMessage(rule.name, {
args,
name: this.mergedValidationName,
value: this.context.model,
vm: this,
formValues: this.getFormValues()
}))
})
)
.then(result => result.filter(result => result))
.then(errorMessages => { this.validationErrors = errorMessages })
return this.pendingValidation
},
hasValidationErrors () {
return new Promise(resolve => {
this.$nextTick(() => {
this.pendingValidation.then(() => resolve(!!this.validationErrors.length))
})
})
}
}
}
</script>