{"id":17004810,"url":"https://github.com/clickermonkey/vuex-router-actions","last_synced_at":"2025-07-25T09:41:58.423Z","repository":{"id":152212256,"uuid":"147982542","full_name":"ClickerMonkey/vuex-router-actions","owner":"ClickerMonkey","description":"Streamline complex Vuex actions and have fast and secure asynchronous routing in your app.","archived":false,"fork":false,"pushed_at":"2018-10-03T13:57:46.000Z","size":121,"stargazers_count":30,"open_issues_count":0,"forks_count":1,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-03-26T02:01:38.509Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ClickerMonkey.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2018-09-09T01:17:10.000Z","updated_at":"2023-08-30T16:34:06.000Z","dependencies_parsed_at":null,"dependency_job_id":"109f5e89-e83d-40a8-be0b-dc0d6f49399d","html_url":"https://github.com/ClickerMonkey/vuex-router-actions","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ClickerMonkey%2Fvuex-router-actions","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ClickerMonkey%2Fvuex-router-actions/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ClickerMonkey%2Fvuex-router-actions/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ClickerMonkey%2Fvuex-router-actions/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ClickerMonkey","download_url":"https://codeload.github.com/ClickerMonkey/vuex-router-actions/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248529717,"owners_count":21119567,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-14T04:44:31.183Z","updated_at":"2025-04-12T06:31:46.222Z","avatar_url":"https://github.com/ClickerMonkey.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"## vuex-router-actions\r\n\r\nThe library you've been waiting for to streamline complex Vuex actions and have fast and secure asynchronous routing in your app.\r\n\r\n**Checkout [this example](https://github.com/ClickerMonkey/vuex-router-actions/blob/master/EXAMPLE.md) to see how this library can be used to create a Slack clone!**\r\n\r\n## Goals\r\n\r\n- To make it easier to use Vuex actions.\r\n- To make it easier to produce apps which can load data asynchronously before being routed to a page and also check along the way whether that user can go to that page - and also what to do when they cannot.\r\n- To cache action results so traversing through an app is as efficient as possible.\r\n- To make it easier to track asynchronous actions and keep the user notified when the app is loading.\r\n\r\n## Features\r\n\r\n- Cache the results of an action based on logic or a cache key.\r\n- Listen to when actions are started, resolved, rejected, ended, and all pending actions are done.\r\n- Call a mutation with a flag on whether a set of actions are currently running.\r\n- Check the state of the store to determine whether an action can continue (subsequently a route).\r\n- Keep a routed Vue component from loading or updating until an action is finished.\r\n- If an action has exited because a user cannot visit a route, provide a way to redirect the user somewhere else.\r\n- Communicate to the user when one or more actions are being processed (makes it easy to create loading screens).\r\n\r\n## Contents\r\n\r\n- [Goals](#goals)\r\n- [Features](#features)\r\n- [Dependencies](#dependencies)\r\n- [Installation](#installation)\r\n- [API](#api)\r\n  - [actionsWatch](#actionswatch)\r\n  - [actionsLoading](#actionsloading)\r\n  - [actionCached](#actioncached)\r\n  - [actionsCached](#actionscached)\r\n  - [actionCachedConditional](#actioncachedconditional)\r\n  - [actionsCachedConditional](#actionscachedconditional)\r\n  - [actionCachedResults](#actionscachedresults)\r\n  - [actionsCachedResults](#actionscachedresults)\r\n  - [actionsDestroyCache](#actionsdestroycache)\r\n  - [actionsProtect](#actionsprotect)\r\n  - [actionBeforeRoute](#actionbeforeroute)\r\n  - [actionOptional](#actionoptional)\r\n\r\n### Dependencies\r\n\r\nNo build time dependencies, but this library is used in conjunction with `vuex` and optionally `vue-router`.\r\n\r\n### Installation\r\n\r\n#### npm\r\n\r\nInstallation via npm : `npm install --save vuex-router-actions`\r\n\r\n## API\r\n\r\n### actionsWatch\r\n\r\nWatches the given actions and invokes the callbacks passed in the options of `VuexRouterActions`. If the result of an action is a promise - it is watched and immediately invokes `onActionStart` followed by `onActionResolve` or `onActionReject` and then `onActionEnd`. If the result of an action is not a promise then `onActionStart` and `onActionEnd` are invoked immediately. If there are no more watched actions being executed then `onActionsDone` is invoked after the last `onActionEnd`.\r\n\r\n```javascript\r\nimport VuexRouterActions, { actionsWatch } from 'vuex-router-actions'\r\nconst plugin = VuexRouterActions({\r\n  onActionStart (action, num, context, payload) {},\r\n  onActionEnd (action, num, context, payload, result, resolved) {}\r\n})\r\nconst store = new Vuex.Store({\r\n  plugins: [plugin],\r\n  actions: {\r\n    ...actionsWatch({\r\n      loadThis (context, payload) {},\r\n      loadThat (context, payload) {} // return a Promise\r\n    })\r\n  }\r\n})\r\n```\r\n\r\nYou can also pass in options as the 2nd argument of of `actionsWatch` to override any options passed to the plugin:\r\n\r\n```javascript\r\n...actionsWatch({\r\n  loadThis (context, payload) {},\r\n  loadThat (context, payload) {} // return a Promise\r\n}, {\r\n  onActionStart (action, num, context, payload) {},\r\n  onActionEnd (action, num, context, payload, result, resolved) {}\r\n})\r\n```\r\n\r\n### actionsLoading\r\n\r\nCalls a mutation on a store when any of the passed actions are running and when they stop. This provides an easy way to signal to the user when something is loading, even a complex set of asynchronous actions.\r\n\r\n```javascript\r\nimport VuexRouterActions, { actionsLoading } from 'vuex-router-actions'\r\nconst store = new Vuex.Store({\r\n  plugins: [VuexRouterActions()],\r\n  state: {\r\n    loading: false\r\n  },\r\n  mutations: {\r\n    setLoading (state, loading) {\r\n      state.loading = loading\r\n    }\r\n  },\r\n  actions: {\r\n    ...actionsLoading('setLoading', {\r\n      page1 (context, payload) {},\r\n      page2 (context, payload) {}\r\n    })\r\n  }\r\n})\r\n```\r\n\r\nInstead of a mutation you can optionally pass a function which is given a `context` and the `loading` flag.\r\n\r\n```javascript\r\n...actionsLoading(\r\n  (context, loading) =\u003e context.commit('setLoading', loading),\r\n  {\r\n    page1 (context, payload) {},\r\n    page2 (context, payload) {}\r\n  }\r\n)\r\n```\r\n\r\n### actionCached\r\n\r\nProduce an action with a cached result based on some cache key. The action will always run the first time. The cached results of the action are \"cleared\" when a different key is returned for a given action.\r\n\r\n```javascript\r\nimport VuexRouterActions, { actionCached } from 'vuex-router-actions'\r\nconst store = new Vuex.Store({\r\n  plugins: [VuexRouterActions()],\r\n  actions: {\r\n    loadPage: actionCache({\r\n      getKey: (context, payload) =\u003e payload, // look at store, getters, payload, etc\r\n      action: (context, payload) =\u003e null // some result that can be cached\r\n    })\r\n  }\r\n})\r\n```\r\n\r\nBy default the results of `getKey` are passed to `JSON.stringify` to make it an easily comparable value. If you want to override this functionality you can pass `createCacheKey (key: any): string` to the plugin options or as the second argument to actionsCached:\r\n\r\n```javascript\r\nloadPage: actionCached({\r\n  getKey: (context, payload) =\u003e payload, // look at store, getters, payload, etc\r\n  action: (context, payload) =\u003e null // some result that can be cached\r\n}, {\r\n  createCacheKey(key) { // the keys for these actions are arrays, we will join them to produce a string.\r\n    return key.join('-')\r\n  }\r\n})\r\n```\r\n\r\n### actionsCached\r\n\r\nSimilar to [actionCached](#actioncached) except it handles multiple actions.\r\nThis function also takes optional cache options as the second argument.\r\n\r\n```javascript\r\nimport VuexRouterActions, { actionsCached } from 'vuex-router-actions'\r\nconst store = new Vuex.Store({\r\n  plugins: [VuexRouterActions()],\r\n  actions: {\r\n    ...actionsCached({\r\n      loadPage: {\r\n        getKey: (context, payload) =\u003e payload, // look at store, getters, payload, etc\r\n        action: (context, payload) =\u003e null // some result that can be cached\r\n      }\r\n    })\r\n  }\r\n})\r\n```\r\n\r\n### actionCachedConditional\r\n\r\nProduce an action with a cached result based on some condition. The action will always run the first time. When `isInvalid` returns true the action is\r\nre-evaluated.\r\n\r\n```javascript\r\nimport VuexRouterActions, { actionCachedConditional } from 'vuex-router-actions'\r\nconst store = new Vuex.Store({\r\n  plugins: [VuexRouterActions()],\r\n  actions: {\r\n    loadPage: actionCachedConditional({\r\n      isInvalid: (context, payload) =\u003e true, // look at store, getters, payload, etc\r\n      action: (context, payload) =\u003e null // some result that can be cached\r\n    })\r\n  }\r\n})\r\n```\r\n\r\n### actionsCachedConditional\r\n\r\nSimilar to [actionCachedConditional](#actioncachedconditional) except it handles multiple actions.\r\n\r\n```javascript\r\nimport VuexRouterActions, { actionsCachedConditional } from 'vuex-router-actions'\r\nconst store = new Vuex.Store({\r\n  plugins: [VuexRouterActions()],\r\n  actions: {\r\n    ...actionsCachedConditional({\r\n      loadPage: {\r\n        isInvalid: (context, payload) =\u003e true, // look at store, getters, payload, etc\r\n        action: (context, payload) =\u003e null // some result that can be cached\r\n      }\r\n    })\r\n  }\r\n})\r\n```\r\n\r\n### actionCachedResults\r\n\r\nProduce an action with multiple cached results. The action has a `getKey` function which is optional - when the result of that function changes it clears the cache for all the results for the action. The action has a required `getResultKey` function which is unique to that action call - and if that key has not ran for that action it is ran, otherwise the cached result is returned.\r\n\r\n```javascript\r\nimport VuexRouterActions, { actionCachedResults } from 'vuex-router-actions'\r\nconst store = new Vuex.Store\u003cTestStore\u003e({\r\n  getGroupUser: actionCachedResults({\r\n    getKey: ({state}) =\u003e state.group.id, // When the group id changes, the cached results clear\r\n    getResultKey: (context, user_id) =\u003e user_id,\r\n    action: ({dispatch, state}, user_id) =\u003e null // TODO Return User instance relative to the given group\r\n  })\r\n})\r\n```\r\n\r\n### actionsCachedResults\r\n\r\nSimilar to [actionCachedResults](#actioncachedresults) except it handles multiple actions.\r\n\r\n```javascript\r\nimport VuexRouterActions, { actionsCachedResults } from 'vuex-router-actions'\r\nconst store = new Vuex.Store\u003cTestStore\u003e({\r\n  ...actionsCachedResults({\r\n    getGroupUser: {\r\n      getKey: ({state}) =\u003e state.group.id, // When the group id changes, the cached results clear\r\n      getResultKey: (context, user_id) =\u003e user_id,\r\n      action: ({dispatch, state}, user_id) =\u003e null // TODO Return User instance relative to the given group\r\n    }\r\n  })\r\n})\r\n```\r\n\r\n### actionsDestroyCache\r\n\r\nThis function destroys all caches created with `actionsCached`, `actionsCachedConditional`, and `actionsCachedResults`. This is a necessary function when you are implementing something like a Sign Out function in your app.\r\n\r\n```javascript\r\nimport { actionsDestroyCache } from 'vuex-router-actions'\r\n// signOut\r\nactionsDestroyCache()\r\n```\r\n\r\n### actionsProtect\r\n\r\nCreates \"protection\" actions. These are functions which return a truthy or falsy value and the action returned produces a Promise that is resolved or rejected. This is useful when creating actions which load data for a route. You can add protection actions before and/or after the loading actions so it can validate the route path and afterwards validate whether the user should be able to see the given route. If the protect action returns another promise that promise is passed through, and whether it resolves or rejects determines if the action stops or continues.\r\n\r\n```javascript\r\nimport VuexRouterActions, { actionsProtect, actionBeforeRoute } from 'vuex-router-actions'\r\nconst store = new Vuex.Store({\r\n  plugins: [VuexRouterActions()],\r\n  actions: {\r\n    loadUser (context, user_id) {}, // returns promise which loads user data, also commits user to state\r\n    loadTask (context, task_id) {}, // returns promise which loads task data\r\n    loadTaskPage ({dispatch}, to) { // for actionBeforeRoute the to route is passed\r\n      return dispatch('loadUser', to.params.user)\r\n        .then(user =\u003e dispatch('hasUser'))\r\n        .then(hasUser =\u003e dispatch('loadTask', to.params.task))\r\n        .then(task =\u003e dispatch('canViewTask', task))\r\n    },\r\n    ...actionsProtect({\r\n      hasUser: ({state}) =\u003e state.user \u0026\u0026 !state.user.disabled,\r\n      canViewTask: ({state}, task) =\u003e state.user.canView( task )\r\n    })\r\n  }\r\n})\r\n// Task page\r\nconst component = {\r\n  ...actionBeforeRoute('loadTaskPage')\r\n}\r\n```\r\n\r\n### actionBeforeRoute\r\n\r\nDispatches an action in the store and waits for the action to finish before the routed component this is placed in is entered or updated (see `beforeRouteEnter` and `beforeRouteUpdate` in `vue-router`). If the action resolves the routed component will be entered, otherwise `getOtherwise` will be invoked to determine where the router should be redirected. `getOtherwise` by default returns false which simply stops routing. If it returned undefined it would proceed as normal. If it returned a string or a route object it would redirect to that route.\r\n\r\n```javascript\r\n// MyPage.vue\r\nimport { actionBeforeRoute } from 'vuex-router-actions'\r\nexport default {\r\n ...actionBeforeRoute('loadMyPage', (to, from, rejectReason, store, action) =\u003e {\r\n    return '/page/blocked/where/do/I/go/next'\r\n })\r\n}\r\n```\r\n\r\n### actionOptional\r\n\r\nAllows you to pass the results of a dispatch through this function and whether or not it resolves or rejects it won't stop from proceeding. This happens by returning a promise which always resolves. If the given promise is rejected then `resolveOnReject` is passed to the resolve function of the returned Promise.\r\n\r\n```javascript\r\nimport VuexRouterActions, { actionsProtect, actionOptional } from 'vuex-router-actions'\r\nconst store = new Vuex.Store({\r\n  plugins: [VuexRouterActions()],\r\n  actions: {\r\n    loadUser (context, user_id) {}, // returns promise which loads user data, also commits user to state\r\n    loadTask (context, task_id) {}, // returns promise which loads task data\r\n    loadUserTask (context, task_id) {}, // returns a promise which may or may not return the relationship between the user and the task\r\n    loadTaskPage ({dispatch}, to) { // for actionBeforeRoute the to route is passed\r\n      return dispatch('loadUser', to.params.user)\r\n        .then(user =\u003e dispatch('hasUser'))\r\n        .then(hasUser =\u003e dispatch('loadTask', to.params.task))\r\n        .then(task =\u003e dispatch('canViewTask', task))\r\n        .then(userTask =\u003e actionOptional(dispatch('loadUserTask', to.params.task))) // \u003c== HERE\r\n    },\r\n    ...actionsProtect({\r\n      hasUser: ({state}) =\u003e state.user \u0026\u0026 !state.user.disabled,\r\n      canViewTask: ({state}, task) =\u003e state.user.canView( task )\r\n    })\r\n  }\r\n})\r\n```\r\n\r\n## LICENSE\r\n[MIT](https://opensource.org/licenses/MIT)\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fclickermonkey%2Fvuex-router-actions","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fclickermonkey%2Fvuex-router-actions","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fclickermonkey%2Fvuex-router-actions/lists"}