Skip to content

Commit 5edaa2e

Browse files
committed
feat: nullable properties
1 parent fe39d6a commit 5edaa2e

File tree

7 files changed

+44
-12
lines changed

7 files changed

+44
-12
lines changed

doc/examples/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import validationSections from './validation-sections'
2323
import tuples from './tuples'
2424
import readOnly from './read-only'
2525
import localization from './localization'
26+
import nullable from './nullable'
2627
import resolvedSchema from './_resolved-schema'
2728
import selectFilledDeps from './_select-filled-deps'
2829
import selectFilledHttp from './_select-filled-http'
@@ -54,6 +55,7 @@ const examples = [
5455
tuples,
5556
readOnly,
5657
localization,
58+
nullable,
5759
resolvedSchema,
5860
selectFilledDeps,
5961
selectFilledHttp,

doc/examples/nullable.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
const id = 'nullable'
2+
3+
const title = 'Nullable properties'
4+
5+
const description = `Properties with basic types can support alternative "null" type.
6+
7+
In this case VJsf will set null values when initializing empty properties or when clearing an existing property.`
8+
9+
const schema = {
10+
type: 'object',
11+
properties: {
12+
stringProp: { type: ['string', 'null'], title: `I'm a nullable string` },
13+
dateProp: { type: ['string', 'null'], title: `I'm a clearable date`, format: 'date' },
14+
selectProp: { type: ['string', 'null'], title: `I'm a clearable string from an enum`, enum: ['value 1', 'value 2', null] }
15+
}
16+
}
17+
18+
const model = {}
19+
20+
export default { id, title, description, schema, model }

lib/VJsfNoDeps.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,12 @@ export default {
180180
this.$emit('change', this.value)
181181
},
182182
input(value) {
183-
this.$emit('input', value)
183+
if (value === null || value === undefined || value === '') {
184+
if (this.fullSchema.nullable) this.$emit('input', null)
185+
else this.$emit('input', undefined)
186+
} else {
187+
this.$emit('input', value)
188+
}
184189
},
185190
defaultValue(schema) {
186191
if (schema.type === 'object' && !schema['x-fromUrl'] && !schema['x-fromData'] && !schema.enum) return {}
@@ -221,7 +226,7 @@ export default {
221226

222227
// console.log('Init from schema', this.modelKey)
223228
if (this.fullSchema.readOnly && this.fullOptions.deleteReadOnly) {
224-
return this.$emit('input', undefined)
229+
return this.input(undefined)
225230
}
226231
let model = this.value
227232
if (this.fullSchema.type === 'object' && [undefined, null].includes(model) && !this.isSelectProp) {
@@ -235,7 +240,7 @@ export default {
235240
if (this.fullSchema.type === 'array') {
236241
model = model.filter(item => ![undefined, null].includes(item))
237242
}
238-
this.$emit('input', model)
243+
this.input(model)
239244
}
240245
}
241246
}

lib/mixins/EditableArray.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ export default {
102102
// TODO: rethink parts of reactivity
103103
this.$nextTick(() => this.$refs['roItem-' + i].initFromSchema())
104104
}
105-
this.$emit('input', this.value)
105+
this.input(this.value)
106106
this.$emit('change', this.value)
107107
this.shouldValidate = true
108108
this.editabledArrayProp.currentDialog = null
@@ -160,7 +160,7 @@ export default {
160160
class: { 'vjsf-array-delete-button': true },
161161
on: { click: () => {
162162
const value = this.value.filter(i => i !== item)
163-
this.$emit('input', value)
163+
this.input(value)
164164
this.$emit('change', value)
165165
this.shouldValidate = true
166166
header.componentInstance.validate()
@@ -174,7 +174,7 @@ export default {
174174
class: 'row',
175175
style: 'cursor: move;',
176176
on: { input: (value) => {
177-
this.$emit('input', value)
177+
this.input(value)
178178
this.$emit('change', value)
179179
this.shouldValidate = true
180180
this.$nextTick(() => {

lib/mixins/ObjectContainer.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export default {
3838
this.showCurrentOneOf = true
3939
if (!this.currentOneOf) this.$set(this.subModels, 'currentOneOf', {})
4040
else this.fixProperties()
41-
this.$emit('input', this.value)
41+
this.input(this.value)
4242
this.$emit('change', this.value)
4343
})
4444
},
@@ -117,7 +117,7 @@ export default {
117117
if (schema.default !== undefined) value = JSON.parse(JSON.stringify(schema.default))
118118
if (value !== undefined && value !== null) {
119119
this.$set(wrapper, modelKey, value)
120-
this.$emit('input', this.value)
120+
this.input(this.value)
121121
}
122122
}
123123
return h('v-jsf', {
@@ -136,8 +136,8 @@ export default {
136136
on: {
137137
error: e => this.$emit('error', e),
138138
input: v => {
139-
if (v === null || v === undefined || v === '') {
140-
// set empty value instead of deleting key in the special case of he parts
139+
if (v === undefined) {
140+
// set empty value instead of deleting key in the special case of the parts
141141
// of a tuple (except the last part), otherwise we move last parts of the array to the beginning of the array
142142
if (Array.isArray(wrapper) && parseInt(modelKey) < (wrapper.length - 1)) {
143143
this.$set(wrapper, modelKey, v)

lib/mixins/SelectProperty.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ export default {
139139
updateSelectItems() {
140140
const selectItems = selectUtils.getSelectItems(this.rawSelectItems, this.fullSchema, this.itemKey, this.itemIcon)
141141
if (this.display === 'list') {
142-
this.$emit('input', selectUtils.fillList(this.fullSchema, this.value, selectItems, this.itemKey))
142+
this.input(selectUtils.fillList(this.fullSchema, this.value, selectItems, this.itemKey))
143143
}
144144

145145
selectUtils.fillSelectItems(this.fullSchema, this.value, selectItems, this.itemKey, this.returnObject)
@@ -170,7 +170,7 @@ export default {
170170
const value = item[this.itemKey]
171171
const on = {
172172
change: (inputValue) => {
173-
this.$emit('input', inputValue)
173+
this.input(inputValue)
174174
this.$emit('change', inputValue)
175175
}
176176
}

lib/utils/schema.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ schemaUtils.prepareFullSchema = (schema, value) => {
2121
if (fullSchema.pattern) fullSchema.patternRegexp = new RegExp(fullSchema.pattern)
2222

2323
if (!fullSchema.type && fullSchema.properties) fullSchema.type = 'object'
24+
if (Array.isArray(fullSchema.type)) {
25+
fullSchema.nullable = fullSchema.type.includes('null')
26+
fullSchema.type = fullSchema.type.find(t => t !== 'null')
27+
if (fullSchema.nullable && fullSchema.enum) fullSchema.enum = fullSchema.enum.filter(v => v !== null)
28+
}
2429
if (fullSchema.type !== 'object') return fullSchema
2530

2631
// Properties as array for easier loops

0 commit comments

Comments
 (0)