• Stars
    star
    159
  • Rank 235,916 (Top 5 %)
  • Language
    JavaScript
  • Created almost 8 years ago
  • Updated over 7 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Create resources that can automatically be activated and deactivated when used (like subscriptions)

vue-supply

Create resources that can automatically be activated and deactivated when used (like subscriptions)

💬 VueConf 2017 demo & slides

Table of contents

Why do I need this?

Efficiently managing reactive and living data from an external source can become difficult in a large app or when using vuex. With vue-supply, you can easily consume data and automatically activate or deactivate subscriptions.

The Vue+Meteor demo project demonstrate how to use Meteor reactive data easily with vue-supply inside components and a vuex store.

What kind of data?

vue-supply is suitable for any kind of reactive and realtime data. For example:

  • meteor reactive data (tracker, minimongo...) and realtime subscriptions/publications
  • apollo GraphQL subscriptions (using websockets)
  • firebase realtime subscriptions
  • ...

How does it work?

With 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.

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.

Installation

npm install --save vue-supply

Default import

import Vue from 'vue'
import VueSupply from 'vue-supply'

Vue.use(VueSupply)

Browser

<script src="vue.js"></script>
<script src="vue-supply/dist/vue-supply.browser.js"></script>

The plugin should be auto-installed. If not, you can install it manually with the instructions below.

Vue.use(VueSupply)

Usage

A 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).

To create a supply, write a Vue definition object extending the Supply definition:

import { Supply } from 'vue-supply'

export default {
  extends: Supply,
  // Vue options here
}

Then you can manually create a supply with the Vue constructor:

import Vue from 'vue'
import TestResourceDef from 'supply/test-resource'

const TestResource = new Vue(TestResource)

The two methods when using the supply are:

  • supply.grasp() which increments supply.consumers by 1
  • supply.release() which decrements supply.consumers by 1

To activate or deactivate the supply, use the grasp and release methods where you need to access the supply:

console.log(TestResource.consumers) // 0
TestResource.grasp()
console.log(TestResource.consumers) // 1
console.log(TestResource.someData) // Access the data
TestResource.release()
console.log(TestResource.consumers) // 0

The supply will emit a consumers event with the count when it changes.

The 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:

export default {
  extends: Supply,
  methods: {
    activate () {
      // Subscribe
    },
  },
}

Also, the active event is emitted on the supply, with a true boolean argument, and the is-active event.

TestResource.$on('active', (isActive) => {
  // Do something
})

And when there are no more consumer for the supply, the deactivate method is called:

export default {
  extends: Supply,
  methods: {
    activate () {
      // Subscribe
    },
    deactivate () {
      // Unsubscribe
    },
  },
}

Also, the active event is emitted on the supply, with a false boolean argument, and the is-not-active event.

There is a active computed boolean available that changes when the supply is activated or deactivated:

TestResource.$watch('active', isActive => {
  console.log(isActive)
})

You 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):

TestResource.ensureActive().then(() => {
  // The supply is active
})

Registration

It is recommended to register the supply definition to enable injection in components and in the vuex store.

import { register } from 'vue-supply'
import TestResourceDef from 'supply/test-resource'
register('TestResource', TestResourceDef)

Usage in components

Inside 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):

import { use } from 'vue-supply'

export default {
  // This component now uses TestResource
  mixins: [use('TestResource')],

  // Use the values in computed properties
  computed: {
    answer () {
      return this.$supply.TestResource.someData
    },
  },

  // ...
}

Then you can use the supply data inside computed properties or inside methods with the this.$supply[name] object:

// Use the values in computed properties
computed: {
  answer () {
    return this.$supply.TestResource.someData
  },
},

Usage in Vuex store

Inside a vuex store, you can inject getters that use supplies:

export default {
  supply: {
    use: ['TestResource'],
    inject: ({ TestResource }) => ({
      getters: {
        'all-items': () => TestResource.items,
      },
    }),
  },

  getters: {
    'count': (state, getters) => getters['all-items'].length,
  },
}

Before creating the Vuex store, transform the options with the injectSupply(options, cache) method:

import { injectSupply } from 'vue-supply'

const supplyCache = {}
const suppliedStoreOptions = injectSupply(storeOptions, supplyCache)

const store = new Vuex.Store(suppliedStoreOptions)

Provide the supply cache to the root Vue instance so that the supplies created for the store are reused in the components:

new Vue({
  // ...
  supplyCache,
}),

Then to activate/deactivate the supply, you can either call the grasp and release methods inside actions:

supply: {
  use: ['TestResource'],
  inject: ({ TestResource }) => ({
    getters: {
      'all-items': () => TestResource.items,
    },

    actions: {
      'subscribe-action' () {
        // Request usage in the store
        // Ex: subscribing to a Meteor publication
        TestResource.grasp()
      },

      'unsubscribe-action' () {
        // No longer used in the store
        // Ex: unsubscribing from a Meteor publication
        TestResource.release()
      },
    }
  }),
},

Or with the mixins and the use function inside components using the getter:

import { use } from 'vue-supply'
import { mapGetters } from 'vuex'

export default {
  // This component now uses TestResource supply
  mixins: [use('TestResource')],

  // Use getter that utilize the supply
  computed: {
    ...mapGetters({
      items: 'all-items',
    })
  },
}

Asynchronous data

A loading system is included in the supply supplies. Change the loading integer property:

  • 0 means the supply is ready to be consumed (for example, data is loaded). This is the default value.
  • 1 or more means there is loading in progress

You should change the loading property inside the activate and deactive methods:

import { Supply } from 'vue-supply'

export default new Vue({
  extends: Supply,
  methods: {
    activate () {
      console.log('subscribing...')
      // Use the integer `loading` property
      // 0 mean ready
      this.loading ++
      // Faking a server request here :p
      setTimeout(() => {
        console.log('data is loaded')
        this.loading --
      }, 1000)
    },
  },
})

You 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:

import TestResource from 'supply/test-resource'

export default {
  // Use the values in computed properties
  computed: {
    isDataReady () {
      return TestResource.ready
    },
  },
}

There are the ready (with a boolean argument), is-ready and is-not-ready events.

You 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):

TestResource.ensureReady().then(() => {
 // The supply is ready
})

There 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:

import { consume } from 'vue-supply'
import TestResource from 'supply/test-resource'
// This will grasp and wait for the supply to be 'ready'
const release = await consume(TestResource)
// Count of active supply consumers
console.log('consumers', TestResource.consumers)
// When you are done with the supply, release it
release()

Base supply definition

It's often useful to create a base definition for each supply.

Example for Meteor:

// base.js
import { Supply } from 'vue-supply'

export default {
  extends: Supply,

  methods: {
    activate () {
      this.$startMeteor()
    },

    deactivate () {
      this.$stopMeteor()
    },
  },

  meteor: {
    $lazy: true,
  },
}

Example supply:

// Items.js
import base from './base'
import { Items } from '../api/collections'

export default {
  extends: base,

  data () {
    return {
      items: [],
    }
  },

  meteor: {
    $subscribe: {
      'items': [],
    },

    items () {
      return Items.find({})
    },
  },
}

Examples

Basics

Create a supply:

export default {
  extends: Supply,
  data () {
    return {
      someData: null,
    }
  },
  methods: {
    activate () {
      console.log('subscribing...')
      // Use the integer `loading` property
      // 0 mean ready
      this.loading ++
      // Faking a server request here :p
      setTimeout(() => {
        this.someData = 42
        this.loading --
      }, 1000)
    },
    deactivate () {
      console.log('unsubscribing...')
    },
  },
}

Register the supply:

import { register } from 'vue-supply'
import TestResource from './supply/test-resource'
register('TestResource', TestResource)

Use the supply in components:

import { use } from 'vue-supply'

export default {
  // This component now uses TestResource
  mixins: [use('TestResource')],

  // Use the values in computed properties
  computed: {
    answer () {
      return this.$supply.TestResource.someData
    }
  },

  // ...
}

Or in the vuex store:

export default {
  supply: {
    use: ['TestResource'],
    inject: ({ TestResource }) => ({
      getters: {
        // Use the supply data in getters
        'my-getter': () => TestResource.someData,
      },
      actions: {
        'subscribe-action' () {
          // Request usage in the store
          // Ex: subscribing to a Meteor publication
          TestResource.grasp()
        },

        'unsubscribe-action' () {
          // No longer used in the store
          // Ex: unsubscribing from a Meteor publication
          TestResource.release()
        },

        async 'consume-action' ({ commit }) {
          // This will wait for the supply to be 'ready'
          const release = await consume(TestResource)
          // Count of active supply consumers
          console.log('consumers', TestResource.consumers)
          commit('my-commit', TestResource.someData)
          // When you are done with the supply, release it
          release()
        },
      },
    }),
  },
}

License

MIT

More Repositories

1

vue-virtual-scroller

⚡️ Blazing fast scrolling for any amount of data
Vue
8,256
star
2

floating-vue

💬 Easy tooltips, popovers, dropdown, menus... for Vue
TypeScript
2,955
star
3

vue-observe-visibility

Detect when an element is becoming visible or hidden on the page.
JavaScript
1,619
star
4

guijs

🐣️ The app that makes your #devlife easier.
Vue
1,276
star
5

peeky

A fast and fun test runner for Vite & Node 🐈️ Powered by Vite ⚡️
TypeScript
683
star
6

vue-9-perf-secrets

Demos from my talk "9 performance secrets revealed"
Vue
555
star
7

vue-googlemaps

Integrate Google Maps in your Vue application
JavaScript
534
star
8

vue-mention

Mention component for Vue.js
Vue
496
star
9

vue-resize

A generic component to detect DOM elements resizing
JavaScript
491
star
10

vue-cli-plugin-apollo

🚀 @vue/cli plugin for Vue Apollo
JavaScript
477
star
11

vue-cli-plugin-ssr

✨ Simple SSR plugin for Vue CLI
JavaScript
444
star
12

vue-progress-path

Progress bars and loading indicators for Vue.js
Vue
421
star
13

awesomejs.dev

Find awesome packages for the framework you are using
Vue
205
star
14

nodepack

📦 A progressive Node.js framework
JavaScript
200
star
15

monorepo-run

Run scripts in monorepo with colors, streaming and separated panes
JavaScript
184
star
16

vue-summit-app

Example App: Vue + GraphQL + Apollo (Devfest Nantes 2017)
JavaScript
183
star
17

vue-share-components

vue-cli template to publish components easily
JavaScript
153
star
18

portless

Easy local domains with superpowers
TypeScript
113
star
19

vue-apollo-example

Simple vue-apollo example app
Vue
107
star
20

apollo-server-example

A very simple Apollo GraphQL server
JavaScript
92
star
21

vue-router-multi-view

router-view meet v-show meet keep-alive
JavaScript
87
star
22

vue-apollo-todos

Vue Apollo GraphQL mutation examples
Vue
80
star
23

frontpage-vue-app

Apollo "hello world" app, for Vue 2.0
Vue
79
star
24

vueconf-2017-demo

VueConf 2017 GraphQL client demo
Vue
79
star
25

vue-safe-teleport

Recommended for teleporting inside the app
TypeScript
72
star
26

graphql-migrate

🔀 Instantly create or update a SQL database from a GraphQL API schema
TypeScript
58
star
27

kickstart-meteor-vue-huge-app

Example meteor project featuring vue, code-splitting and hot-reload
Vue
48
star
28

meteor-vue2-example

Meteor & Vue 2.0 example
Vue
44
star
29

packt-vue-project-guide

Vue Project Guide Book Sources
JavaScript
42
star
30

reactive-fs

Reactive file system interface
TypeScript
38
star
31

sheep

🐑 Opinionated CLI command to release pnpm monorepos
TypeScript
38
star
32

vue-typegen

Generate types for TypeScript Vue components libraries
TypeScript
37
star
33

blaze2

Vue-powered blaze
JavaScript
35
star
34

meteor-vite

JavaScript
34
star
35

meteor-vue-example

Simple meteor example with vue
Vue
31
star
36

vue-perf-tests

Demos for my 'Vue performance tricks' talk
Vue
27
star
37

meteor-socket-io

Simple meteor example with socket.io
JavaScript
25
star
38

window-button-layout

Retrieve OS window buttons layout (close, maximize, minimize...)
TypeScript
21
star
39

vue-amsterdam-2018

🚀 Demo sources of my talk at Vue Amsterdam 2018
18
star
40

graphql-annotations

Annotate a GraphQL schema
JavaScript
17
star
41

mincopy

a CLI that synchronizes two folders with minimal writes
JavaScript
14
star
42

vue3-devtools-plugin-example

JavaScript
14
star
43

meteor-vue2-example-routing

Simple meteor example with vue 2.x and routing
Vue
14
star
44

apollo-client-browser

A browser-ready version of apollo-client
HTML
12
star
45

nux

Node-webkit web browser
HTML
10
star
46

meteor-vuex-example

Simple meteor app with vuex
JavaScript
10
star
47

vue-ssr-after-2-6

Vue
10
star
48

vue-cli-locale-fr

French localization for @vue/cli
9
star
49

vue2-devtools-plugin-example

TypeScript
8
star
50

code-civil

Static website for the french "code civil"
JavaScript
8
star
51

vue-cli-plugin-demo

A demo plugin for Vue CLI 3
JavaScript
7
star
52

nuxt3-demo

Vue
6
star
53

vue-template-explorer

Explore Vue templates
Vue
6
star
54

meteor-vue-blaze

Example meteor project
JavaScript
5
star
55

wear-calculator

Calculator for Wear OS 3
Kotlin
5
star
56

turbo-vs-vite

Pre-generated apps from vercel/turbo bundler benchmark + additional variations
JavaScript
4
star
57

test-video-perf

Vue
4
star
58

vue-apollo-githunt

Githunt example
JavaScript
4
star
59

electron-vite-vue-test

TypeScript
4
star
60

yarn-check-node

[Yarn berry] Check node version from engines field in package.json
JavaScript
4
star
61

peeky-vue-demo

TypeScript
3
star
62

vue-example

Vue
3
star
63

nuxt-i18n-hmr

Hot reload issue reproduction for nuxt-i18n
Vue
3
star
64

guijs-generator-nuxt

Nuxt project generator for guijs
TypeScript
2
star
65

vue-test-functional

Testing functional components
Vue
2
star
66

vue-cli-plugin-fs-router

2
star
67

meteor-mysql

This is a MeteorJS package which brings strong and easy way in order to write apps using Mysql.
TypeScript
2
star
68

no-unnecessary-boolean-literal-compare-repro

2
star
69

storybook-issue-app-use

Vue
2
star
70

polytech-pokedex-2019

JavaScript
2
star
71

guijs-website

guijs website
Vue
2
star
72

mahjong

C++
1
star
73

vue-ssr-before-2-6

JavaScript
1
star
74

nuxt-fallback-404

Vue
1
star
75

unplugin-icons-issue-94

JavaScript
1
star
76

graphql-autoquery

Mysterious space augment for resolvers
1
star
77

graphql-demo

Meetup demo
JavaScript
1
star
78

webpack-issue-11612

JavaScript
1
star
79

guijs-generator-vue-cli

guijs generator for Vue CLI project
TypeScript
1
star
80

dd-apm-test-ui

Vue
1
star
81

polytech-pokedex

Vue
1
star
82

meteor-vue-example-routing

Simple meteor example with vue and routing
Vue
1
star
83

md-icons-svg

Material Icons (SVG files)
1
star
84

repro-rollup-vue2-jsx

JavaScript
1
star