├── tsconfig.json ├── package.json ├── .gitignore ├── src └── index.ts └── README.md /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es5", 5 | "lib": ["dom", "es2016"], 6 | "outDir": "./dist", 7 | "moduleResolution": "node", 8 | "preserveConstEnums": true 9 | }, 10 | "include": ["./src"] 11 | } 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-streams", 3 | "version": "1.0.0-beta.5", 4 | "description": "Streams for Vue", 5 | "main": "dist/index.js", 6 | "files": ["dist"], 7 | "repository": { 8 | "type": "git", 9 | "url": "git+https://github.com/johnlindquist/vue-streams.git" 10 | }, 11 | "keywords": ["vue", "streams", "rx", "rxjs"], 12 | "author": "John Lindquist", 13 | "license": "MIT", 14 | "bugs": { 15 | "url": "https://github.com/johnlindquist/vue-streams/issues" 16 | }, 17 | "homepage": "https://github.com/johnlindquist/vue-streams#readme", 18 | "scripts": { 19 | "dev": "tsc -w", 20 | "build": "tsc" 21 | }, 22 | "devDependencies": { 23 | "rxjs": "^6.0.0-rc.0", 24 | "rxjs-compat": "^6.0.0-rc.0", 25 | "typescript": "^2.8.1", 26 | "vue": "^2.5.16" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | 63 | dist/ -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export enum Source { 2 | fromMethod, 3 | fromEmit, 4 | fromWatch 5 | } 6 | 7 | export const fromMethod = name => { 8 | const config = { 9 | type: Source.fromMethod, 10 | name 11 | } 12 | 13 | return name ? () => config : config 14 | } 15 | 16 | export const fromWatch = name => { 17 | const config = { 18 | type: Source.fromWatch, 19 | name 20 | } 21 | 22 | return name ? () => config : config 23 | } 24 | 25 | export const fromEmit = name => { 26 | const config = { 27 | type: Source.fromEmit, 28 | name 29 | } 30 | 31 | return name ? () => config : config 32 | } 33 | 34 | function VueStreams(Vue, { Subject, BehaviorSubject }) { 35 | Vue.mixin({ 36 | created() { 37 | const vm = this 38 | vm._streamKeysToDelete = [] 39 | vm._streamKeysToUnwatch = [] 40 | vm._streamKeysToOff = [] 41 | 42 | const sourcesConfig = {} 43 | const { sources = {}, subscriptions } = vm.$options as any 44 | if (sources) { 45 | Object.keys(sources).forEach(sourceName => { 46 | if (typeof sources[sourceName] === "function") { 47 | const { type, name = sourceName } = sources[sourceName]() 48 | switch (type) { 49 | case Source.fromMethod: 50 | const methodSubject = (sourcesConfig[ 51 | sourceName 52 | ] = new Subject()) 53 | 54 | vm._streamKeysToDelete.push(name) 55 | vm[name] = methodSubject.next.bind(methodSubject) 56 | break 57 | case Source.fromWatch: 58 | if (!vm[name]) { 59 | vm._streamKeysToDelete.push(name) 60 | ;(Vue as any).util.defineReactive(vm, name, undefined) 61 | } 62 | const watchSubject = (sourcesConfig[ 63 | sourceName 64 | ] = new BehaviorSubject(vm[name])) 65 | vm._streamKeysToUnwatch.push( 66 | vm.$watch(name, watchSubject.next.bind(watchSubject)) 67 | ) 68 | break 69 | case Source.fromEmit: 70 | const emitSubject = (sourcesConfig[sourceName] = new Subject()) 71 | vm._streamKeysToDelete.push(name) 72 | vm.$on(name, emitSubject.next.bind(emitSubject)) 73 | vm._streamKeysToOff.push(name) 74 | break 75 | } 76 | } 77 | }) 78 | } 79 | 80 | if (subscriptions) { 81 | const subs = subscriptions(sourcesConfig) || {} 82 | vm._subscriptions = Object.keys(subs).map(key => { 83 | ;(Vue as any).util.defineReactive(vm, key, undefined) 84 | 85 | return subs[key].subscribe(value => { 86 | vm[key] = value 87 | }) 88 | }) 89 | } 90 | }, 91 | destroy() { 92 | const vm = this 93 | const { _subscriptions } = vm 94 | if (_subscriptions) { 95 | _subscriptions.forEach(sub => sub.unsubscribe()) 96 | } 97 | 98 | vm._streamKeysToDelete.forEach(key => delete vm[key]) 99 | vm._streamKeysToUnwatch.forEach(unwatch => unwatch()) 100 | vm._streamKeysToOff.forEach(event => vm.$off(event)) 101 | } 102 | }) 103 | } 104 | 105 | export default VueStreams 106 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ⛲️ vue-streams ⛲️ 2 | 3 | A simplified approach to using streams with Vue. 4 | 5 | ## Install 6 | 7 | ```bash 8 | npm i vue-streams rxjs@rc 9 | ``` 10 | 11 | rxjs v6 is recommended (currently in Release Candidate) 12 | 13 | ## Setup 14 | 15 | Install as a plugin: 16 | 17 | ```js 18 | import VueStreams from "vue-streams" 19 | import { Subject, BehaviorSubject } from "rxjs" 20 | Vue.use(VueStreams, { Subject, BehaviorSubject }) 21 | ``` 22 | 23 | ## Usage 24 | 25 | ### Simple Example 26 | 27 | [](https://codesandbox.io/s/yv37425lox) 28 | 29 | ```js 30 | 31 | 32 | Click me 33 | {{random$}} 34 | 35 | 36 | 49 | ``` 50 | 51 | ### Standard Example 52 | 53 | [](https://codesandbox.io/s/ov6yk15w26) 54 | 55 | ```js 56 | 57 | 58 | Search 59 | Clear 60 | 61 | {{message$}} 62 | 63 | 64 | 65 | {{person.name}} 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 124 | 145 | ``` 146 | 147 | ### Crazy Example 148 | 149 | [](https://codesandbox.io/s/48jyk13nm7) 150 | 151 | ```js 152 | 153 | 154 | {{x$}} 155 | One 156 | Two 157 | Load Random 158 | 159 | 160 | Toggle 161 | 162 | 163 | hello 164 | 165 | 166 | {{message$}} 167 | 168 | 169 | 170 | 171 | 240 | 253 | ``` 254 | 255 | ## Other Demos 256 | 257 | ### Basic Vue Counter with JSX 258 | 259 | [](https://codesandbox.io/s/21zv0jqz40) 260 | 261 | ### Basic Form with `.prevent` 262 | 263 | [](https://codesandbox.io/s/mjr6x6o888?module=%2Fsrc%2FApp.vue) 264 | --------------------------------------------------------------------------------
hello