Skip to content

Instantly share code, notes, and snippets.

@dkebler
Last active September 23, 2021 16:04
Show Gist options
  • Save dkebler/0e2559126f77cc063578843c834c59cb to your computer and use it in GitHub Desktop.
Save dkebler/0e2559126f77cc063578843c834c59cb to your computer and use it in GitHub Desktop.
Vue dynamic/reactive nested/deep property setting and getting with vuex store
import Vue from 'vue'
import Vuex from 'vuex'
import { setProp, getProp } from 'vue-properties'
import traverse from 'traverse'
import isPlainObj from 'is-plain-object'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
},
getters: {
get: state => {
return args => {
let path, rootPath
if (Array.isArray(args)) [path, rootPath] = args
else path = args
if (path) {
if (typeof path === 'string') path = path.split(/[./]/)
path = rootPath ? (path.unshift(typeof rootPath === 'string' ? rootPath.split(/[./]/) : rootPath)) : path
let value = getProp(state, path)
if (value == null) { // make non-existent prop reactive so later changes will be so
store.commit('set', [path, false, rootPath])
value = getProp(state, path)
}
return value
}
}
},
},
mutations: {
set: (state, args = []) => {
let path, value, object, rootPath
if (Array.isArray(args)) [path, value, rootPath] = args
else ({path, value, object, rootPath} = args)
// if path is object or object passed, traverse and set all leaf props of object into store
if (object || isPlainObj(path)) {
// does not support rootPath
traverse(object || path).forEach(initLeaf)
} else {
// else update a single value
if (path) {
if (typeof path === 'string') path = path.split(/[./]/)
path = rootPath ? (path.unshift(typeof rootPath === 'string' ? rootPath.split(/[./]/) : rootPath)) : path
setProp(state, path, value)
}
}
function initLeaf (rootPath) {
if (this.isLeaf) {
store.commit('set', [this.path, this.node])
}
}
}
}
export default store
import Vue from 'vue'
function setProp (obj, path, value, first = true) {
let props
if (first) {
// covert props/path from dot/slash string or avoid mutation of a passed path/props array
props = typeof path === 'string' ? path.split(/[./]/) : [...path]
} else props = path
const prop = props.shift()
if (!obj[prop]) {
Vue.set(obj, prop, {})
}
if (!props.length) {
if (value && typeof value === 'object' && !Array.isArray(value)) {
obj[prop] = { ...obj[prop], ...value }
} else {
obj[prop] = value
}
return
}
setProp(obj[prop], props, value, false)
}
function getProp (obj, path, first = true) {
let props
if (first) {
props = typeof path === 'string' ? path.split(/[./]/) : [...path]
} else props = path
const prop = props.shift()
if (!obj[prop] || !props.length) {
return obj[prop]
}
return getProp(obj[prop], props, false)
}
function delProp (obj, path, first = true) {
let props
if (first) {
props = typeof path === 'string' ? path.split(/[./]/) : [...path]
} else props = path
const prop = props.shift()
if (!obj[prop]) {
return
}
if (!props.length) {
Vue.delete(obj, prop)
return
}
delProp(obj[prop], props, false)
}
export default {
getProp: getProp,
setProp: setProp,
delProp: delProp
}
export { setProp, getProp, delProp }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment