Adds new registry class
This commit is contained in:
parent
978a209e3e
commit
f7248d029c
2
dist/formulate.esm.js
vendored
2
dist/formulate.esm.js
vendored
File diff suppressed because one or more lines are too long
6
dist/formulate.min.js
vendored
6
dist/formulate.min.js
vendored
File diff suppressed because one or more lines are too long
2
dist/formulate.umd.js
vendored
2
dist/formulate.umd.js
vendored
File diff suppressed because one or more lines are too long
9
dist/snow.css
vendored
9
dist/snow.css
vendored
@ -28,7 +28,14 @@
|
||||
line-height: 1.5;
|
||||
margin-bottom: .25em; }
|
||||
.formulate-input .formulate-input-group-item {
|
||||
margin-bottom: .5em; }
|
||||
margin-bottom: 1.5em;
|
||||
padding: 1.5em;
|
||||
border: 1px solid #efefef;
|
||||
border-radius: .25em; }
|
||||
.formulate-input .formulate-input-group-item:last-child {
|
||||
margin-bottom: 1.5em;
|
||||
border-bottom: 0;
|
||||
padding-bottom: 0; }
|
||||
.formulate-input:last-child {
|
||||
margin-bottom: 0; }
|
||||
.formulate-input[data-classification='text'] input {
|
||||
|
2
dist/snow.min.css
vendored
2
dist/snow.min.css
vendored
File diff suppressed because one or more lines are too long
@ -2,7 +2,7 @@ import library from './libs/library'
|
||||
import rules from './libs/rules'
|
||||
import mimes from './libs/mimes'
|
||||
import FileUpload from './FileUpload'
|
||||
import { arrayify, parseLocale } from './libs/utils'
|
||||
import { arrayify, parseLocale, has } from './libs/utils'
|
||||
import isPlainObject from 'is-plain-object'
|
||||
import { en } from '@braid/vue-formulate-i18n'
|
||||
import fauxUploader from './libs/faux-uploader'
|
||||
@ -210,7 +210,7 @@ class Formulate {
|
||||
}
|
||||
if (locale) {
|
||||
const option = parseLocale(locale)
|
||||
.find(locale => Object.prototype.hasOwnProperty.call(this.options.locales, locale))
|
||||
.find(locale => has(this.options.locales, locale))
|
||||
if (option) {
|
||||
selection = option
|
||||
}
|
||||
|
@ -12,7 +12,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { shallowEqualObjects, arrayify } from './libs/utils'
|
||||
import { shallowEqualObjects, arrayify, has } from './libs/utils'
|
||||
import Registry from './libs/registry'
|
||||
import FormSubmission from './FormSubmission'
|
||||
|
||||
export default {
|
||||
@ -55,7 +56,7 @@ export default {
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
registry: {},
|
||||
registry: new Registry(),
|
||||
internalFormModelProxy: {},
|
||||
formShouldShowErrors: false,
|
||||
errorObservers: [],
|
||||
@ -87,13 +88,13 @@ export default {
|
||||
},
|
||||
initialValues () {
|
||||
if (
|
||||
Object.prototype.hasOwnProperty.call(this.$options.propsData, 'formulateValue') &&
|
||||
has(this.$options.propsData, 'formulateValue') &&
|
||||
typeof this.formulateValue === 'object'
|
||||
) {
|
||||
// If there is a v-model on the form, use those values as first priority
|
||||
return Object.assign({}, this.formulateValue) // @todo - use a deep clone to detach reference types
|
||||
} else if (
|
||||
Object.prototype.hasOwnProperty.call(this.$options.propsData, 'values') &&
|
||||
has(this.$options.propsData, 'values') &&
|
||||
typeof this.values === 'object'
|
||||
) {
|
||||
// If there are values, use them as secondary priority
|
||||
@ -135,9 +136,9 @@ export default {
|
||||
typeof newValue === 'object'
|
||||
) {
|
||||
for (const field in newValue) {
|
||||
if (this.registry.hasOwnProperty(field) &&
|
||||
if (this.registry.has(field) &&
|
||||
!shallowEqualObjects(newValue[field], this.internalFormModelProxy[field]) &&
|
||||
!shallowEqualObjects(newValue[field], this.registry[field].internalModelProxy[field])
|
||||
!shallowEqualObjects(newValue[field], this.registry.get(field).internalModelProxy[field])
|
||||
) {
|
||||
this.setFieldValue(field, newValue[field])
|
||||
this.registry[field].context.model = newValue[field]
|
||||
@ -184,7 +185,7 @@ export default {
|
||||
this.errorObservers.push(observer)
|
||||
if (observer.type === 'form') {
|
||||
observer.callback(this.mergedFormErrors)
|
||||
} else if (Object.prototype.hasOwnProperty.call(this.mergedFieldErrors, observer.field)) {
|
||||
} else if (has(this.mergedFieldErrors, observer.field)) {
|
||||
observer.callback(this.mergedFieldErrors[observer.field])
|
||||
}
|
||||
}
|
||||
@ -196,21 +197,16 @@ export default {
|
||||
Object.assign(this.internalFormModelProxy, { [field]: value })
|
||||
this.$emit('input', Object.assign({}, this.internalFormModelProxy))
|
||||
},
|
||||
getUniqueRegistryName (base, count = 0) {
|
||||
if (Object.prototype.hasOwnProperty.call(this.registry, base + (count || ''))) {
|
||||
return this.getUniqueRegistryName(base, count + 1)
|
||||
}
|
||||
return base + (count || '')
|
||||
},
|
||||
register (field, component) {
|
||||
// Don't re-register fields... @todo come up with another way of handling this that doesn't break multi option
|
||||
if (Object.prototype.hasOwnProperty.call(this.registry, field)) {
|
||||
if (this.registry.has(field)) {
|
||||
return false
|
||||
}
|
||||
this.registry[field] = component
|
||||
const hasVModelValue = Object.prototype.hasOwnProperty.call(component.$options.propsData, 'formulateValue')
|
||||
const hasValue = Object.prototype.hasOwnProperty.call(component.$options.propsData, 'value')
|
||||
this.registry.add(field, component)
|
||||
const hasVModelValue = has(component.$options.propsData, 'formulateValue')
|
||||
const hasValue = has(component.$options.propsData, 'value')
|
||||
if (
|
||||
!component.context.isSubField() &&
|
||||
!hasVModelValue &&
|
||||
this.hasInitialValue &&
|
||||
this.initialValues[field]
|
||||
@ -222,6 +218,8 @@ export default {
|
||||
(hasVModelValue || hasValue) &&
|
||||
!shallowEqualObjects(component.internalModelProxy, this.initialValues[field])
|
||||
) {
|
||||
// In this case, the field is v-modeled or has an initial value and the
|
||||
// form has no value or a different value, so use the field value
|
||||
this.setFieldValue(field, component.internalModelProxy)
|
||||
}
|
||||
},
|
||||
@ -246,9 +244,9 @@ export default {
|
||||
})
|
||||
},
|
||||
showErrors () {
|
||||
for (const fieldName in this.registry) {
|
||||
this.registry[fieldName].formShouldShowErrors = true
|
||||
}
|
||||
this.registry.map(input => {
|
||||
input.formShouldShowErrors = true
|
||||
})
|
||||
},
|
||||
getFormValues () {
|
||||
return this.internalFormModelProxy
|
||||
@ -257,13 +255,10 @@ export default {
|
||||
this.$emit('validation', errorObject)
|
||||
},
|
||||
hasValidationErrors () {
|
||||
const resolvers = []
|
||||
for (const fieldName in this.registry) {
|
||||
if (typeof this.registry[fieldName].getValidationErrors === 'function') {
|
||||
resolvers.push(this.registry[fieldName].getValidationErrors())
|
||||
}
|
||||
}
|
||||
return Promise.all(resolvers).then((errorObjects) => {
|
||||
return Promise.all(this.registry.reduce((resolvers, cmp, name) => {
|
||||
resolvers.push(cmp.getValidationErrors())
|
||||
return resolvers
|
||||
}, [])).then((errorObjects) => {
|
||||
return errorObjects.some(item => item.hasErrors)
|
||||
})
|
||||
}
|
||||
|
@ -35,7 +35,8 @@ export default {
|
||||
provide () {
|
||||
return {
|
||||
formulateFormSetter: this.setFieldValue,
|
||||
formulateFormRegister: this.register
|
||||
formulateFormRegister: this.register,
|
||||
isSubField: () => true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@ -63,7 +64,7 @@ export default {
|
||||
))
|
||||
this.context.model = values
|
||||
},
|
||||
register () {
|
||||
register (field, component) {
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -67,7 +67,7 @@
|
||||
|
||||
<script>
|
||||
import context from './libs/context'
|
||||
import { shallowEqualObjects, parseRules, snakeToCamel, arrayify } from './libs/utils'
|
||||
import { shallowEqualObjects, parseRules, snakeToCamel, arrayify, has } from './libs/utils'
|
||||
import nanoid from 'nanoid/non-secure'
|
||||
|
||||
export default {
|
||||
@ -79,7 +79,8 @@ export default {
|
||||
formulateFormRegister: { default: undefined },
|
||||
getFormValues: { default: () => () => ({}) },
|
||||
observeErrors: { default: undefined },
|
||||
removeErrorObserver: { default: undefined }
|
||||
removeErrorObserver: { default: undefined },
|
||||
isSubField: { default: () => () => false }
|
||||
},
|
||||
model: {
|
||||
prop: 'formulateValue',
|
||||
@ -290,9 +291,9 @@ export default {
|
||||
classification = (classification === 'box' && this.options) ? 'group' : classification
|
||||
if (classification === 'box' && this.checked) {
|
||||
return this.value || true
|
||||
} else if (Object.prototype.hasOwnProperty.call(this.$options.propsData, 'value') && classification !== 'box') {
|
||||
} else if (has(this.$options.propsData, 'value') && classification !== 'box') {
|
||||
return this.value
|
||||
} else if (Object.prototype.hasOwnProperty.call(this.$options.propsData, 'formulateValue')) {
|
||||
} else if (has(this.$options.propsData, 'formulateValue')) {
|
||||
return this.formulateValue
|
||||
}
|
||||
return ''
|
||||
|
@ -58,6 +58,7 @@ export default {
|
||||
component,
|
||||
hasLabel,
|
||||
slotComponents,
|
||||
isSubField,
|
||||
...context
|
||||
} = this.context
|
||||
return this.options.map(option => this.groupItemContext(
|
||||
|
@ -37,6 +37,7 @@ export default {
|
||||
validationErrors: this.validationErrors,
|
||||
value: this.value,
|
||||
visibleValidationErrors: this.visibleValidationErrors,
|
||||
isSubField: this.isSubField,
|
||||
...this.typeContext
|
||||
})
|
||||
},
|
||||
|
69
src/libs/registry.js
Normal file
69
src/libs/registry.js
Normal file
@ -0,0 +1,69 @@
|
||||
/**
|
||||
* Component registry with inherent depth to handle complex nesting. This is
|
||||
* important for features such as grouped fields.
|
||||
*/
|
||||
class Registry {
|
||||
/**
|
||||
* Create a new registry of components.
|
||||
*/
|
||||
constructor () {
|
||||
this.registry = new Map()
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an item to the registry.
|
||||
* @param {string|array} key
|
||||
* @param {vue} component
|
||||
*/
|
||||
add (name, component) {
|
||||
this.registry.set(name, component)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an item from the registry.
|
||||
* @param {string} name
|
||||
*/
|
||||
remove (name) {
|
||||
this.registry.delete(name)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the registry has the given key.
|
||||
* @param {string|array} key
|
||||
*/
|
||||
has (key) {
|
||||
return this.registry.has(key)
|
||||
}
|
||||
|
||||
/**
|
||||
* Map over the registry (recursively).
|
||||
* @param {function} callback
|
||||
*/
|
||||
map (callback) {
|
||||
const value = {}
|
||||
this.registry.forEach((component, field) => Object.assign(value, { [field]: callback(component, field) }))
|
||||
return value
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the keys of the registry.
|
||||
*/
|
||||
keys () {
|
||||
return Array.from(this.registry.keys())
|
||||
}
|
||||
|
||||
/**
|
||||
* Reduce the registry.
|
||||
* @param {function} callback
|
||||
*/
|
||||
reduce (callback, accumulator) {
|
||||
this.registry.forEach((component, field) => {
|
||||
accumulator = callback(accumulator, component, field)
|
||||
})
|
||||
return accumulator
|
||||
}
|
||||
}
|
||||
|
||||
export default Registry
|
@ -206,3 +206,18 @@ export function parseLocale (locale) {
|
||||
return options.length ? options : [segment]
|
||||
}, [])
|
||||
}
|
||||
|
||||
/**
|
||||
* Shorthand for Object.prototype.hasOwnProperty.call (space saving)
|
||||
*/
|
||||
export function has (ctx, prop) {
|
||||
return Object.prototype.hasOwnProperty.call(ctx, prop)
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a registry object, map over it recursively entering groups.
|
||||
* @param {Object} registry key => component
|
||||
*/
|
||||
export function mapRegistry (registry) {
|
||||
//
|
||||
}
|
||||
|
@ -1,8 +1,10 @@
|
||||
<template>
|
||||
<div class="formulate-input-group-add-more">
|
||||
<button @click="$emit('add')">
|
||||
Add more
|
||||
</button>
|
||||
<FormulateInput
|
||||
type="button"
|
||||
label="Add more"
|
||||
@click.native="$emit('add')"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -42,7 +42,7 @@ describe('FormulateForm', () => {
|
||||
propsData: { formulateValue: { testinput: 'has initial value' } },
|
||||
slots: { default: '<FormulateInput type="text" name="subinput1" /><FormulateInput type="checkbox" name="subinput2" />' }
|
||||
})
|
||||
expect(Object.keys(wrapper.vm.registry)).toEqual(['subinput1', 'subinput2'])
|
||||
expect(wrapper.vm.registry.keys()).toEqual(['subinput1', 'subinput2'])
|
||||
})
|
||||
|
||||
it('can set a field’s initial value', async () => {
|
||||
|
Loading…
Reference in New Issue
Block a user