From 6f927823b6e964ea42897a6867b8c99079a325fc Mon Sep 17 00:00:00 2001 From: wangxiao Date: Mon, 5 Jun 2017 19:04:55 +0800 Subject: [PATCH 1/5] add feature eventToObservable --- README.md | 24 +++++++++++++++++++ src/index.js | 2 ++ src/methods/eventToObservable.js | 28 ++++++++++++++++++++++ test/test.js | 40 ++++++++++++++++++++++++++++++++ 4 files changed, 94 insertions(+) create mode 100644 src/methods/eventToObservable.js diff --git a/README.md b/README.md index a33ced4..a94b85d 100644 --- a/README.md +++ b/README.md @@ -183,6 +183,30 @@ vm.$watchAsObservable('a') The optional `options` object accepts the same options as `vm.$watch`. +#### `$eventToObservable(event)` + +> This feature requires RxJS. + +Convert vue.$on (including lifecycle events) to Observables. The emitted value is in the format of `{ name, msg }`: + +``` js +var vm = new Vue({ + created () { + this.$eventToObservable('customEvent') + .subscribe((event) => console.log(event.name,event.msg)) + } +}) + +// vm.$once vue-rx version +this.$eventToObservable('customEvent') + .take(1) + +// Another way to auto unsub: +let beforeDestroy$ = this.$eventToObservable('hook:beforeDestroy') +this.$eventToObservable('customEvent') + .takeUntil(beforeDestroy$) +``` + #### `$subscribeTo(observable, next, error, complete)` This is a prototype method added to instances. You can use it to subscribe to an observable, but let VueRx manage the dispose/unsubscribe. diff --git a/src/index.js b/src/index.js index 60961b9..5e5ed78 100644 --- a/src/index.js +++ b/src/index.js @@ -4,6 +4,7 @@ import streamDirective from './directives/stream' import watchAsObservable from './methods/watchAsObservable' import fromDOMEvent from './methods/fromDOMEvent' import subscribeTo from './methods/subscribeTo' +import eventToObservable from './methods/eventToObservable' export default function VueRx (Vue, Rx) { install(Vue, Rx) @@ -12,6 +13,7 @@ export default function VueRx (Vue, Rx) { Vue.prototype.$watchAsObservable = watchAsObservable Vue.prototype.$fromDOMEvent = fromDOMEvent Vue.prototype.$subscribeTo = subscribeTo + Vue.prototype.$eventToObservable = eventToObservable } // auto install diff --git a/src/methods/eventToObservable.js b/src/methods/eventToObservable.js new file mode 100644 index 0000000..392d193 --- /dev/null +++ b/src/methods/eventToObservable.js @@ -0,0 +1,28 @@ +import { Rx, hasRx } from '../util' + +/** + * @see {@link https://vuejs.org/v2/api/#vm-on} + * @param {String||Array} evtName Event name + * @return {Observable} Event stream + */ +export default function eventToObservable (evtName) { + if (!hasRx()) { + return + } + const vm = this + let evtNames = Array.isArray(evtName) ? evtName : [evtName] + const obs$ = Rx.Observable.create(observer => { + let eventPairs = evtNames.map(name =>{ + let callback = msg => observer.next({name, msg}) + vm.$on(name, callback) + return {name, callback} + }) + return () => { + //Only remove the specific callback + eventPairs.forEach(pair => vm.$off(pair.name, pair.callback)) + } + }) + + ;(vm._obSubscriptions || (vm._obSubscriptions = [])).push(obs$) + return obs$ +} diff --git a/test/test.js b/test/test.js index 6e6bb40..9c764f9 100644 --- a/test/test.js +++ b/test/test.js @@ -284,3 +284,43 @@ test('$subscribeTo()', () => { next(2) expect(results).toEqual([1]) // should not trigger anymore }) + + +test('$eventToObservable()', done => { + let calls = 0; + const vm = new Vue({ + created(){ + let ob = this.$eventToObservable('ping') + .subscribe(function (event) { + expect(event.name).toEqual('ping'); + expect(event.msg).toEqual('ping message'); + calls++ + }); + } + }); + vm.$emit('ping','ping message'); + + nextTick(()=>{ + vm.$destroy(); + //Should not emit + vm.$emit('pong','pong message'); + expect(calls).toEqual(1); + done() + }); +}); + + +test('$eventToObservable() with lifecycle hooks', done => { + const vm = new Vue({ + created(){ + this.$eventToObservable('hook:beforeDestroy') + .subscribe(function (event) { + console.log(event); + done(event) + }); + } + }); + nextTick(()=>{ + vm.$destroy() + }) +}); From a1dfa820eecb1f6a3acdc3eba1b4eaa8041add6b Mon Sep 17 00:00:00 2001 From: wangxiao Date: Mon, 5 Jun 2017 19:11:50 +0800 Subject: [PATCH 2/5] .take(1) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a94b85d..15cc05d 100644 --- a/README.md +++ b/README.md @@ -202,7 +202,7 @@ this.$eventToObservable('customEvent') .take(1) // Another way to auto unsub: -let beforeDestroy$ = this.$eventToObservable('hook:beforeDestroy') +let beforeDestroy$ = this.$eventToObservable('hook:beforeDestroy').take(1) this.$eventToObservable('customEvent') .takeUntil(beforeDestroy$) ``` From 22a4372ee0a4e372b601a964bf9ca80064e9e7c8 Mon Sep 17 00:00:00 2001 From: regou Date: Mon, 5 Jun 2017 21:14:50 +0800 Subject: [PATCH 3/5] better example --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 15cc05d..8d05c2b 100644 --- a/README.md +++ b/README.md @@ -203,7 +203,7 @@ this.$eventToObservable('customEvent') // Another way to auto unsub: let beforeDestroy$ = this.$eventToObservable('hook:beforeDestroy').take(1) -this.$eventToObservable('customEvent') +Rx.Observable.interval(500) .takeUntil(beforeDestroy$) ``` From 1649ef28d1d8537892cf4d60b908a2d96bdad95a Mon Sep 17 00:00:00 2001 From: regou Date: Fri, 9 Jun 2017 23:15:46 +0800 Subject: [PATCH 4/5] custom event support --- example/counter-simple.html | 17 +++++++++--- src/directives/stream.js | 46 +++++++++++++++++++------------- src/methods/eventToObservable.js | 4 +-- test/test.js | 1 - 4 files changed, 44 insertions(+), 24 deletions(-) diff --git a/example/counter-simple.html b/example/counter-simple.html index 75b981c..c996d52 100644 --- a/example/counter-simple.html +++ b/example/counter-simple.html @@ -6,20 +6,31 @@
{{ count }}
+ + \ No newline at end of file diff --git a/src/directives/stream.js b/src/directives/stream.js index 8a92f46..47417ad 100644 --- a/src/directives/stream.js +++ b/src/directives/stream.js @@ -11,15 +11,6 @@ export default { const event = binding.arg const streamName = binding.expression - if (!Rx.Observable.fromEvent) { - warn( - `No 'fromEvent' method on Observable class. ` + - `v-stream directive requires Rx.Observable.fromEvent method. ` + - `Try import 'rxjs/add/observable/fromEvent' for ${streamName}`, - vnode.context - ) - return - } if (isSubject(handle)) { handle = { subject: handle } } else if (!handle || !isSubject(handle.subject)) { @@ -34,17 +25,36 @@ export default { const subject = handle.subject const next = (subject.next || subject.onNext).bind(subject) - let fromEventArgs = handle.options ? [el, event, handle.options] : [el, event] - handle.subscription = Rx.Observable.fromEvent(...fromEventArgs).subscribe(e => { - next({ - event: e, - data: handle.data + + if (vnode.componentInstance) { + handle.subscription = vnode.componentInstance.$eventToObservable(event).subscribe(e => { + next({ + event: e, + data: handle.data + }) + }) + } else { + if (!Rx.Observable.fromEvent) { + warn( + `No 'fromEvent' method on Observable class. ` + + `v-stream directive requires Rx.Observable.fromEvent method. ` + + `Try import 'rxjs/add/observable/fromEvent' for ${streamName}`, + vnode.context + ) + return + } + let fromEventArgs = handle.options ? [el, event, handle.options] : [el, event] + handle.subscription = Rx.Observable.fromEvent(...fromEventArgs).subscribe(e => { + next({ + event: e, + data: handle.data + }) }) - }) - // store handle on element with a unique key for identifying - // multiple v-stream directives on the same node - ;(el._rxHandles || (el._rxHandles = {}))[getKey(binding)] = handle + // store handle on element with a unique key for identifying + // multiple v-stream directives on the same node + ;(el._rxHandles || (el._rxHandles = {}))[getKey(binding)] = handle + } }, update (el, binding) { diff --git a/src/methods/eventToObservable.js b/src/methods/eventToObservable.js index 392d193..4fefbae 100644 --- a/src/methods/eventToObservable.js +++ b/src/methods/eventToObservable.js @@ -12,13 +12,13 @@ export default function eventToObservable (evtName) { const vm = this let evtNames = Array.isArray(evtName) ? evtName : [evtName] const obs$ = Rx.Observable.create(observer => { - let eventPairs = evtNames.map(name =>{ + let eventPairs = evtNames.map(name => { let callback = msg => observer.next({name, msg}) vm.$on(name, callback) return {name, callback} }) return () => { - //Only remove the specific callback + // Only remove the specific callback eventPairs.forEach(pair => vm.$off(pair.name, pair.callback)) } }) diff --git a/test/test.js b/test/test.js index 9c764f9..f09eb4b 100644 --- a/test/test.js +++ b/test/test.js @@ -315,7 +315,6 @@ test('$eventToObservable() with lifecycle hooks', done => { created(){ this.$eventToObservable('hook:beforeDestroy') .subscribe(function (event) { - console.log(event); done(event) }); } From 911879a91421f30ec2998758e578556335cfa225 Mon Sep 17 00:00:00 2001 From: regou Date: Fri, 9 Jun 2017 23:39:35 +0800 Subject: [PATCH 5/5] Fix : Only subscriptions can be unsubed --- src/methods/eventToObservable.js | 1 - src/methods/fromDOMEvent.js | 1 - src/methods/watchAsObservable.js | 1 - 3 files changed, 3 deletions(-) diff --git a/src/methods/eventToObservable.js b/src/methods/eventToObservable.js index 4fefbae..37b1c67 100644 --- a/src/methods/eventToObservable.js +++ b/src/methods/eventToObservable.js @@ -23,6 +23,5 @@ export default function eventToObservable (evtName) { } }) - ;(vm._obSubscriptions || (vm._obSubscriptions = [])).push(obs$) return obs$ } diff --git a/src/methods/fromDOMEvent.js b/src/methods/fromDOMEvent.js index f862084..271db0e 100644 --- a/src/methods/fromDOMEvent.js +++ b/src/methods/fromDOMEvent.js @@ -27,6 +27,5 @@ export default function fromDOMEvent (selector, event) { }) }) - ;(vm._obSubscriptions || (vm._obSubscriptions = [])).push(obs$) return obs$ } diff --git a/src/methods/watchAsObservable.js b/src/methods/watchAsObservable.js index 778d65c..cdf4a14 100644 --- a/src/methods/watchAsObservable.js +++ b/src/methods/watchAsObservable.js @@ -29,6 +29,5 @@ export default function watchAsObservable (expOrFn, options) { }) }) - ;(vm._obSubscriptions || (vm._obSubscriptions = [])).push(obs$) return obs$ }