vuex-map-fields
Enable two-way data binding for form fields saved in a Vuex store.
Install
npm install --save vuex-map-fields
Basic example
The following example component shows the most basic usage, for mapping fields to the Vuex store using two-way data binding with v-model
, without directly modifying the store itself, but using getter and setter functions internally (as it is described in the official Vuex documentation: Two-way Computed Property).
Store
import Vue from 'vue';
import Vuex from 'vuex';
import { getField, updateField } from 'vuex-map-fields';
Vue.use(Vuex);
export default new Vuex.Store({
state: {
fieldA: '',
fieldB: '',
},
getters: {
getField,
},
mutations: {
updateField,
},
});
Component
<template>
<div id="app">
<input v-model="fieldA">
<input v-model="fieldB">
</div>
</template>
<script>
import { mapFields } from 'vuex-map-fields';
export default {
computed: {
...mapFields([
'fieldA',
'fieldB',
]),
},
};
</script>
Nested properties
Oftentimes you want to have nested properties in the Vuex store. vuex-map-fields
supports nested data structures by utilizing the object dot string notation.
Store
import Vue from 'vue';
import Vuex from 'vuex';
import { getField, updateField } from 'vuex-map-fields';
Vue.use(Vuex);
export default new Vuex.Store({
state: {
user: {
firstName: '',
lastName: '',
},
addresses: [
{
town: '',
},
],
},
getters: {
getField,
},
mutations: {
updateField,
},
});
Component
<template>
<div id="app">
<input v-model="firstName">
<input v-model="lastName">
<input v-model="town">
</div>
</template>
<script>
import { mapFields } from 'vuex-map-fields';
export default {
computed: {
...mapFields([
'user.firstName',
'user.lastName',
'addresses[0].town',
]),
},
};
</script>
Rename properties
Sometimes you might want to give your computed properties different names than what you're using in the Vuex store. Renaming properties is made possible by passing an object of fields to the mapFields
function instead of an array.
<template>
<div id="app">
<input v-model="userFirstName">
<input v-model="userLastName">
</div>
</template>
<script>
import { mapFields } from 'vuex-map-fields';
export default {
computed: {
...mapFields({
userFirstName: 'user.firstName',
userLastName: 'user.lastName',
}),
},
};
</script>
Custom getters and mutations
By default vuex-map-fields
is searching for the given properties starting from the root of your state object. Depending on the size of your application, the state object might become quite big and therefore updating the state starting from the root might become a performance issue. To circumvent such problems, it is possible to create a custom mapFields()
function which is configured to access custom mutation and getter functions which don't start from the root of the state object but are accessing a specific point of the state.
Store
import Vue from 'vue';
import Vuex from 'vuex';
import { getField, updateField } from 'vuex-map-fields';
Vue.use(Vuex);
export default new Vuex.Store({
state: {
user: {
firstName: '',
lastName: '',
},
},
getters: {
getUserField(state) {
return getField(state.user);
},
},
mutations: {
updateUserField(state, field) {
updateField(state.user, field);
},
},
});
Component
<template>
<div id="app">
<input v-model="firstName">
<input v-model="lastName">
</div>
</template>
<script>
import { createHelpers } from 'vuex-map-fields';
const { mapFields } = createHelpers({
getterType: 'getUserField',
mutationType: 'updateUserField',
});
export default {
computed: {
...mapFields([
'firstName',
'lastName',
]),
},
};
</script>
Vuex modules
Vuex makes it possible to divide the store into modules.
Store
import Vue from 'vue';
import Vuex from 'vuex';
import { createHelpers } from 'vuex-map-fields';
const { getFooField, updateFooField } = createHelpers({
getterType: 'getFooField',
mutationType: 'updateFooField',
});
Vue.use(Vuex);
export default new Vuex.Store({
modules: {
fooModule: {
state: {
foo: '',
},
getters: {
getFooField,
},
mutations: {
updateFooField,
},
},
},
});
Component
<template>
<div id="app">
<input v-model="foo">
</div>
</template>
<script>
import { createHelpers } from 'vuex-map-fields';
const { mapFields } = createHelpers({
getterType: 'getFooField',
mutationType: 'updateFooField',
});
export default {
computed: {
...mapFields(['foo']),
},
};
</script>
Namespaced Vuex modules
By default, mutations and getters inside modules are registered under the global namespace – but you can mark modules as namespaced
which prevents naming clashes of mutations and getters between modules.
Store
import Vue from 'vue';
import Vuex from 'vuex';
import { getField, updateField } from 'vuex-map-fields';
Vue.use(Vuex);
export default new Vuex.Store({
modules: {
fooModule: {
namespaced: true,
state: {
foo: '',
},
getters: {
getField,
},
mutations: {
updateField,
},
},
},
});
Component
<template>
<div id="app">
<input v-model="foo">
</div>
</template>
<script>
import { createHelpers } from 'vuex-map-fields';
const { mapFields } = createHelpers({
getterType: 'fooModule/getField',
mutationType: 'fooModule/updateField',
});
export default {
computed: {
...mapFields(['foo']),
},
};
</script>
Or you can pass the module namespace string as the first argument of the mapFields()
function.
<template>
<div id="app">
<input v-model="foo">
</div>
</template>
<script>
import { mapFields } from 'vuex-map-fields';
export default {
computed: {
...mapFields('fooModule', ['foo']),
},
};
</script>
Multi-row fields
If you want to build a form which allows the user to enter multiple rows of a specific data type with multiple fields (e.g. multiple addresses) you can use the multi-row field mapping function.
Store
import Vue from 'vue';
import Vuex from 'vuex';
import { getField, updateField } from 'vuex-map-fields';
Vue.use(Vuex);
export default new Vuex.Store({
state: {
addresses: [
{
zip: '12345',
town: 'Foo Town',
},
{
zip: '54321',
town: 'Bar Town',
},
],
},
getters: {
getField,
},
mutations: {
updateField,
},
});
Component
<template>
<div id="app">
<div v-for="address in addresses">
<label>ZIP <input v-model="address.zip"></label>
<label>Town <input v-model="address.town"></label>
</div>
</div>
</template>
<script>
import { mapMultiRowFields } from 'vuex-map-fields';
export default {
computed: {
...mapMultiRowFields(['addresses']),
},
};
</script>
Upgrade from 0.x.x to 1.x.x
Instead of accessing the state directly, since the 1.0.0 release, in order to enable the ability to implement custom getters and mutations, vuex-map-fields
is using a getter function to access the state. This makes it necessary to add a getter function to your Vuex store.
import Vue from 'vue';
import Vuex from 'vuex';
import { getField, updateField } from 'vuex-map-fields';
Vue.use(Vuex);
export default new Vuex.Store({
state: {
fieldA: '',
fieldB: '',
},
getters: {
getField,
},
mutations: {
updateField,
},
});
Become a sponsor and get your logo in this README with a link to your site.
Articles
About
Author
Markus Oberlehner
Website: https://markus.oberlehner.net
Twitter: https://twitter.com/MaOberlehner
PayPal.me: https://paypal.me/maoberlehner
Patreon: https://www.patreon.com/maoberlehner
License
MIT