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,
typeContext: typeContext,
elementAttributes: elementAttributes,
logicalLabelPosition: logicalLabelPosition
logicalLabelPosition: logicalLabelPosition,
isVmodeled: isVmodeled
};
/**
@ -237,6 +238,16 @@ function nameOrFallback () {
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,
* value, and id.
@ -272,19 +283,21 @@ function defineModel (context) {
* Get the value from a model.
**/
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 []
}
if (!this.formulateValue) {
if (!this[model]) {
return ''
}
return this.formulateValue
return this[model]
}
/**
* Set the value from a model.
**/
function modelSetter (value) {
this.internalModelProxy = value;
this.$emit('input', value);
if (this.context.name && typeof this.formulateFormSetter === 'function') {
this.formulateFormSetter(this.context.name, value);
@ -315,7 +328,7 @@ var script = {
},
/* eslint-disable */
formulateValue: {
default: undefined
default: ''
},
value: {
default: false
@ -353,7 +366,8 @@ var script = {
data: function data () {
return {
defaultId: nanoid(9),
localAttributes: {}
localAttributes: {},
internalModelProxy: this.formulateValue
}
},
computed: Object.assign({}, context,
@ -370,19 +384,24 @@ var script = {
this.updateLocalAttributes(value);
},
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 () {
if (this.formulateFormRegister && typeof this.formulateFormRegister === 'function') {
this.formulateFormRegister(this.name, this);
this.formulateFormRegister(this.nameOrFallback, this);
}
this.updateLocalAttributes(this.$attrs);
},
mounted: function mounted () {
if (this.debug) {
console.log('MOUNTED:' + this.$options.name + ':' + this.type);
}
},
methods: {
updateLocalAttributes: function updateLocalAttributes (value) {
if (!shallowEqualObjects(value, this.localAttributes)) {
@ -584,13 +603,6 @@ __vue_render__._withStripped = true;
);
//
//
//
//
//
//
//
//
var script$1 = {
provide: function provide () {
@ -627,6 +639,36 @@ var script$1 = {
set: function set (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: {
@ -637,6 +679,11 @@ var script$1 = {
},
register: function register (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,
typeContext: typeContext,
elementAttributes: elementAttributes,
logicalLabelPosition: logicalLabelPosition
logicalLabelPosition: logicalLabelPosition,
isVmodeled: isVmodeled
};
/**
@ -240,6 +241,16 @@ var Formulate = (function (exports, isPlainObject, nanoid) {
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,
* value, and id.
@ -275,19 +286,21 @@ var Formulate = (function (exports, isPlainObject, nanoid) {
* Get the value from a model.
**/
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 []
}
if (!this.formulateValue) {
if (!this[model]) {
return ''
}
return this.formulateValue
return this[model]
}
/**
* Set the value from a model.
**/
function modelSetter (value) {
this.internalModelProxy = value;
this.$emit('input', value);
if (this.context.name && typeof this.formulateFormSetter === 'function') {
this.formulateFormSetter(this.context.name, value);
@ -318,7 +331,7 @@ var Formulate = (function (exports, isPlainObject, nanoid) {
},
/* eslint-disable */
formulateValue: {
default: undefined
default: ''
},
value: {
default: false
@ -356,7 +369,8 @@ var Formulate = (function (exports, isPlainObject, nanoid) {
data: function data () {
return {
defaultId: nanoid(9),
localAttributes: {}
localAttributes: {},
internalModelProxy: this.formulateValue
}
},
computed: Object.assign({}, context,
@ -373,19 +387,24 @@ var Formulate = (function (exports, isPlainObject, nanoid) {
this.updateLocalAttributes(value);
},
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 () {
if (this.formulateFormRegister && typeof this.formulateFormRegister === 'function') {
this.formulateFormRegister(this.name, this);
this.formulateFormRegister(this.nameOrFallback, this);
}
this.updateLocalAttributes(this.$attrs);
},
mounted: function mounted () {
if (this.debug) {
console.log('MOUNTED:' + this.$options.name + ':' + this.type);
}
},
methods: {
updateLocalAttributes: function updateLocalAttributes (value) {
if (!shallowEqualObjects(value, this.localAttributes)) {
@ -587,13 +606,6 @@ var Formulate = (function (exports, isPlainObject, nanoid) {
);
//
//
//
//
//
//
//
//
var script$1 = {
provide: function provide () {
@ -630,6 +642,36 @@ var Formulate = (function (exports, isPlainObject, nanoid) {
set: function set (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: {
@ -640,6 +682,11 @@ var Formulate = (function (exports, isPlainObject, nanoid) {
},
register: function register (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,
typeContext: typeContext,
elementAttributes: elementAttributes,
logicalLabelPosition: logicalLabelPosition
logicalLabelPosition: logicalLabelPosition,
isVmodeled: isVmodeled
};
/**
@ -243,6 +244,16 @@
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,
* value, and id.
@ -278,19 +289,21 @@
* Get the value from a model.
**/
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 []
}
if (!this.formulateValue) {
if (!this[model]) {
return ''
}
return this.formulateValue
return this[model]
}
/**
* Set the value from a model.
**/
function modelSetter (value) {
this.internalModelProxy = value;
this.$emit('input', value);
if (this.context.name && typeof this.formulateFormSetter === 'function') {
this.formulateFormSetter(this.context.name, value);
@ -321,7 +334,7 @@
},
/* eslint-disable */
formulateValue: {
default: undefined
default: ''
},
value: {
default: false
@ -359,7 +372,8 @@
data: function data () {
return {
defaultId: nanoid(9),
localAttributes: {}
localAttributes: {},
internalModelProxy: this.formulateValue
}
},
computed: Object.assign({}, context,
@ -376,19 +390,24 @@
this.updateLocalAttributes(value);
},
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 () {
if (this.formulateFormRegister && typeof this.formulateFormRegister === 'function') {
this.formulateFormRegister(this.name, this);
this.formulateFormRegister(this.nameOrFallback, this);
}
this.updateLocalAttributes(this.$attrs);
},
mounted: function mounted () {
if (this.debug) {
console.log('MOUNTED:' + this.$options.name + ':' + this.type);
}
},
methods: {
updateLocalAttributes: function updateLocalAttributes (value) {
if (!shallowEqualObjects(value, this.localAttributes)) {
@ -590,13 +609,6 @@
);
//
//
//
//
//
//
//
//
var script$1 = {
provide: function provide () {
@ -633,6 +645,36 @@
set: function set (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: {
@ -643,6 +685,11 @@
},
register: function register (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>
<script>
import { shallowEqualObjects } from './libs/utils'
export default {
provide () {
return {
@ -42,6 +44,36 @@ export default {
set (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: {
@ -50,6 +82,11 @@ export default {
},
register (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 */
formulateValue: {
default: undefined
default: ''
},
value: {
default: false
@ -107,7 +107,8 @@ export default {
data () {
return {
defaultId: nanoid(9),
localAttributes: {}
localAttributes: {},
internalModelProxy: this.formulateValue
}
},
computed: {
@ -126,19 +127,24 @@ export default {
this.updateLocalAttributes(value)
},
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 () {
if (this.formulateFormRegister && typeof this.formulateFormRegister === 'function') {
this.formulateFormRegister(this.name, this)
this.formulateFormRegister(this.nameOrFallback, this)
}
this.updateLocalAttributes(this.$attrs)
},
mounted () {
if (this.debug) {
console.log('MOUNTED:' + this.$options.name + ':' + this.type)
}
},
methods: {
updateLocalAttributes (value) {
if (!shallowEqualObjects(value, this.localAttributes)) {

View File

@ -27,7 +27,8 @@ export default {
nameOrFallback,
typeContext,
elementAttributes,
logicalLabelPosition
logicalLabelPosition,
isVmodeled
}
/**
@ -98,6 +99,16 @@ function nameOrFallback () {
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,
* value, and id.
@ -133,19 +144,21 @@ function defineModel (context) {
* Get the value from a model.
**/
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 []
}
if (!this.formulateValue) {
if (!this[model]) {
return ''
}
return this.formulateValue
return this[model]
}
/**
* Set the value from a model.
**/
function modelSetter (value) {
this.internalModelProxy = value
this.$emit('input', value)
if (this.context.name && typeof this.formulateFormSetter === 'function') {
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: "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')
})