diff --git a/README.md b/README.md
index bc8752a..97dac29 100644
--- a/README.md
+++ b/README.md
@@ -241,6 +241,36 @@ var vm = new Vue({
})
```
+#### `$createObservableMethod(methodName)`
+
+> This feature requires RxJS.
+
+Convert function calls to observable sequence which emits the call arguments.
+
+This is a prototype method added to instances. Use it to create a shared hot observable from a function name. The function will assigned as a vm method.
+
+```html
+
+```
+``` js
+var vm = new Vue({
+ subscriptions () {
+ return {
+ // requires `share` operator
+ formData: this.$createObservableMethod('submitHandler')
+ }
+ }
+})
+```
+Or, use the `observableMethods` convenience option:
+``` js
+new Vue({
+ observableMethods: { submitHandler:'submitHandler$' }
+})
+```
+[example](https://github.com/vuejs/vue-rx/blob/master/example/counter-function.html)
+
+
### Caveats
You cannot use the `watch` option to watch subscriptions, because it is processed before the subscriptions are set up. But you can use `$watch` in the `created` hook instead.
diff --git a/example/counter-function.html b/example/counter-function.html
new file mode 100644
index 0000000..6b5a06e
--- /dev/null
+++ b/example/counter-function.html
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
{{ count }}
+
+
+
+
+
+
+
{{ $data }}
+
+
+
+
diff --git a/package.json b/package.json
index fe71d31..a9e5132 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "vue-rx",
- "version": "3.2.0",
+ "version": "3.3.0",
"description": "RxJS bindings for Vue",
"main": "dist/vue-rx.js",
"files": [
diff --git a/src/index.js b/src/index.js
index 461a93d..7919dcf 100644
--- a/src/index.js
+++ b/src/index.js
@@ -7,6 +7,7 @@ import watchAsObservable from './methods/watchAsObservable'
import fromDOMEvent from './methods/fromDOMEvent'
import subscribeTo from './methods/subscribeTo'
import eventToObservable from './methods/eventToObservable'
+import createObservableMethod from './methods/createObservableMethod'
export default function VueRx (Vue, Rx) {
install(Vue, Rx)
@@ -16,6 +17,7 @@ export default function VueRx (Vue, Rx) {
Vue.prototype.$fromDOMEvent = fromDOMEvent
Vue.prototype.$subscribeTo = subscribeTo
Vue.prototype.$eventToObservable = eventToObservable
+ Vue.prototype.$createObservableMethod = createObservableMethod
}
// auto install
diff --git a/src/methods/createObservableMethod.js b/src/methods/createObservableMethod.js
new file mode 100644
index 0000000..c4ee27b
--- /dev/null
+++ b/src/methods/createObservableMethod.js
@@ -0,0 +1,56 @@
+import { Rx, hasRx, warn } from '../util'
+
+/**
+ * @name Vue.prototype.$createObservableMethod
+ * @description Creates an observable from a given function name.
+ * @param {String} methodName Function name
+ * @param {Boolean} [passContext] Append the call context at the end of emit data?
+ * @return {Observable} Hot stream
+ */
+export default function createObservableMethod (methodName, passContext) {
+ if (!hasRx()) {
+ return
+ }
+ const vm = this
+
+ if (!Rx.Observable.prototype.share) {
+ warn(
+ `No 'share' operator. ` +
+ `$createObservableMethod returns a shared hot observable. ` +
+ `Try import 'rxjs/add/operator/share' for creating ${methodName}`,
+ vm
+ )
+ return
+ }
+
+ if (vm[methodName] !== undefined) {
+ warn(
+ 'Potential bug: ' +
+ `Method ${methodName} already defined on vm and has been overwritten by $createObservableMethod.` +
+ String(vm[methodName]),
+ vm
+ )
+ }
+
+ const creator = function (observer) {
+ vm[methodName] = function () {
+ const args = Array.from(arguments)
+ if (passContext) {
+ args.push(this)
+ observer.next(args)
+ } else {
+ if (args.length <= 1) {
+ observer.next(args[0])
+ } else {
+ observer.next(args)
+ }
+ }
+ }
+ return function () {
+ delete vm[methodName]
+ }
+ }
+
+ // Must be a hot stream otherwise function context may overwrite over and over again
+ return Rx.Observable.create(creator).share()
+}
diff --git a/src/mixin.js b/src/mixin.js
index 3363976..e8282c4 100644
--- a/src/mixin.js
+++ b/src/mixin.js
@@ -14,6 +14,19 @@ export default {
}
}
+ const observableMethods = vm.$options.observableMethods
+ if (observableMethods) {
+ if (Array.isArray(observableMethods)) {
+ observableMethods.forEach(methodName => {
+ vm[ methodName + '$' ] = vm.$createObservableMethod(methodName)
+ })
+ } else {
+ Object.keys(observableMethods).forEach(methodName => {
+ vm[observableMethods[methodName]] = vm.$createObservableMethod(methodName)
+ })
+ }
+ }
+
let obs = vm.$options.subscriptions
if (typeof obs === 'function') {
obs = obs.call(vm)
diff --git a/test/test.js b/test/test.js
index 8ea674c..7a3a7e2 100644
--- a/test/test.js
+++ b/test/test.js
@@ -10,6 +10,7 @@ const Observable = require('rxjs/Observable').Observable
const Subject = require('rxjs/Subject').Subject
const Subscription = require('rxjs/Subscription').Subscription
require('rxjs/add/observable/fromEvent')
+require('rxjs/add/operator/share')
// user
require('rxjs/add/operator/map')
@@ -316,3 +317,69 @@ test('$eventToObservable() with lifecycle hooks', done => {
vm.$destroy()
})
})
+
+test('$createObservableMethod() with no context', done => {
+ const vm = new Vue({
+ created () {
+ this.$createObservableMethod('add')
+ .subscribe(function (param) {
+ expect(param).toEqual('hola')
+ done(param)
+ })
+ }
+ })
+ nextTick(() => {
+ vm.add('hola')
+ })
+})
+
+test('$createObservableMethod() with muli params & context', done => {
+ const vm = new Vue({
+ created () {
+ this.$createObservableMethod('add', true)
+ .subscribe(function (param) {
+ expect(param[0]).toEqual('hola')
+ expect(param[1]).toEqual('mundo')
+ expect(param[2]).toEqual(vm)
+ done(param)
+ })
+ }
+ })
+ nextTick(() => {
+ vm.add('hola', 'mundo')
+ })
+})
+
+test('observableMethods mixin', done => {
+ const vm = new Vue({
+ observableMethods: ['add'],
+ created () {
+ this.add$
+ .subscribe(function (param) {
+ expect(param[0]).toEqual('Qué')
+ expect(param[1]).toEqual('tal')
+ done(param)
+ })
+ }
+ })
+ nextTick(() => {
+ vm.add('Qué', 'tal')
+ })
+})
+
+test('observableMethods mixin', done => {
+ const vm = new Vue({
+ observableMethods: { 'add': 'plus$' },
+ created () {
+ this.plus$
+ .subscribe(function (param) {
+ expect(param[0]).toEqual('Qué')
+ expect(param[1]).toEqual('tal')
+ done(param)
+ })
+ }
+ })
+ nextTick(() => {
+ vm.add('Qué', 'tal')
+ })
+})