1
0
mirror of synced 2024-11-25 23:06:02 +03:00

Adds form level data binding

This commit is contained in:
Justin Schroeder 2019-10-29 23:33:31 -04:00
parent 942ff41ad9
commit 7809cdd2e3
7 changed files with 271 additions and 69 deletions

85
dist/formulate.esm.js vendored
View File

@ -164,7 +164,8 @@ var context = {
nameOrFallback: nameOrFallback, nameOrFallback: nameOrFallback,
typeContext: typeContext, typeContext: typeContext,
elementAttributes: elementAttributes, elementAttributes: elementAttributes,
logicalLabelPosition: logicalLabelPosition logicalLabelPosition: logicalLabelPosition,
isVmodeled: isVmodeled
}; };
/** /**
@ -237,6 +238,16 @@ function nameOrFallback () {
return this.name return this.name
} }
/**
* Determines if this formulate element is v-modeled or not.
*/
function isVmodeled () {
return !!(this.$options.propsData.hasOwnProperty('formulateValue') &&
this._events &&
Array.isArray(this._events.input) &&
this._events.input.length)
}
/** /**
* Given an object or array of options, create an array of objects with label, * Given an object or array of options, create an array of objects with label,
* value, and id. * value, and id.
@ -272,19 +283,21 @@ function defineModel (context) {
* Get the value from a model. * Get the value from a model.
**/ **/
function modelGetter () { function modelGetter () {
if (this.type === 'checkbox' && !Array.isArray(this.formulateValue) && this.options) { var model = this.isVmodeled ? 'formulateValue' : 'internalModelProxy';
if (this.type === 'checkbox' && !Array.isArray(this[model]) && this.options) {
return [] return []
} }
if (!this.formulateValue) { if (!this[model]) {
return '' return ''
} }
return this.formulateValue return this[model]
} }
/** /**
* Set the value from a model. * Set the value from a model.
**/ **/
function modelSetter (value) { function modelSetter (value) {
this.internalModelProxy = value;
this.$emit('input', value); this.$emit('input', value);
if (this.context.name && typeof this.formulateFormSetter === 'function') { if (this.context.name && typeof this.formulateFormSetter === 'function') {
this.formulateFormSetter(this.context.name, value); this.formulateFormSetter(this.context.name, value);
@ -315,7 +328,7 @@ var script = {
}, },
/* eslint-disable */ /* eslint-disable */
formulateValue: { formulateValue: {
default: undefined default: ''
}, },
value: { value: {
default: false default: false
@ -353,7 +366,8 @@ var script = {
data: function data () { data: function data () {
return { return {
defaultId: nanoid(9), defaultId: nanoid(9),
localAttributes: {} localAttributes: {},
internalModelProxy: this.formulateValue
} }
}, },
computed: Object.assign({}, context, computed: Object.assign({}, context,
@ -370,19 +384,24 @@ var script = {
this.updateLocalAttributes(value); this.updateLocalAttributes(value);
}, },
deep: true deep: true
},
internalModelProxy: function internalModelProxy (newValue, oldValue) {
if (!this.isVmodeled && !shallowEqualObjects(newValue, oldValue)) {
this.context.model = newValue;
}
},
formulateValue: function formulateValue (newValue, oldValue) {
if (this.isVmodeled && !shallowEqualObjects(newValue, oldValue)) {
this.context.model = newValue;
}
} }
}, },
created: function created () { created: function created () {
if (this.formulateFormRegister && typeof this.formulateFormRegister === 'function') { if (this.formulateFormRegister && typeof this.formulateFormRegister === 'function') {
this.formulateFormRegister(this.name, this); this.formulateFormRegister(this.nameOrFallback, this);
} }
this.updateLocalAttributes(this.$attrs); this.updateLocalAttributes(this.$attrs);
}, },
mounted: function mounted () {
if (this.debug) {
console.log('MOUNTED:' + this.$options.name + ':' + this.type);
}
},
methods: { methods: {
updateLocalAttributes: function updateLocalAttributes (value) { updateLocalAttributes: function updateLocalAttributes (value) {
if (!shallowEqualObjects(value, this.localAttributes)) { if (!shallowEqualObjects(value, this.localAttributes)) {
@ -584,13 +603,6 @@ __vue_render__._withStripped = true;
); );
// //
//
//
//
//
//
//
//
var script$1 = { var script$1 = {
provide: function provide () { provide: function provide () {
@ -627,6 +639,36 @@ var script$1 = {
set: function set (value) { set: function set (value) {
this.$emit('input', value); this.$emit('input', value);
} }
},
hasFormulateValue: function hasFormulateValue () {
return this.formulateValue && typeof this.formulateValue === 'object'
},
isVmodeled: function isVmodeled () {
return !!(this.$options.propsData.hasOwnProperty('formulateValue') &&
this._events &&
Array.isArray(this._events.input) &&
this._events.input.length)
}
},
watch: {
formulateValue: {
handler: function handler (newValue, oldValue) {
if (this.isVmodeled &&
newValue &&
typeof newValue === 'object'
) {
for (var field in newValue) {
if (this.registry.hasOwnProperty(field) && !shallowEqualObjects(newValue[field], this.registry[field].internalModelProxy)) {
// If the value of the formulateValue changed (probably as a prop)
// and it doesn't match the internal proxied value of the registered
// component, we set it explicitly. Its important we check the
// model proxy here since the model itself is not fully synchronous.
this.registry[field].context.model = newValue[field];
}
}
}
},
deep: true
} }
}, },
methods: { methods: {
@ -637,6 +679,11 @@ var script$1 = {
}, },
register: function register (field, component) { register: function register (field, component) {
this.registry[field] = component; this.registry[field] = component;
if (!component.$options.propsData.hasOwnProperty('formulateValue') && this.hasFormulateValue && this.formulateValue[field]) {
// In the case that the form is carrying an initial value and the
// element is not, set it directly.
component.context.model = this.formulateValue[field];
}
} }
} }
}; };

85
dist/formulate.min.js vendored
View File

@ -167,7 +167,8 @@ var Formulate = (function (exports, isPlainObject, nanoid) {
nameOrFallback: nameOrFallback, nameOrFallback: nameOrFallback,
typeContext: typeContext, typeContext: typeContext,
elementAttributes: elementAttributes, elementAttributes: elementAttributes,
logicalLabelPosition: logicalLabelPosition logicalLabelPosition: logicalLabelPosition,
isVmodeled: isVmodeled
}; };
/** /**
@ -240,6 +241,16 @@ var Formulate = (function (exports, isPlainObject, nanoid) {
return this.name return this.name
} }
/**
* Determines if this formulate element is v-modeled or not.
*/
function isVmodeled () {
return !!(this.$options.propsData.hasOwnProperty('formulateValue') &&
this._events &&
Array.isArray(this._events.input) &&
this._events.input.length)
}
/** /**
* Given an object or array of options, create an array of objects with label, * Given an object or array of options, create an array of objects with label,
* value, and id. * value, and id.
@ -275,19 +286,21 @@ var Formulate = (function (exports, isPlainObject, nanoid) {
* Get the value from a model. * Get the value from a model.
**/ **/
function modelGetter () { function modelGetter () {
if (this.type === 'checkbox' && !Array.isArray(this.formulateValue) && this.options) { var model = this.isVmodeled ? 'formulateValue' : 'internalModelProxy';
if (this.type === 'checkbox' && !Array.isArray(this[model]) && this.options) {
return [] return []
} }
if (!this.formulateValue) { if (!this[model]) {
return '' return ''
} }
return this.formulateValue return this[model]
} }
/** /**
* Set the value from a model. * Set the value from a model.
**/ **/
function modelSetter (value) { function modelSetter (value) {
this.internalModelProxy = value;
this.$emit('input', value); this.$emit('input', value);
if (this.context.name && typeof this.formulateFormSetter === 'function') { if (this.context.name && typeof this.formulateFormSetter === 'function') {
this.formulateFormSetter(this.context.name, value); this.formulateFormSetter(this.context.name, value);
@ -318,7 +331,7 @@ var Formulate = (function (exports, isPlainObject, nanoid) {
}, },
/* eslint-disable */ /* eslint-disable */
formulateValue: { formulateValue: {
default: undefined default: ''
}, },
value: { value: {
default: false default: false
@ -356,7 +369,8 @@ var Formulate = (function (exports, isPlainObject, nanoid) {
data: function data () { data: function data () {
return { return {
defaultId: nanoid(9), defaultId: nanoid(9),
localAttributes: {} localAttributes: {},
internalModelProxy: this.formulateValue
} }
}, },
computed: Object.assign({}, context, computed: Object.assign({}, context,
@ -373,19 +387,24 @@ var Formulate = (function (exports, isPlainObject, nanoid) {
this.updateLocalAttributes(value); this.updateLocalAttributes(value);
}, },
deep: true deep: true
},
internalModelProxy: function internalModelProxy (newValue, oldValue) {
if (!this.isVmodeled && !shallowEqualObjects(newValue, oldValue)) {
this.context.model = newValue;
}
},
formulateValue: function formulateValue (newValue, oldValue) {
if (this.isVmodeled && !shallowEqualObjects(newValue, oldValue)) {
this.context.model = newValue;
}
} }
}, },
created: function created () { created: function created () {
if (this.formulateFormRegister && typeof this.formulateFormRegister === 'function') { if (this.formulateFormRegister && typeof this.formulateFormRegister === 'function') {
this.formulateFormRegister(this.name, this); this.formulateFormRegister(this.nameOrFallback, this);
} }
this.updateLocalAttributes(this.$attrs); this.updateLocalAttributes(this.$attrs);
}, },
mounted: function mounted () {
if (this.debug) {
console.log('MOUNTED:' + this.$options.name + ':' + this.type);
}
},
methods: { methods: {
updateLocalAttributes: function updateLocalAttributes (value) { updateLocalAttributes: function updateLocalAttributes (value) {
if (!shallowEqualObjects(value, this.localAttributes)) { if (!shallowEqualObjects(value, this.localAttributes)) {
@ -587,13 +606,6 @@ var Formulate = (function (exports, isPlainObject, nanoid) {
); );
// //
//
//
//
//
//
//
//
var script$1 = { var script$1 = {
provide: function provide () { provide: function provide () {
@ -630,6 +642,36 @@ var Formulate = (function (exports, isPlainObject, nanoid) {
set: function set (value) { set: function set (value) {
this.$emit('input', value); this.$emit('input', value);
} }
},
hasFormulateValue: function hasFormulateValue () {
return this.formulateValue && typeof this.formulateValue === 'object'
},
isVmodeled: function isVmodeled () {
return !!(this.$options.propsData.hasOwnProperty('formulateValue') &&
this._events &&
Array.isArray(this._events.input) &&
this._events.input.length)
}
},
watch: {
formulateValue: {
handler: function handler (newValue, oldValue) {
if (this.isVmodeled &&
newValue &&
typeof newValue === 'object'
) {
for (var field in newValue) {
if (this.registry.hasOwnProperty(field) && !shallowEqualObjects(newValue[field], this.registry[field].internalModelProxy)) {
// If the value of the formulateValue changed (probably as a prop)
// and it doesn't match the internal proxied value of the registered
// component, we set it explicitly. Its important we check the
// model proxy here since the model itself is not fully synchronous.
this.registry[field].context.model = newValue[field];
}
}
}
},
deep: true
} }
}, },
methods: { methods: {
@ -640,6 +682,11 @@ var Formulate = (function (exports, isPlainObject, nanoid) {
}, },
register: function register (field, component) { register: function register (field, component) {
this.registry[field] = component; this.registry[field] = component;
if (!component.$options.propsData.hasOwnProperty('formulateValue') && this.hasFormulateValue && this.formulateValue[field]) {
// In the case that the form is carrying an initial value and the
// element is not, set it directly.
component.context.model = this.formulateValue[field];
}
} }
} }
}; };

85
dist/formulate.umd.js vendored
View File

@ -170,7 +170,8 @@
nameOrFallback: nameOrFallback, nameOrFallback: nameOrFallback,
typeContext: typeContext, typeContext: typeContext,
elementAttributes: elementAttributes, elementAttributes: elementAttributes,
logicalLabelPosition: logicalLabelPosition logicalLabelPosition: logicalLabelPosition,
isVmodeled: isVmodeled
}; };
/** /**
@ -243,6 +244,16 @@
return this.name return this.name
} }
/**
* Determines if this formulate element is v-modeled or not.
*/
function isVmodeled () {
return !!(this.$options.propsData.hasOwnProperty('formulateValue') &&
this._events &&
Array.isArray(this._events.input) &&
this._events.input.length)
}
/** /**
* Given an object or array of options, create an array of objects with label, * Given an object or array of options, create an array of objects with label,
* value, and id. * value, and id.
@ -278,19 +289,21 @@
* Get the value from a model. * Get the value from a model.
**/ **/
function modelGetter () { function modelGetter () {
if (this.type === 'checkbox' && !Array.isArray(this.formulateValue) && this.options) { var model = this.isVmodeled ? 'formulateValue' : 'internalModelProxy';
if (this.type === 'checkbox' && !Array.isArray(this[model]) && this.options) {
return [] return []
} }
if (!this.formulateValue) { if (!this[model]) {
return '' return ''
} }
return this.formulateValue return this[model]
} }
/** /**
* Set the value from a model. * Set the value from a model.
**/ **/
function modelSetter (value) { function modelSetter (value) {
this.internalModelProxy = value;
this.$emit('input', value); this.$emit('input', value);
if (this.context.name && typeof this.formulateFormSetter === 'function') { if (this.context.name && typeof this.formulateFormSetter === 'function') {
this.formulateFormSetter(this.context.name, value); this.formulateFormSetter(this.context.name, value);
@ -321,7 +334,7 @@
}, },
/* eslint-disable */ /* eslint-disable */
formulateValue: { formulateValue: {
default: undefined default: ''
}, },
value: { value: {
default: false default: false
@ -359,7 +372,8 @@
data: function data () { data: function data () {
return { return {
defaultId: nanoid(9), defaultId: nanoid(9),
localAttributes: {} localAttributes: {},
internalModelProxy: this.formulateValue
} }
}, },
computed: Object.assign({}, context, computed: Object.assign({}, context,
@ -376,19 +390,24 @@
this.updateLocalAttributes(value); this.updateLocalAttributes(value);
}, },
deep: true deep: true
},
internalModelProxy: function internalModelProxy (newValue, oldValue) {
if (!this.isVmodeled && !shallowEqualObjects(newValue, oldValue)) {
this.context.model = newValue;
}
},
formulateValue: function formulateValue (newValue, oldValue) {
if (this.isVmodeled && !shallowEqualObjects(newValue, oldValue)) {
this.context.model = newValue;
}
} }
}, },
created: function created () { created: function created () {
if (this.formulateFormRegister && typeof this.formulateFormRegister === 'function') { if (this.formulateFormRegister && typeof this.formulateFormRegister === 'function') {
this.formulateFormRegister(this.name, this); this.formulateFormRegister(this.nameOrFallback, this);
} }
this.updateLocalAttributes(this.$attrs); this.updateLocalAttributes(this.$attrs);
}, },
mounted: function mounted () {
if (this.debug) {
console.log('MOUNTED:' + this.$options.name + ':' + this.type);
}
},
methods: { methods: {
updateLocalAttributes: function updateLocalAttributes (value) { updateLocalAttributes: function updateLocalAttributes (value) {
if (!shallowEqualObjects(value, this.localAttributes)) { if (!shallowEqualObjects(value, this.localAttributes)) {
@ -590,13 +609,6 @@
); );
// //
//
//
//
//
//
//
//
var script$1 = { var script$1 = {
provide: function provide () { provide: function provide () {
@ -633,6 +645,36 @@
set: function set (value) { set: function set (value) {
this.$emit('input', value); this.$emit('input', value);
} }
},
hasFormulateValue: function hasFormulateValue () {
return this.formulateValue && typeof this.formulateValue === 'object'
},
isVmodeled: function isVmodeled () {
return !!(this.$options.propsData.hasOwnProperty('formulateValue') &&
this._events &&
Array.isArray(this._events.input) &&
this._events.input.length)
}
},
watch: {
formulateValue: {
handler: function handler (newValue, oldValue) {
if (this.isVmodeled &&
newValue &&
typeof newValue === 'object'
) {
for (var field in newValue) {
if (this.registry.hasOwnProperty(field) && !shallowEqualObjects(newValue[field], this.registry[field].internalModelProxy)) {
// If the value of the formulateValue changed (probably as a prop)
// and it doesn't match the internal proxied value of the registered
// component, we set it explicitly. Its important we check the
// model proxy here since the model itself is not fully synchronous.
this.registry[field].context.model = newValue[field];
}
}
}
},
deep: true
} }
}, },
methods: { methods: {
@ -643,6 +685,11 @@
}, },
register: function register (field, component) { register: function register (field, component) {
this.registry[field] = component; this.registry[field] = component;
if (!component.$options.propsData.hasOwnProperty('formulateValue') && this.hasFormulateValue && this.formulateValue[field]) {
// In the case that the form is carrying an initial value and the
// element is not, set it directly.
component.context.model = this.formulateValue[field];
}
} }
} }
}; };

View File

@ -7,6 +7,8 @@
</template> </template>
<script> <script>
import { shallowEqualObjects } from './libs/utils'
export default { export default {
provide () { provide () {
return { return {
@ -42,6 +44,36 @@ export default {
set (value) { set (value) {
this.$emit('input', value) this.$emit('input', value)
} }
},
hasFormulateValue () {
return this.formulateValue && typeof this.formulateValue === 'object'
},
isVmodeled () {
return !!(this.$options.propsData.hasOwnProperty('formulateValue') &&
this._events &&
Array.isArray(this._events.input) &&
this._events.input.length)
}
},
watch: {
formulateValue: {
handler (newValue, oldValue) {
if (this.isVmodeled &&
newValue &&
typeof newValue === 'object'
) {
for (const field in newValue) {
if (this.registry.hasOwnProperty(field) && !shallowEqualObjects(newValue[field], this.registry[field].internalModelProxy)) {
// If the value of the formulateValue changed (probably as a prop)
// and it doesn't match the internal proxied value of the registered
// component, we set it explicitly. Its important we check the
// model proxy here since the model itself is not fully synchronous.
this.registry[field].context.model = newValue[field]
}
}
}
},
deep: true
} }
}, },
methods: { methods: {
@ -50,6 +82,11 @@ export default {
}, },
register (field, component) { register (field, component) {
this.registry[field] = component this.registry[field] = component
if (!component.$options.propsData.hasOwnProperty('formulateValue') && this.hasFormulateValue && this.formulateValue[field]) {
// In the case that the form is carrying an initial value and the
// element is not, set it directly.
component.context.model = this.formulateValue[field]
}
} }
} }
} }

View File

@ -69,7 +69,7 @@ export default {
}, },
/* eslint-disable */ /* eslint-disable */
formulateValue: { formulateValue: {
default: undefined default: ''
}, },
value: { value: {
default: false default: false
@ -107,7 +107,8 @@ export default {
data () { data () {
return { return {
defaultId: nanoid(9), defaultId: nanoid(9),
localAttributes: {} localAttributes: {},
internalModelProxy: this.formulateValue
} }
}, },
computed: { computed: {
@ -126,19 +127,24 @@ export default {
this.updateLocalAttributes(value) this.updateLocalAttributes(value)
}, },
deep: true deep: true
},
internalModelProxy (newValue, oldValue) {
if (!this.isVmodeled && !shallowEqualObjects(newValue, oldValue)) {
this.context.model = newValue
}
},
formulateValue (newValue, oldValue) {
if (this.isVmodeled && !shallowEqualObjects(newValue, oldValue)) {
this.context.model = newValue
}
} }
}, },
created () { created () {
if (this.formulateFormRegister && typeof this.formulateFormRegister === 'function') { if (this.formulateFormRegister && typeof this.formulateFormRegister === 'function') {
this.formulateFormRegister(this.name, this) this.formulateFormRegister(this.nameOrFallback, this)
} }
this.updateLocalAttributes(this.$attrs) this.updateLocalAttributes(this.$attrs)
}, },
mounted () {
if (this.debug) {
console.log('MOUNTED:' + this.$options.name + ':' + this.type)
}
},
methods: { methods: {
updateLocalAttributes (value) { updateLocalAttributes (value) {
if (!shallowEqualObjects(value, this.localAttributes)) { if (!shallowEqualObjects(value, this.localAttributes)) {

View File

@ -27,7 +27,8 @@ export default {
nameOrFallback, nameOrFallback,
typeContext, typeContext,
elementAttributes, elementAttributes,
logicalLabelPosition logicalLabelPosition,
isVmodeled
} }
/** /**
@ -98,6 +99,16 @@ function nameOrFallback () {
return this.name return this.name
} }
/**
* Determines if this formulate element is v-modeled or not.
*/
function isVmodeled () {
return !!(this.$options.propsData.hasOwnProperty('formulateValue') &&
this._events &&
Array.isArray(this._events.input) &&
this._events.input.length)
}
/** /**
* Given an object or array of options, create an array of objects with label, * Given an object or array of options, create an array of objects with label,
* value, and id. * value, and id.
@ -133,19 +144,21 @@ function defineModel (context) {
* Get the value from a model. * Get the value from a model.
**/ **/
function modelGetter () { function modelGetter () {
if (this.type === 'checkbox' && !Array.isArray(this.formulateValue) && this.options) { const model = this.isVmodeled ? 'formulateValue' : 'internalModelProxy'
if (this.type === 'checkbox' && !Array.isArray(this[model]) && this.options) {
return [] return []
} }
if (!this.formulateValue) { if (!this[model]) {
return '' return ''
} }
return this.formulateValue return this[model]
} }
/** /**
* Set the value from a model. * Set the value from a model.
**/ **/
function modelSetter (value) { function modelSetter (value) {
this.internalModelProxy = value
this.$emit('input', value) this.$emit('input', value)
if (this.context.name && typeof this.formulateFormSetter === 'function') { if (this.context.name && typeof this.formulateFormSetter === 'function') {
this.formulateFormSetter(this.context.name, value) this.formulateFormSetter(this.context.name, value)

View File

@ -141,3 +141,8 @@ test('test that inputs that arent updated dont re-context themselves', () => {
expect(wrapper.find({ref: "first"}).vm.context === firstContext).toBe(false) expect(wrapper.find({ref: "first"}).vm.context === firstContext).toBe(false)
expect(wrapper.find({ref: "second"}).vm.context === secondContext).toBe(true) expect(wrapper.find({ref: "second"}).vm.context === secondContext).toBe(true)
}) })
test('test that inputs contain their v-model value as the initial input', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'text', formulateValue: 'initial val' } })
expect(wrapper.find('input').element.value).toBe('initial val')
})