Vue Aspect
This package has been renamed with major improvements. Use the new vuex-aspect instead!
Create custom vuex state bindings with async queries.
Feature List
- [x] Bind
state
attribute to result of async queries (via "aspects") - [x] Reactive vuex state and getters for query parameters
- [x] Activate aspects on demand (via component mixin and/or programmatic API)
- [x] Vuex module system integration
- [x] Skip aspect cycle depending on state and getters
- [x] No dependencies, lightweight library
Installation
npm install --save vue-aspect
Grant Vuex.Store access
Vue-aspect needs access to the Vuex/Store
instance. Grant this as following:
import {prepareStore} from 'vue-aspect';
const store = prepareStore(new Vuex.Store({ ... }));
(elaborate example store instance snippet)
Usage Guide
Aspect creation
You may create as many individual aspects as needed:
import {Aspect} from 'vue-aspect';
const userAspect = new Aspect({
variables({state}) { // optional function
// get reactive variables, e.g.
return state.userId == null ? false : {id: state.userId};
},
async resolve(variables) {
// run async query to fetch the new value, e.g.
return (await apollo.query({query: userQuery, variables})).data.user;
},
});
(elaborate example aspects snippet)
Each aspect is responsible to keep the local state value of remote data up-to-date. With the variables collected from reactive state, the resolve
function will be called every time the variables change. If variables
returns false
however (state.userId == null
), the aspect will not trigger the resolve
function (we call this a 'skip').
On its own this aspect won't do anything, it needs to be attached to any vuex module first.
Aspect registration
Each aspect can only be attached to one vuex module.
Add an aspects
object to your vuex module options that contains a mapping of state-keys to their respective aspect.
export default { // vuex store module options
state: { user: null },
aspects: { user: userAspect },
};
(elaborate vuex module snippet)
Note: You should always set an initial state value for each attached aspect.
As of now the registration of our aspects is completed. Still, the aspect does nothing. It is considered disabled until it has any consumer.
Aspect consumption
Aspects can have two types of consumers:
- Components and
- API access
Component consumer
Since components are a first-class citizen of vue.js, this is probably the more important consumer type. Any vue component can consume some aspect simply by using its mixin.
export default { // vue component options
mixins: [userAspect.mixin]
}
(elaborate vue component consumer example)
Now the userAspect
is consumed by this component while it is alive.
This enables the aspect and triggers an initial state.user
retrieval as well as re-evaluation whenever state.userId
changes (via vuex commits). This is it, we now can use state.user
within this component; knowing that it is at least loading (if state.userId
is not null
).
API consumer
Aspects provide an programmatic API for consumption as well:
// consume and get promise of immediate evaluation (or cached promise if aspect was already enabled)
userPromise = userAspect.grasp();
// force a re-evaluation and get its promise
userPromise = userAspect.fetch();
// get the latest promise (may resolve to null if all preceeding evaluations were skipped)
userPromise = userAspect.value;
// end consumption
disablePromise = userAspect.release();
End of consumption
When all consumers have stopped the consumption (components died / aspect.release()
got called), the userAspect
once again turns inactive. It does set state.user
to null
and remove its cache.
API
Constructor
The Aspect(options)
constructor accepts the following options:
-
{function} variables
- Optional
- Accepts:
{state, getters, rootState, rootGetters}
- Returns:
false | *
- If
false
is returned,resolve
will not be called. Otherwise the return value will be passed as first parameter toresolve
. This function is watched by vuex while any single consumer exists for the aspect. Each time any used state attribute changes,resolve
will be triggered again with the new return value as first parameter (if notfalse
).
-
{async function} resolve
- Required
- Accepts:
variables, context
- Returns:
*
- The return value will be committed onto the vuex state.
-
{async function} enable
- Optional
- Called when the aspect is consumed for the first time (since disable).
-
{async function} disable
- Optional
- Called when the aspect is not consumed anymore.
- Default:
async function () { this.clear(); }
-
{async function} error
- Optional
- Accepts:
err
- Returns:
*
- Default:
async function (err) { console.error(err); throw err; }
- Called when
resolve
fails with an error. If it does return successful, the value will be used as updated result of the aspect.
All functions are called with the Aspect
instance as this
.
Aspect.prototype
The Aspect prototype contains the consumption API (async grasp()
, async fetch()
, value
, async release()
) as well as some modification methods:
-
clear()
- Sets the value within the state as well as the cache to
undefined
.
- Sets the value within the state as well as the cache to
-
setValue({*} value)
- Sets the value within the state to the passed
value
.
- Sets the value within the state to the passed
-
setCache({undefined|Promise} value)
- Sets the cache to the passed
value
. Set toundefined
for cache invalidation, or to aPromise
for cache injection.
- Sets the cache to the passed
Thanks
Thank you for considering vue-aspect
.
Feel free to file issues, send merge requests or contribute in any other way!
Suggestions and feedback are highly appreciated.
Many thanks to the vue.js community for being awesome!
License
This program is licensed under MIT. It is supposed to help others but comes with no warranty of any kind.