1 |
|
2 | function extractValidity(el) {
|
3 | const validity = el.validity
|
4 |
|
5 |
|
6 | let tooShort = validity.tooShort
|
7 | let valid = validity.valid
|
8 | const minlength = el.getAttribute('minlength')
|
9 | if (minlength && typeof tooShort === 'undefined') {
|
10 | tooShort = el.value.length < minlength
|
11 | if (tooShort) {
|
12 | valid = false
|
13 | el.setCustomValidity(`
|
14 | Please lengthen this text to ${minlength} characters or more (you are
|
15 | currently using ${el.value.length} characters).
|
16 | `.trim())
|
17 | } else {
|
18 | el.setCustomValidity('')
|
19 | }
|
20 | }
|
21 |
|
22 | return {
|
23 | badInput: validity.badInput,
|
24 | customError: validity.customError,
|
25 | patternMismatch: validity.patternMismatch,
|
26 | rangeOverflow: validity.rangeOverflow,
|
27 | rangeUnderflow: validity.rangeUnderflow,
|
28 | stepMismatch: validity.stepMismatch,
|
29 | tooLong: validity.tooLong,
|
30 | tooShort,
|
31 | typeMismatch: validity.typeMismatch,
|
32 | valid,
|
33 | valueMissing: validity.valueMissing,
|
34 | }
|
35 | }
|
36 |
|
37 | export default class VueForm {
|
38 | constructor (options = {
|
39 | wasFocusedClass: 'wasFocused',
|
40 | wasSubmittedClass: 'wasSubmitted',
|
41 | noValidate: true
|
42 | }) {
|
43 | this.$noValidate = options.noValidate
|
44 | this.$wasFocusedClass = options.wasFocusedClass
|
45 | this.$wasSubmittedClass = options.wasSubmittedClass
|
46 | this.$wasSubmitted = false
|
47 | this.$isInvalid = false
|
48 | this.$isValid = true
|
49 | this.$invalidFields = []
|
50 | }
|
51 |
|
52 | static install (Vue) {
|
53 |
|
54 | Vue.directive('form', {
|
55 | bind (el, { value }) {
|
56 | value.$el = el
|
57 | value.$el.noValidate = value.$noValidate
|
58 |
|
59 |
|
60 | value.$el.addEventListener('submit', () => {
|
61 | value.$wasSubmitted = true
|
62 | value.$el.classList.add(value.$wasSubmittedClass)
|
63 | })
|
64 |
|
65 | value.$el.addEventListener('reset', () => {
|
66 |
|
67 | value.$wasSubmitted = false
|
68 | value.$el.classList.remove(value.$wasSubmittedClass)
|
69 |
|
70 |
|
71 |
|
72 | for (const id of Object.keys(value)) {
|
73 | if (id.indexOf('$') === -1) {
|
74 | value[id].$wasFocused = false
|
75 | value[id].$el.classList.remove(value.$wasFocusedClass)
|
76 | Object.assign(value[id], extractValidity(value[id].$el))
|
77 | value.$updateFormValidity(id)
|
78 | }
|
79 | }
|
80 | })
|
81 |
|
82 | for (const $el of el.querySelectorAll('input, textarea, select')) {
|
83 |
|
84 | if ($el.form === el && $el.willValidate && $el.hasAttribute('id')) {
|
85 |
|
86 | const field = Object.assign({ $el }, extractValidity($el))
|
87 | const id = $el.getAttribute('id')
|
88 | Vue.set(value, id, field)
|
89 | value.$updateFormValidity(id)
|
90 |
|
91 |
|
92 | $el.addEventListener('focus', ({ target }) => {
|
93 | const id = $el.getAttribute('id')
|
94 | value[id].$wasFocused = true
|
95 | target.classList.add(value.$wasFocusedClass)
|
96 | })
|
97 |
|
98 | $el.addEventListener('input', ({ target }) => {
|
99 | const id = target.getAttribute('id')
|
100 | Object.assign(value[id], extractValidity(target))
|
101 | value.$updateFormValidity(id)
|
102 | })
|
103 | }
|
104 | }
|
105 | }
|
106 | })
|
107 | }
|
108 |
|
109 | $setCustomValidity (field, invalid) {
|
110 | if (this[field]) {
|
111 | const isBoolean = typeof invalid === 'boolean'
|
112 | const isNonEmptyString = typeof invalid === 'string' && invalid.length > 0
|
113 | if (invalid && (isBoolean || isNonEmptyString)) {
|
114 | if (isNonEmptyString) {
|
115 | this[field].customMessage = invalid
|
116 | this[field].$el.setCustomValidity(invalid)
|
117 | } else {
|
118 | this[field].$el.setCustomValidity('Error')
|
119 | }
|
120 | } else {
|
121 | delete this[field].customMessage
|
122 | this[field].$el.setCustomValidity('')
|
123 | }
|
124 | Object.assign(this[field], extractValidity(this[field].$el))
|
125 | this.$updateFormValidity(field)
|
126 | }
|
127 | }
|
128 |
|
129 | $updateFormValidity (field) {
|
130 | const index = this.$invalidFields.indexOf(field)
|
131 | if (this[field].valid && index !== -1) {
|
132 | this.$invalidFields.splice(index, 1)
|
133 | if (this.$invalidFields.length === 0) {
|
134 | this.$isValid = true
|
135 | this.$isInvalid = false
|
136 | }
|
137 | } else if (!this[field].valid && index === -1) {
|
138 | this.$isValid = false
|
139 | this.$isInvalid = true
|
140 | this.$invalidFields.push(field)
|
141 | }
|
142 | }
|
143 | }
|