vuex-inject v1.1.1
vuex-inject
Decouple Vuex modules from each other.
Installation
$ npm install vuex-injectUsage
import modules from 'src/store/modules';
import createInjectPlugin from 'vuex-inject';
const store = new Vuex.Store({
// pass 'modules' to the plugin instead of store
plugins: [
createInjectPlugin({
modules,
inject: {
modA: {
getters: { modBList: 'modB/list' },
},
},
}),
],
// modules,
});Example
Let's have a look at the following simple vuex modules:
// store/modules/app.js
export default {
namespaced: true,
getters: {
version: () => '0.1',
},
};
// store/modules/rest.js
export default {
namespaced: true,
getters: {
baseRestUrl: (state, getters, rootState, rootGetters) => {
let appVersion = rootGetters['app/version'];
return `/api/${appVersion}`;
},
},
};
// store/index.js
// ...
export default function initStore (params) {
return Vuex.store({
modules: { app, rest },
...params,
});
};rest module is coupled with app module: we can't use it or
test it separately.
Let's rewrite rest module with the plugin:
// store/modules/rest.js
export default {
namespaced: true,
getters: {
baseRestUrl: (state, getters) => {
return `/api/${getters.$appVersion}`;
},
// $ here doesn't have any special meaning
$appVersion: () => '',
},
};
// store/index.js
// ...
export default function initStore (params, injectParams) {
return new Vuex.Store({
plugins: [
createInjectPlugin({
modules: { app, rest },
inject: {
rest: {
getters: { $appVersion: 'app/version' },
},
},
...injectParams,
}),
],
...params,
});
};Now rest module knows nothing about app module and it can be tested
separately but in a more "integrated" way (through the store) than unit
tests for each function in a module config:
import initStore from './store';
describe('rest', () => {
it('should have baseRestUrl', () => {
let store = initStore({}, {
inject: {
rest: {
getters: { $appVersion: () => '1.1' },
},
},
});
expect(store.getters['rest/baseRestUrl']).toBe('/api/1.1');
});
});What's inside
The injections are quite simple. When the following module will be passed to createInjectPlugin:
export default {
namespaced: true,
getters: {
$todoList: () => [],
$todoTitle: () => '',
},
actions: {
$addTodoItem: () => {},
$mutateTodoTitle: () => {},
},
inject: {
getters: {
$todoList: 'todo/list',
},
state: {
// Note: state will be injected as getter
$todoTitle: 'todo/form.title',
},
actions: {
$addTodoItem: 'todo/addItem',
},
mutations: {
// Note: mutation will be injected as action
$mutateTodoTitle: 'todo/setTitle',
},
},
};the module functions will be replaced with something like this:
export default {
namespaced: true,
getters: {
$todoList: (s, g, rootGetters) => rootGetters['todo/list'],
$todoTitle: (s, g, rg, rootState) => rootState.todo.form.title,
},
actions: {
$addTodoItem: ({ dispatch }, p) => {
dispatch('todo/addItem', p, { root: true });
},
$mutateTodoTitle: ({ commit }, value) => {
commit('todo/setTitle', value, { root: true });
},
},
};Conditional injection
Conditional injections may help implement dynamic feature toggles. If the module,
we are injecting from, has isInjectable (default name) getter and its value is false, then
all the parts injected from that module will return default (original) values,
until isInjectable getter become true.
Example:
// store/modules/profile.js
export default {
namespaced: true,
state: {
isFeatureEnabled: false,
groups: ['user', 'admin'],
},
getters: {
isInjectable: state => state.isFeatureEnabled,
},
mutations: {
setFeatureStatus: (state, value) => { state.isFeatureEnabled = value; },
},
};
// store/modules/acl.js
export default {
namespaced: true,
getters: {
isAdmin: (state, getters) => getters.$profileGroups.indexOf('admin') >= 0,
$profileGroups: () => [],
},
inject: {
state: {
$profileGroups: 'profile/groups',
},
},
};
// test
describe('acl', () => {
it('should have conditional injection', () => {
let store = initStore();
// profile feature is turned off,
// default implementation for $profileGroups
expect(store.getters['acl/$profileGroups']).toEqual([]);
expect(store.getters['acl/isAdmin']).toBe(false);
// profile feature should be toggled for beta user
store.commit('profile/setFeatureStatus', true);
// profile/groups become available
expect(store.getters['acl/$profileGroups']).toEqual(['user', 'admin']);
expect(store.getters['acl/isAdmin']).toBe(true);
});
});API
createInjectPlugin(options)
Creates a new instance of the plugin with the given options. The following options can be provided:
modules <Object>: store modules config. We pass it here instead of store constructor.inject <Object>: injection config. This config has precedence over inner module inject config.moduleField <String>: Inner module inject config will be looked under this name. Defaults toinjectconditionalGetterName <String>: Getter name to check to find out if conditional injection is allowed. Defaults toisInjectable
License
MIT © Denis Pobelov