├── .babelrc ├── .gitignore ├── LICENSE ├── README.md ├── dist └── vuex-jsdata-plugin.js ├── examples └── simple │ ├── index.html │ ├── index.js │ ├── jsdata │ ├── index.js │ └── models │ │ ├── comment.js │ │ └── user.js │ └── vuex │ ├── actions.js │ ├── config.js │ ├── index.js │ └── plugins.js ├── index.js ├── package.json ├── rollup.config.js └── utils ├── helpers.js └── polyfill.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "es2015", 5 | { 6 | "modules": false 7 | } 8 | ] 9 | ], 10 | "plugins": [ 11 | "external-helpers", 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Alexandre Bonaventure Geissmann 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vuex-jsdata-plugin 2 | A simple attempt to help using jsdata alongside Vue.js: 3 | This plugin syncing Vuex store with js-data 4 | After each injection made by js-data some silent mutations are triggered to ensure vuex store is kept in sync with your ressources (and trigger reactivity). 5 | 6 | Read more : https://github.com/js-data/js-data/issues/57 7 | 8 | # Dependencies 9 | This plugin is developed for 10 | ``` 11 | vuex@2.0.x 12 | js-data@2.9.x 13 | ``` 14 | 15 | You're welcome to contribute to help compatibility issues. 16 | 17 | # Usage 18 | With NPM 19 | ```npm install vuex-plugin-jsdata``` 20 | 21 | Then when you setup vuex: 22 | ``` 23 | import jsdataPlugin from 'vuex-plugin-jsdata' 24 | import yourJsDataStore from 'xxxx' 25 | 26 | const plugins = [ 27 | jsdataPlugin(yourJsDataStore), 28 | ... // other plugins 29 | ] 30 | 31 | new Vuex.Store({ 32 | // state, 33 | // actions, 34 | // mutations, 35 | plugins, 36 | }) 37 | 38 | ``` 39 | # How does it work ? 40 | Every change in a js-data ressource are made with the DSInject method. 41 | The plugin manage the state tree(vuex) under a DS module by listening to the afterInject hook (js-data) 42 | 43 | ## mutation 44 | vuex-plugin-jsdata fire only one silent mutation : 45 | ``REFRESH_DATASTORE`` 46 | 47 | ## getters 48 | Although all local ressources injected in the jsdata-store can be found in the vuex store under the namespaced module DS, the plugin provide automatic getters for every model. 49 | 50 | Ex: 51 | ``` 52 | // Register a model in js-data 53 | 54 | export const User = store.defineResource({ 55 | name: 'user', 56 | endpoint: 'users', 57 | }) 58 | ``` 59 | ``` 60 | // in a .vue component 61 | 77 | 78 | 82 | 83 | ``` 84 | ## global helper 85 | This plugin provide a handy way to make a ressource available inside components. 86 | ### mapRessources 87 | mapRessources([ 88 | { nameOfTheGetter: [nameOfTheRessource:string, id_key:string]}, 89 | ... 90 | ]) 91 | mapRessources is a getter factory designed to get a single record which id is computed from $vm[id_key]. 92 | Its really useful for getting specific records dynamicly (eg: get user with id picked from router params) 93 | example: 94 | ``` 95 | // in store - DSUsers: { 1: { name: 'Alex' } } 96 | // component definition 97 | // using the object spread operator 98 | $vm = { 99 | data() { 100 | return { 101 | user_id: 1 102 | } 103 | }, 104 | computed: { 105 | ...mapRessources([ 106 | { user: ['User', 'user_id'] } 107 | { userFromRoute: ['User', '$route.params.id'] } // with vue-router 108 | ]), 109 | } 110 | } 111 | // Log 112 | $vm.user.name -> 'Alex' 113 | $vm.userFromRoute.name -> 'Alex' 114 | ``` 115 | 116 | # Example 117 | Clone the repo and run 118 | ``` 119 | npm install 120 | npm run example-simple 121 | => go to /examples/simple 122 | ``` 123 | 124 | more to come ... 125 | 126 | # TO-DO 127 | [ ] handle config options 128 | [ ] some examples 129 | 130 | # Contributions 131 | are welcome :) 132 | -------------------------------------------------------------------------------- /dist/vuex-jsdata-plugin.js: -------------------------------------------------------------------------------- 1 | (function (global, factory) { 2 | typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('vue'), require('lodash.get')) : 3 | typeof define === 'function' && define.amd ? define(['exports', 'vue', 'lodash.get'], factory) : 4 | (factory((global.vuexjsdataplugin = global.vuexjsdataplugin || {}),global.vue,global.get)); 5 | }(this, (function (exports,vue,get) { 'use strict'; 6 | 7 | get = 'default' in get ? get['default'] : get; 8 | 9 | // Object.values polyfill (borrowed from https://github.com/tc39/proposal-object-values-entries/blob/master/polyfill.js) 10 | var reduce = Function.bind.call(Function.call, Array.prototype.reduce); 11 | var isEnumerable = Function.bind.call(Function.call, Object.prototype.propertyIsEnumerable); 12 | var concat = Function.bind.call(Function.call, Array.prototype.concat); 13 | var keys = Reflect.ownKeys; 14 | 15 | if (!Object.values) { 16 | Object.values = function values(O) { 17 | return reduce(keys(O), function (v, k) { 18 | return concat(v, typeof k === 'string' && isEnumerable(O, k) ? [O[k]] : []); 19 | }, []); 20 | }; 21 | } 22 | 23 | var asyncGenerator = function () { 24 | function AwaitValue(value) { 25 | this.value = value; 26 | } 27 | 28 | function AsyncGenerator(gen) { 29 | var front, back; 30 | 31 | function send(key, arg) { 32 | return new Promise(function (resolve, reject) { 33 | var request = { 34 | key: key, 35 | arg: arg, 36 | resolve: resolve, 37 | reject: reject, 38 | next: null 39 | }; 40 | 41 | if (back) { 42 | back = back.next = request; 43 | } else { 44 | front = back = request; 45 | resume(key, arg); 46 | } 47 | }); 48 | } 49 | 50 | function resume(key, arg) { 51 | try { 52 | var result = gen[key](arg); 53 | var value = result.value; 54 | 55 | if (value instanceof AwaitValue) { 56 | Promise.resolve(value.value).then(function (arg) { 57 | resume("next", arg); 58 | }, function (arg) { 59 | resume("throw", arg); 60 | }); 61 | } else { 62 | settle(result.done ? "return" : "normal", result.value); 63 | } 64 | } catch (err) { 65 | settle("throw", err); 66 | } 67 | } 68 | 69 | function settle(type, value) { 70 | switch (type) { 71 | case "return": 72 | front.resolve({ 73 | value: value, 74 | done: true 75 | }); 76 | break; 77 | 78 | case "throw": 79 | front.reject(value); 80 | break; 81 | 82 | default: 83 | front.resolve({ 84 | value: value, 85 | done: false 86 | }); 87 | break; 88 | } 89 | 90 | front = front.next; 91 | 92 | if (front) { 93 | resume(front.key, front.arg); 94 | } else { 95 | back = null; 96 | } 97 | } 98 | 99 | this._invoke = send; 100 | 101 | if (typeof gen.return !== "function") { 102 | this.return = undefined; 103 | } 104 | } 105 | 106 | if (typeof Symbol === "function" && Symbol.asyncIterator) { 107 | AsyncGenerator.prototype[Symbol.asyncIterator] = function () { 108 | return this; 109 | }; 110 | } 111 | 112 | AsyncGenerator.prototype.next = function (arg) { 113 | return this._invoke("next", arg); 114 | }; 115 | 116 | AsyncGenerator.prototype.throw = function (arg) { 117 | return this._invoke("throw", arg); 118 | }; 119 | 120 | AsyncGenerator.prototype.return = function (arg) { 121 | return this._invoke("return", arg); 122 | }; 123 | 124 | return { 125 | wrap: function (fn) { 126 | return function () { 127 | return new AsyncGenerator(fn.apply(this, arguments)); 128 | }; 129 | }, 130 | await: function (value) { 131 | return new AwaitValue(value); 132 | } 133 | }; 134 | }(); 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | var defineProperty = function (obj, key, value) { 149 | if (key in obj) { 150 | Object.defineProperty(obj, key, { 151 | value: value, 152 | enumerable: true, 153 | configurable: true, 154 | writable: true 155 | }); 156 | } else { 157 | obj[key] = value; 158 | } 159 | 160 | return obj; 161 | }; 162 | 163 | var get$1 = function get$1(object, property, receiver) { 164 | if (object === null) object = Function.prototype; 165 | var desc = Object.getOwnPropertyDescriptor(object, property); 166 | 167 | if (desc === undefined) { 168 | var parent = Object.getPrototypeOf(object); 169 | 170 | if (parent === null) { 171 | return undefined; 172 | } else { 173 | return get$1(parent, property, receiver); 174 | } 175 | } else if ("value" in desc) { 176 | return desc.value; 177 | } else { 178 | var getter = desc.get; 179 | 180 | if (getter === undefined) { 181 | return undefined; 182 | } 183 | 184 | return getter.call(receiver); 185 | } 186 | }; 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | var set$1 = function set$1(object, property, value, receiver) { 205 | var desc = Object.getOwnPropertyDescriptor(object, property); 206 | 207 | if (desc === undefined) { 208 | var parent = Object.getPrototypeOf(object); 209 | 210 | if (parent !== null) { 211 | set$1(parent, property, value, receiver); 212 | } 213 | } else if ("value" in desc && desc.writable) { 214 | desc.value = value; 215 | } else { 216 | var setter = desc.set; 217 | 218 | if (setter !== undefined) { 219 | setter.call(receiver, value); 220 | } 221 | } 222 | 223 | return value; 224 | }; 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | var toConsumableArray = function (arr) { 241 | if (Array.isArray(arr)) { 242 | for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; 243 | 244 | return arr2; 245 | } else { 246 | return Array.from(arr); 247 | } 248 | }; 249 | 250 | var MUTATION = 'datastore/REFRESH_DATASTORE'; 251 | var MUTATION_DELETE = 'datastore/DELETE'; 252 | var DStore = void 0; 253 | 254 | var index = function (_DStore) { 255 | var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, 256 | _ref$namespace = _ref.namespace, 257 | namespace = _ref$namespace === undefined ? 'DS' : _ref$namespace, 258 | _ref$silent = _ref.silent, 259 | silent = _ref$silent === undefined ? true : _ref$silent; 260 | 261 | DStore = _DStore; 262 | if (!DStore) { 263 | console.warn('You must initialize vuex-jsdata-plugin with a DS store object from js-data'); 264 | return; 265 | } 266 | 267 | return function (store) { 268 | var _mutations; 269 | 270 | var ressources = Object.values(DStore.definitions); 271 | var getters = {}; 272 | var moduleState = {}; 273 | vue.set(store.state, namespace, {}); // init state 274 | getters[namespace] = function (state) { 275 | return state[namespace]; 276 | }; // set global getter 277 | 278 | ressources.forEach(function (_ref2) { 279 | var ressourceName = _ref2.class; 280 | 281 | var key = "" + namespace + ressourceName; 282 | getters[key] = function (state) { 283 | return state[ressourceName]; 284 | }; 285 | vue.set(moduleState, ressourceName, {}); 286 | }); 287 | 288 | var module = { 289 | state: moduleState, // init ressource state 290 | getters: getters, 291 | mutations: (_mutations = {}, defineProperty(_mutations, MUTATION, function (state, _ref3) { 292 | var type = _ref3.type, 293 | data = _ref3.data; 294 | var id = data.id; 295 | 296 | var namespace = state[type]; 297 | vue.set(namespace, id, Object.assign(JSON.parse(JSON.stringify(data)))); // assign to trigger reactivity 298 | }), defineProperty(_mutations, MUTATION_DELETE, function (state, _ref4) { 299 | var type = _ref4.type, 300 | data = _ref4.data; 301 | var id = data.id; 302 | 303 | var namespace = state[type]; 304 | vue.delete(namespace, id); // assign to trigger reactivity 305 | }), _mutations) 306 | }; 307 | store.registerModule('DS', module); 308 | 309 | function commitRefresh(res, data) { 310 | var commit = function commit(instance) { 311 | // set(instance, '__refresh', !instance.__refresh) 312 | store.commit(MUTATION, { 313 | type: res.class, 314 | data: instance 315 | }, { silent: silent }); 316 | }; 317 | if (Array.isArray(data)) data.forEach(commit);else commit(data); 318 | } 319 | function commitDelete(res, data) { 320 | var commit = function commit(instance) { 321 | store.commit(MUTATION_DELETE, { 322 | type: res.class, 323 | data: instance 324 | }, { silent: silent }); 325 | }; 326 | if (Array.isArray(data)) data.forEach(commit);else commit(data); 327 | } 328 | 329 | ressources.forEach(function (ressource) { 330 | ressource.on('Refresh', function (res, id) { 331 | var data = res.get(id); 332 | commitRefresh(res, data); 333 | }); 334 | // ressource.on('DS.change', (res, data) => commitRefresh(res, data)) 335 | ressource.on('DS.afterDestroy', function (res, data) { 336 | res.off('DS.change'); 337 | commitDelete(res, data); 338 | setTimeout(function () { 339 | // FIXME 340 | res.on('DS.change', function (res, data) { 341 | commitRefresh(res, data); 342 | }); 343 | }, 100); 344 | }); 345 | var refreshCb = function refreshCb(res, data) { 346 | return commitRefresh(res, data); 347 | }; 348 | ressource.on('DS.afterInject', function handler(res, data) { 349 | refreshCb(res, data); 350 | // ressource.off('DS.afterInject', handler) 351 | // ressource.on('DS.change', refreshCb) 352 | }); 353 | }); 354 | }; 355 | } 356 | 357 | function mapRessources() { 358 | var ressources = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; 359 | 360 | function generateGetter(name, key) { 361 | return function getter() { 362 | var id = get(this, key); 363 | if (id === null || id === undefined || !this.$store.state.DS[name][id]) { 364 | console.warn('no ressource with id:' + id); 365 | return undefined; 366 | } // !IMPORTANT trigger reactivity 367 | return DStore.get(name, id); 368 | }; 369 | } 370 | var ressourceGetters = ressources.reduce(function (sum, ressource) { 371 | var getterName = Object.keys(ressource)[0]; 372 | ressource[getterName] = generateGetter.apply(undefined, toConsumableArray(ressource[getterName])); 373 | return Object.assign(sum, ressource); 374 | }, {}); 375 | return ressourceGetters; 376 | } 377 | 378 | exports['default'] = index; 379 | exports.mapRessources = mapRessources; 380 | 381 | Object.defineProperty(exports, '__esModule', { value: true }); 382 | 383 | }))); 384 | -------------------------------------------------------------------------------- /examples/simple/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Simple 6 | 7 | 8 |
9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /examples/simple/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue/dist/vue.common.js' 2 | // import Vue from 'vue' 3 | import { mapRessources } from '../../dist/vuex-jsdata-plugin.js' 4 | import { mapGetters } from 'vuex' 5 | 6 | // Init JsDataStore 7 | import Comment from 'jsdata/models/comment' 8 | import User from 'jsdata/models/user' 9 | 10 | // Setup some mocks 11 | Comment.inject([ 12 | { 13 | id: 0, 14 | comment: 'Hello', 15 | }, 16 | { 17 | id: 1, 18 | comment: 'World', 19 | }, 20 | ]) 21 | User.inject([ 22 | { 23 | id: 0, 24 | name: 'Alex', 25 | }, 26 | ]) 27 | 28 | // Init VuexStore 29 | import VuexStore from 'vuex/index.js' 30 | 31 | // Kickstart App 32 | 33 | 34 | const vm = new Vue({ 35 | store: VuexStore, 36 | data: { 37 | msg: 'Simple Example', 38 | user_id: 0, 39 | }, 40 | template: ` 41 |
42 |

{{msg}}

43 | Comments: 44 |
{{comments}}
45 | 46 | Users: 47 |
{{users}}
48 | 49 | Specific User: 50 |
{{user}}
51 | 52 |

Please, open your DevTool

53 |
54 | `, 55 | computed: Object.assign({}, 56 | { 57 | comments() { return this.$store.getters.DSComment}, 58 | users() { return this.$store.getters.DSUser}, 59 | }, 60 | // mapGetters([ 61 | // 'DSUser', 62 | // ]), 63 | mapRessources([ 64 | { user: ['User', 'user_id'] }, 65 | ]) 66 | ), 67 | }).$mount('#app') 68 | -------------------------------------------------------------------------------- /examples/simple/jsdata/index.js: -------------------------------------------------------------------------------- 1 | import JSData from 'js-data' 2 | 3 | export default new JSData.DS() 4 | -------------------------------------------------------------------------------- /examples/simple/jsdata/models/comment.js: -------------------------------------------------------------------------------- 1 | import store from '../' 2 | export default store.defineResource({ 3 | name: 'Comment', 4 | endpoint: 'comment', 5 | }) 6 | -------------------------------------------------------------------------------- /examples/simple/jsdata/models/user.js: -------------------------------------------------------------------------------- 1 | import store from '../' 2 | export default store.defineResource({ 3 | name: 'User', 4 | endpoint: 'users', 5 | }) 6 | -------------------------------------------------------------------------------- /examples/simple/vuex/actions.js: -------------------------------------------------------------------------------- 1 | 2 | function makeAction(type) { 3 | return ({ commit }, payload) => commit(type, payload) 4 | } 5 | 6 | export const addNode = makeAction('UPDATE_FIELD') 7 | -------------------------------------------------------------------------------- /examples/simple/vuex/config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | mobile: false, 3 | sidebar: true, 4 | } 5 | -------------------------------------------------------------------------------- /examples/simple/vuex/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue/dist/vue.common.js' 2 | import Vuex from 'vuex' 3 | import plugins from './plugins' 4 | 5 | const state = { 6 | count: 0, 7 | } 8 | 9 | const mutations = { 10 | INCREMENT (state) { 11 | state.count++ 12 | }, 13 | } 14 | 15 | const actions = { 16 | INCREMENT ({ commit }) { 17 | commit('INCREMENT') 18 | }, 19 | } 20 | 21 | Vue.use(Vuex) 22 | const store = new Vuex.Store({ 23 | state, 24 | mutations, 25 | plugins, 26 | actions, 27 | }) 28 | 29 | export default store 30 | -------------------------------------------------------------------------------- /examples/simple/vuex/plugins.js: -------------------------------------------------------------------------------- 1 | import createLogger from 'vuex/dist/logger' 2 | import jsDataPlugin from '../../../dist/vuex-jsdata-plugin.js' 3 | import store from 'jsdata/index' 4 | 5 | const plugins = [ 6 | jsDataPlugin(store, { silent: false }), 7 | ] 8 | 9 | export default process.env.NODE_ENV !== 'production' 10 | ? [ 11 | createLogger(), 12 | ...plugins, 13 | ] : plugins 14 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue" 2 | import get from "lodash.get" 3 | import './utils/polyfill' 4 | 5 | const config = {} 6 | const { set, delete: remove } = Vue 7 | const MUTATION = 'datastore/REFRESH_DATASTORE' 8 | const MUTATION_DELETE = 'datastore/DELETE' 9 | let DStore 10 | 11 | export default function(_DStore, { 12 | namespace = 'DS', 13 | silent = true, 14 | } = {}) { 15 | DStore = _DStore 16 | if (!DStore) { 17 | console.warn('You must initialize vuex-jsdata-plugin with a DS store object from js-data') 18 | return 19 | } 20 | 21 | return function(store) { 22 | const ressources = Object.values(DStore.definitions) 23 | let getters = {} 24 | let moduleState = {} 25 | set(store.state, namespace, {}) // init state 26 | getters[namespace] = (state) => state[namespace] // set global getter 27 | 28 | ressources.forEach(({ class: ressourceName }) => { 29 | const key = `${namespace}${ressourceName}` 30 | getters[key] = (state) => state[ressourceName] 31 | set(moduleState, ressourceName, {}) 32 | }) 33 | 34 | const module = { 35 | state: moduleState, // init ressource state 36 | getters, 37 | mutations: { 38 | [MUTATION](state, { type, data }) { 39 | const { id } = data 40 | const namespace = state[type] 41 | set(namespace, id, Object.assign(JSON.parse(JSON.stringify(data)))) // assign to trigger reactivity 42 | }, 43 | [MUTATION_DELETE](state, { type, data }) { 44 | const { id } = data 45 | const namespace = state[type] 46 | remove(namespace, id) // assign to trigger reactivity 47 | }, 48 | }, 49 | } 50 | store.registerModule('DS', module) 51 | 52 | function commitRefresh(res, data) { 53 | const commit = instance => { 54 | // set(instance, '__refresh', !instance.__refresh) 55 | store.commit(MUTATION, { 56 | type: res.class, 57 | data: instance, 58 | }, { silent }) 59 | } 60 | if (Array.isArray(data)) data.forEach(commit) 61 | else commit(data) 62 | } 63 | function commitDelete(res, data) { 64 | const commit = instance => { 65 | store.commit(MUTATION_DELETE, { 66 | type: res.class, 67 | data: instance, 68 | }, { silent }) 69 | } 70 | if (Array.isArray(data)) data.forEach(commit) 71 | else commit(data) 72 | } 73 | 74 | ressources.forEach((ressource) => { 75 | ressource.on('Refresh', (res, id) => { 76 | const data = res.get(id) 77 | commitRefresh(res, data) 78 | }) 79 | // ressource.on('DS.change', (res, data) => commitRefresh(res, data)) 80 | ressource.on('DS.afterDestroy', (res, data) => { 81 | res.off('DS.change') 82 | commitDelete(res, data) 83 | setTimeout(() => { // FIXME 84 | res.on('DS.change', function (res, data) { 85 | commitRefresh(res, data); 86 | }); 87 | }, 100) 88 | }) 89 | const refreshCb = (res, data) => commitRefresh(res, data) 90 | ressource.on('DS.afterInject', function handler(res, data) { 91 | refreshCb(res, data) 92 | // ressource.off('DS.afterInject', handler) 93 | // ressource.on('DS.change', refreshCb) 94 | }) 95 | }) 96 | } 97 | } 98 | 99 | export function mapRessources(ressources = []) { 100 | function generateGetter(name, key) { 101 | return function getter() { 102 | const id = get(this, key) 103 | if (id === null || id === undefined || !this.$store.state.DS[name][id]) { 104 | console.warn('no ressource with id:' + id) 105 | return undefined 106 | } // !IMPORTANT trigger reactivity 107 | return DStore.get(name, id); 108 | } 109 | } 110 | const ressourceGetters = ressources.reduce((sum, ressource) => { 111 | const getterName = Object.keys(ressource)[0] 112 | ressource[getterName] = generateGetter(...ressource[getterName]) 113 | return Object.assign(sum, ressource) 114 | }, {}) 115 | return ressourceGetters 116 | } 117 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vuex-plugin-jsdata", 3 | "version": "0.3.0", 4 | "description": "A vuex plugin to sync vuex store with jsdata-store", 5 | "main": "dist/vuex-jsdata-plugin.js", 6 | "module": "index.js", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1", 9 | "build": "rollup -c --name vuexjsdataplugin --input index.js --output dist/vuex-jsdata-plugin.js --format umd", 10 | "example-simple": "NODE_PATH=$NODE_PATH:examples/simple budo examples/simple/index.js --open -d . --live -- --paths -t [ babelify --presets [ es2015 ] --plugins [external-helpers-2] --sourceType module] -p [ babelify-external-helpers ]" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/AlexandreBonaventure/vuex-jsdata-plugin.git" 15 | }, 16 | "keywords": [ 17 | "vuex", 18 | "js-data", 19 | "plugin", 20 | "store", 21 | "model", 22 | "data" 23 | ], 24 | "author": "Alexandre Bonaventure Geissmann", 25 | "license": "MIT", 26 | "bugs": { 27 | "url": "https://github.com/AlexandreBonaventure/vuex-jsdata-plugin/issues" 28 | }, 29 | "homepage": "https://github.com/AlexandreBonaventure/vuex-jsdata-plugin#readme", 30 | "peerDependencies": { 31 | "vue": ">= 1.x <= 2.x" 32 | }, 33 | "dependencies": { 34 | "lodash.get": "=4.4.2" 35 | }, 36 | "devDependencies": { 37 | "babel-plugin-external-helpers": "^6.8.0", 38 | "babel-preset-es2015": "^6.18.0", 39 | "babelify": "^7.3.0", 40 | "babelify-external-helpers": "^1.1.0", 41 | "budo": "^9.2.2", 42 | "js-data": "^2.9.0", 43 | "rollup": "^0.36.1", 44 | "rollup-plugin-babel": "^2.6.1", 45 | "vue": "^2.1.4", 46 | "vuex": "^2.0.0" 47 | }, 48 | "browserify-shim": {} 49 | } 50 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import { rollup } from 'rollup' 2 | import babel from 'rollup-plugin-babel' 3 | 4 | export default { 5 | plugins: [ babel() ], 6 | format: 'umd', 7 | } 8 | -------------------------------------------------------------------------------- /utils/helpers.js: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /utils/polyfill.js: -------------------------------------------------------------------------------- 1 | // Object.values polyfill (borrowed from https://github.com/tc39/proposal-object-values-entries/blob/master/polyfill.js) 2 | const reduce = Function.bind.call(Function.call, Array.prototype.reduce); 3 | const isEnumerable = Function.bind.call(Function.call, Object.prototype.propertyIsEnumerable); 4 | const concat = Function.bind.call(Function.call, Array.prototype.concat); 5 | const keys = Reflect.ownKeys; 6 | 7 | if (!Object.values) { 8 | Object.values = function values(O) { 9 | return reduce(keys(O), (v, k) => concat(v, typeof k === 'string' && isEnumerable(O, k) ? [O[k]] : []), []); 10 | }; 11 | } 12 | --------------------------------------------------------------------------------