{"id":22486286,"url":"https://github.com/Akryum/vue-supply","last_synced_at":"2025-08-02T19:31:26.166Z","repository":{"id":65379119,"uuid":"81816057","full_name":"Akryum/vue-supply","owner":"Akryum","description":"Create resources that can automatically be activated and deactivated when used (like subscriptions)","archived":false,"fork":false,"pushed_at":"2017-07-07T23:33:29.000Z","size":70,"stargazers_count":159,"open_issues_count":7,"forks_count":16,"subscribers_count":13,"default_branch":"master","last_synced_at":"2024-04-14T09:51:16.111Z","etag":null,"topics":["realtime-data","subscription","vuejs2","vuex"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Akryum.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-02-13T11:05:38.000Z","updated_at":"2023-08-25T09:44:52.000Z","dependencies_parsed_at":"2023-01-20T08:35:39.265Z","dependency_job_id":null,"html_url":"https://github.com/Akryum/vue-supply","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Akryum%2Fvue-supply","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Akryum%2Fvue-supply/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Akryum%2Fvue-supply/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Akryum%2Fvue-supply/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Akryum","download_url":"https://codeload.github.com/Akryum/vue-supply/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":228500100,"owners_count":17929999,"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":["realtime-data","subscription","vuejs2","vuex"],"created_at":"2024-12-06T17:14:16.790Z","updated_at":"2024-12-06T17:15:40.705Z","avatar_url":"https://github.com/Akryum.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\u003cimg src=\"logo.png\"/\u003e\u003c/p\u003e\n\n\u003ch1 align=\"center\"\u003evue-supply\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://www.npmjs.com/package/vue-supply\"\u003e\u003cimg src=\"https://img.shields.io/npm/v/vue-supply.svg\"/\u003e \u003cimg src=\"https://img.shields.io/npm/dm/vue-supply.svg\"/\u003e\u003c/a\u003e\n  \u003ca href=\"https://vuejs.org/\"\u003e\u003cimg src=\"https://img.shields.io/badge/vue-2.x-brightgreen.svg\"/\u003e\u003c/a\u003e\n\u003c/p\u003e\n\nCreate resources that can automatically be activated and deactivated when used (like subscriptions)\n\n💬 [VueConf 2017 demo](https://github.com/Akryum/vueconf-2017-demo) \u0026amp; [slides](http://slides.com/akryum/graphql#/)\n\n# Table of contents\n\n- [Why do I need this?](#why-do-i-need-this)\n- [Installation](#installation)\n- [Usage](#usage)\n- [Examples](#examples)\n\n# Why do I need this?\n\nEfficiently managing reactive and living data from an external source can become difficult in a large app or when using [vuex](https://github.com/vuejs/vuex/). With `vue-supply`, you can easily consume data and automatically activate or deactivate subscriptions.\n\n[The Vue+Meteor demo project](https://github.com/Akryum/vue-meteor-demo) demonstrate how to use Meteor reactive data easily with `vue-supply` inside components and a vuex store.\n\n## What kind of data?\n\n`vue-supply` is suitable for any kind of reactive and realtime data. For example:\n\n- [meteor](https://www.meteor.com/) reactive data (tracker, minimongo...) and realtime subscriptions/publications\n- [apollo](http://www.apollodata.com/) GraphQL subscriptions (using websockets)\n- [firebase](https://firebase.google.com/) realtime subscriptions\n- ...\n\n## How does it work?\n\nWith `vue-supply`, you create Vue instances extending the `Supply` definition. You then define two methods: `activate` and `deactivate`. For example, you can subscribe to a realtime publication in the `activate` method and destroy this subscription in the `deactivate` method. When you will use this `Supply` in your components (called 'consumers'), it will automatically activate itself when it is first used (with the `grasp` method) or deactivate itself when no component use it anymore (with the `release` method). You can also easily store the realtime data inside the `Supply` and access it in the consumer components or in vuex getters. Anywhere in your code, you can wait for a `Supply` to be activated with the `ensureActive` method.\n\n`Supply` also understands the notion of loading the data: when your subscription is being processed, just increment the `loading` property. When it's ready, decrement `loading`. If all the operations are done (which means that `loading` value is `0`), the `Supply` will emit the `is-ready` event you can listen to. You can also use the `ready` property directly in your templates (or somewhere else). There is also a `ensureReady` method that waits for the `Supply` to be ready.\n\n# Installation\n\n```\nnpm install --save vue-supply\n```\n\n## Default import\n\n```javascript\nimport Vue from 'vue'\nimport VueSupply from 'vue-supply'\n\nVue.use(VueSupply)\n```\n\n## Browser\n\n```html\n\u003cscript src=\"vue.js\"\u003e\u003c/script\u003e\n\u003cscript src=\"vue-supply/dist/vue-supply.browser.js\"\u003e\u003c/script\u003e\n```\n\nThe plugin should be auto-installed. If not, you can install it manually with the instructions below.\n\n```javascript\nVue.use(VueSupply)\n```\n\n# Usage\n\nA supply is a Vue instance which is responsible for managing a piece of dynamic data (for example, a Meteor, GraphQL or Firebase subscription with data that may change and update from the server). It has an deactivated state (default), and an activated state when the data should be updated (for example, when a subscription is running).\n\nTo create a supply, write a Vue definition object extending the `Supply` definition:\n\n```javascript\nimport { Supply } from 'vue-supply'\n\nexport default {\n  extends: Supply,\n  // Vue options here\n}\n```\n\nThen you can manually create a supply with the Vue constructor:\n\n```javascript\nimport Vue from 'vue'\nimport TestResourceDef from 'supply/test-resource'\n\nconst TestResource = new Vue(TestResource)\n```\n\nThe two methods when using the supply are:\n\n - `supply.grasp()` which increments `supply.consumers` by `1`\n - `supply.release()` which decrements `supply.consumers` by `1`\n\nTo activate or deactivate the supply, use the `grasp` and `release` methods where you need to access the supply:\n\n```javascript\nconsole.log(TestResource.consumers) // 0\nTestResource.grasp()\nconsole.log(TestResource.consumers) // 1\nconsole.log(TestResource.someData) // Access the data\nTestResource.release()\nconsole.log(TestResource.consumers) // 0\n```\n\nThe supply will emit a `consumers` event with the count when it changes.\n\nThe supply is active if it has one or more `consumers`. When it becomes active, it calls the `activate` method, which you should override in the definition:\n\n```javascript\nexport default {\n  extends: Supply,\n  methods: {\n    activate () {\n      // Subscribe\n    },\n  },\n}\n```\n\nAlso, the `active` event is emitted on the supply, with a `true` boolean argument, and the `is-active` event.\n\n```javascript\nTestResource.$on('active', (isActive) =\u003e {\n  // Do something\n})\n```\n\nAnd when there are no more consumer for the supply, the `deactivate` method is called:\n\n```javascript\nexport default {\n  extends: Supply,\n  methods: {\n    activate () {\n      // Subscribe\n    },\n    deactivate () {\n      // Unsubscribe\n    },\n  },\n}\n```\n\nAlso, the `active` event is emitted on the supply, with a `false` boolean argument, and the `is-not-active` event.\n\nThere is a `active` computed boolean available that changes when the supply is activated or deactivated:\n\n```javascript\nTestResource.$watch('active', isActive =\u003e {\n  console.log(isActive)\n})\n```\n\nYou can also use the `supply.ensureActive()` method which return a promise that resolves as soon as the supply is activated (or immediatly if it is already):\n\n```javascript\nTestResource.ensureActive().then(() =\u003e {\n  // The supply is active\n})\n```\n\n## Registration\n\nIt is recommended to register the supply definition to enable injection in components and in the vuex store.\n\n```javascript\nimport { register } from 'vue-supply'\nimport TestResourceDef from 'supply/test-resource'\nregister('TestResource', TestResourceDef)\n```\n\n## Usage in components\n\nInside a component, add a mixin with `use(name, manageKeepAlive = true)` to automatically `grasp` and `release` the supply when the component is created and destroyed, using the name used in the registration (see above):\n\n```javascript\nimport { use } from 'vue-supply'\n\nexport default {\n  // This component now uses TestResource\n  mixins: [use('TestResource')],\n\n  // Use the values in computed properties\n  computed: {\n    answer () {\n      return this.$supply.TestResource.someData\n    },\n  },\n\n  // ...\n}\n```\n\nThen you can use the supply data inside computed properties or inside methods with the `this.$supply[name]` object:\n\n```javascript\n// Use the values in computed properties\ncomputed: {\n  answer () {\n    return this.$supply.TestResource.someData\n  },\n},\n```\n\n## Usage in Vuex store\n\nInside a vuex store, you can inject getters that use supplies:\n\n```javascript\nexport default {\n  supply: {\n    use: ['TestResource'],\n    inject: ({ TestResource }) =\u003e ({\n      getters: {\n        'all-items': () =\u003e TestResource.items,\n      },\n    }),\n  },\n\n  getters: {\n    'count': (state, getters) =\u003e getters['all-items'].length,\n  },\n}\n```\n\nBefore creating the Vuex store, transform the options with the `injectSupply(options, cache)` method:\n\n```javascript\nimport { injectSupply } from 'vue-supply'\n\nconst supplyCache = {}\nconst suppliedStoreOptions = injectSupply(storeOptions, supplyCache)\n\nconst store = new Vuex.Store(suppliedStoreOptions)\n```\n\nProvide the supply cache to the root Vue instance so that the supplies created for the store are reused in the components:\n\n```javascript\nnew Vue({\n  // ...\n  supplyCache,\n}),\n```\n\nThen to activate/deactivate the supply, you can either call the `grasp` and `release` methods inside actions:\n\n```javascript\nsupply: {\n  use: ['TestResource'],\n  inject: ({ TestResource }) =\u003e ({\n    getters: {\n      'all-items': () =\u003e TestResource.items,\n    },\n\n    actions: {\n      'subscribe-action' () {\n        // Request usage in the store\n        // Ex: subscribing to a Meteor publication\n        TestResource.grasp()\n      },\n\n      'unsubscribe-action' () {\n        // No longer used in the store\n        // Ex: unsubscribing from a Meteor publication\n        TestResource.release()\n      },\n    }\n  }),\n},\n```\n\nOr with the mixins and the `use` function inside components using the getter:\n\n```javascript\nimport { use } from 'vue-supply'\nimport { mapGetters } from 'vuex'\n\nexport default {\n  // This component now uses TestResource supply\n  mixins: [use('TestResource')],\n\n  // Use getter that utilize the supply\n  computed: {\n    ...mapGetters({\n      items: 'all-items',\n    })\n  },\n}\n```\n\n## Asynchronous data\n\nA loading system is included in the supply supplies. Change the `loading` integer property:\n\n - `0` means the supply is ready to be consumed (for example, data is loaded). This is the default value.\n - `1` or more means there is loading in progress\n\nYou should change the `loading` property inside the `activate` and `deactive` methods:\n\n```javascript\nimport { Supply } from 'vue-supply'\n\nexport default new Vue({\n  extends: Supply,\n  methods: {\n    activate () {\n      console.log('subscribing...')\n      // Use the integer `loading` property\n      // 0 mean ready\n      this.loading ++\n      // Faking a server request here :p\n      setTimeout(() =\u003e {\n        console.log('data is loaded')\n        this.loading --\n      }, 1000)\n    },\n  },\n})\n```\n\nYou can get the loading state with the `ready` computed property, a boolean which is `true` when there are no loading in progress. It can directly used inside computed properties:\n\n```javascript\nimport TestResource from 'supply/test-resource'\n\nexport default {\n  // Use the values in computed properties\n  computed: {\n    isDataReady () {\n      return TestResource.ready\n    },\n  },\n}\n```\n\nThere are the `ready` (with a boolean argument), `is-ready` and `is-not-ready` events.\n\nYou can also use the `supply.ensureReady()` method which return a promise that resolves as soon as the supply is ready (or immediatly if it is already):\n\n```javascript\nTestResource.ensureReady().then(() =\u003e {\n // The supply is ready\n})\n```\n\nThere is a useful function, `consume`, which comes in handy when you only need to use the supply periodically. It both graspes and wait for ready and return a `release` function:\n\n```javascript\nimport { consume } from 'vue-supply'\nimport TestResource from 'supply/test-resource'\n// This will grasp and wait for the supply to be 'ready'\nconst release = await consume(TestResource)\n// Count of active supply consumers\nconsole.log('consumers', TestResource.consumers)\n// When you are done with the supply, release it\nrelease()\n```\n\n## Base supply definition\n\nIt's often useful to create a base definition for each supply.\n\nExample for Meteor:\n\n```javascript\n// base.js\nimport { Supply } from 'vue-supply'\n\nexport default {\n  extends: Supply,\n\n  methods: {\n    activate () {\n      this.$startMeteor()\n    },\n\n    deactivate () {\n      this.$stopMeteor()\n    },\n  },\n\n  meteor: {\n    $lazy: true,\n  },\n}\n```\n\nExample supply:\n\n```javascript\n// Items.js\nimport base from './base'\nimport { Items } from '../api/collections'\n\nexport default {\n  extends: base,\n\n  data () {\n    return {\n      items: [],\n    }\n  },\n\n  meteor: {\n    $subscribe: {\n      'items': [],\n    },\n\n    items () {\n      return Items.find({})\n    },\n  },\n}\n```\n\n# Examples\n\n## Basics\n\nCreate a supply:\n\n```javascript\n\nexport default {\n  extends: Supply,\n  data () {\n    return {\n      someData: null,\n    }\n  },\n  methods: {\n    activate () {\n      console.log('subscribing...')\n      // Use the integer `loading` property\n      // 0 mean ready\n      this.loading ++\n      // Faking a server request here :p\n      setTimeout(() =\u003e {\n        this.someData = 42\n        this.loading --\n      }, 1000)\n    },\n    deactivate () {\n      console.log('unsubscribing...')\n    },\n  },\n}\n```\n\nRegister the supply:\n\n```javascript\nimport { register } from 'vue-supply'\nimport TestResource from './supply/test-resource'\nregister('TestResource', TestResource)\n```\n\nUse the supply in components:\n\n```javascript\nimport { use } from 'vue-supply'\n\nexport default {\n  // This component now uses TestResource\n  mixins: [use('TestResource')],\n\n  // Use the values in computed properties\n  computed: {\n    answer () {\n      return this.$supply.TestResource.someData\n    }\n  },\n\n  // ...\n}\n```\n\nOr in the vuex store:\n\n```javascript\nexport default {\n  supply: {\n    use: ['TestResource'],\n    inject: ({ TestResource }) =\u003e ({\n      getters: {\n        // Use the supply data in getters\n        'my-getter': () =\u003e TestResource.someData,\n      },\n      actions: {\n        'subscribe-action' () {\n          // Request usage in the store\n          // Ex: subscribing to a Meteor publication\n          TestResource.grasp()\n        },\n\n        'unsubscribe-action' () {\n          // No longer used in the store\n          // Ex: unsubscribing from a Meteor publication\n          TestResource.release()\n        },\n\n        async 'consume-action' ({ commit }) {\n          // This will wait for the supply to be 'ready'\n          const release = await consume(TestResource)\n          // Count of active supply consumers\n          console.log('consumers', TestResource.consumers)\n          commit('my-commit', TestResource.someData)\n          // When you are done with the supply, release it\n          release()\n        },\n      },\n    }),\n  },\n}\n```\n\n---\n\n## License\n\n[MIT](http://opensource.org/licenses/MIT)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FAkryum%2Fvue-supply","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FAkryum%2Fvue-supply","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FAkryum%2Fvue-supply/lists"}