Skip to content

Commit 38bdd32

Browse files
committed
support separate data + docs
1 parent 1d5e85b commit 38bdd32

File tree

5 files changed

+185
-66
lines changed

5 files changed

+185
-66
lines changed

README.md

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,14 @@ import Vue from 'vue'
2828
import VueRx from 'vue-rx'
2929
import { Observable } from 'rxjs/Observable'
3030
import { Subscription } from 'rxjs/Subscription' // Disposable if using RxJS4
31+
import { Subject } from 'rxjs/Subject' // required for domStreams option
3132

3233
// tada!
33-
Vue.use(VueRx, { Observable, Subscription })
34+
Vue.use(VueRx, {
35+
Observable,
36+
Subscription,
37+
Subject
38+
})
3439
```
3540

3641
#### Global Script
@@ -78,6 +83,64 @@ var vm = new Vue({
7883
vm.$observables.msg.subscribe(msg => console.log(msg))
7984
```
8085

86+
### `v-stream`: Streaming DOM Events
87+
88+
> New in 3.0
89+
90+
> This feature requires RxJS.
91+
92+
`vue-rx` provides the `v-stream` directive which allows you to stream DOM events to an Rx Subject. The syntax is similar to `v-on` where the directive argument is the event name, and the binding value is the target Rx Subject.
93+
94+
``` html
95+
<button v-stream:click="plus$">+</button>
96+
```
97+
98+
Note that you need to declare `plus$` as an instance of `Rx.Subject` on the vm instance before the render happens, just like you need to declare data. You can do that right in the `subscriptions` function:
99+
100+
``` js
101+
new Vue({
102+
subscriptions () {
103+
// declare the receiving Subjects
104+
this.plus$ = new Rx.Subject()
105+
// ...then create subscriptions using the Subjects as source stream.
106+
// the source stream emits in the form of { event: HTMLEvent, data?: any }
107+
return {
108+
count: this.plus$.map(() => 1)
109+
.startWith(0)
110+
.scan((total, change) => total + change)
111+
}
112+
}
113+
})
114+
```
115+
116+
Or, use the `domStreams` convenience option:
117+
118+
``` js
119+
new Vue({
120+
// requires `Rx` passed to Vue.use() to expose `Subject`
121+
domStreams: ['plus$'],
122+
subscriptions () {
123+
// use this.plus$
124+
}
125+
})
126+
```
127+
128+
Finally, you can pass additional data to the stream using the alternative syntax:
129+
130+
``` html
131+
<button v-stream:click="{ subject: plus$, data: someData }">+</button>
132+
```
133+
134+
This is useful when you need to pass along temporary variables like `v-for` iterators. You can get the data by simply plucking it from the source stream:
135+
136+
``` js
137+
const plusData$ = this.plus$.pluck('data')
138+
```
139+
140+
See [example](https://github.com/vuejs/vue-rx/blob/master/example/counter.html) for actual usage.
141+
142+
### Other API Methods
143+
81144
#### `$watchAsObservable(expOrFn, [options])`
82145

83146
> This feature requires RxJS.
@@ -126,6 +189,8 @@ var vm = new Vue({
126189

127190
#### `$fromDOMEvent(selector, event)`
128191

192+
> Deprecated in 3.0+. Prefer `v-stream` instead.
193+
129194
> This feature requires RxJS.
130195
131196
This is a prototype method added to instances. Use it to create an observable from DOM events within the instances' element. This is similar to `Rx.Observable.fromEvent`, but usable inside the `subscriptions` function even before the DOM is actually rendered.

example/counter-simple.html

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<script src="https://unpkg.com/@reactivex/rxjs/dist/global/Rx.js"></script>
2+
<script src="https://unpkg.com/vue/dist/vue.js"></script>
3+
<script src="../vue-rx.js"></script>
4+
5+
<div id="app">
6+
<div>{{ count }}</div>
7+
<button v-stream:click="plus$">+</button>
8+
<button v-stream:click="minus$">-</button>
9+
</div>
10+
11+
<script>
12+
new Vue({
13+
el: '#app',
14+
15+
// declare dom stream Subjects
16+
domStreams: ['plus$', 'minus$'],
17+
18+
subscriptions () {
19+
var plus$ = this.plus$.map(() => 1)
20+
var minus$ = this.minus$.map(() => -1)
21+
var count$ = Rx.Observable
22+
.merge(plus$, minus$)
23+
.startWith(0)
24+
.scan((total, change) => total + change)
25+
26+
return {
27+
count: count$
28+
}
29+
}
30+
})
31+
</script>

example/counter.html

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,52 @@
1+
<!-- this demo requires a browser that natively supports ES2015 -->
2+
13
<script src="https://unpkg.com/@reactivex/rxjs/dist/global/Rx.js"></script>
24
<script src="https://unpkg.com/vue/dist/vue.js"></script>
35
<script src="../vue-rx.js"></script>
46

57
<div id="app">
6-
{{ count }}
7-
<button class="plus">+</button>
8-
<button class="minus">-</button>
8+
<div>{{ count }}</div>
9+
10+
<!-- simple usage -->
11+
<button v-stream:click="plus$">Add on Click</button>
12+
13+
<!-- you can also stream to the same subject with different events/data -->
14+
<button
15+
v-stream:click="{ subject: minus$, data: minusDelta1 }"
16+
v-stream:mousemove="{ subject: minus$, data: minusDelta2 }">
17+
Minus on Click &amp; Mousemove
18+
</button>
19+
20+
<pre>{{ $data }}</pre>
921
</div>
1022

1123
<script>
1224
new Vue({
1325
el: '#app',
26+
27+
data () {
28+
return {
29+
minusDelta1: -1,
30+
minusDelta2: -1
31+
}
32+
},
33+
34+
created () {
35+
//Speed up mousemove minus delta after 5s
36+
setTimeout(() => {
37+
this.minusDelta2 = -5
38+
}, 5000)
39+
},
40+
41+
// declare dom stream Subjects
42+
domStreams: ['plus$', 'minus$'],
43+
1444
subscriptions () {
15-
var plus$ = this.$fromDOMEvent('.plus', 'click').map(e => 1)
16-
var minus$ = this.$fromDOMEvent('.minus', 'click').map(e => -1)
17-
var count$ = Rx.Observable.merge(plus$, minus$)
45+
var count$ = Rx.Observable
46+
.merge(
47+
this.plus$.map(() => 1),
48+
this.minus$.pluck('data')
49+
)
1850
.startWith(0)
1951
.scan((total, change) => total + change)
2052

example/couter-with-directive.html

Lines changed: 0 additions & 43 deletions
This file was deleted.

vue-rx.js

Lines changed: 50 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
}
2727

2828
function unsub (handle) {
29-
if (!handle) { return }
29+
if (!handle) return
3030
if (handle.dispose) {
3131
handle.dispose()
3232
} else if (handle.unsubscribe) {
@@ -50,14 +50,22 @@
5050
}
5151
}
5252

53+
function getKey (binding) {
54+
return [binding.arg].concat(Object.keys(binding.modifiers)).join(':')
55+
}
56+
5357
Vue.mixin({
5458
created: function init () {
5559
var vm = this
56-
var domStreams = vm.$option.domStreams
60+
var domStreams = vm.$options.domStreams
5761
if (domStreams) {
58-
domStreams.forEach(function (key) {
59-
vm[key] = new Rx.Subject()
60-
})
62+
if (!Rx.Subject) {
63+
warn('Rx.Subject is required to use the "domStreams" option.')
64+
} else {
65+
domStreams.forEach(function (key) {
66+
vm[key] = new Rx.Subject()
67+
})
68+
}
6169
}
6270

6371
var obs = vm.$options.subscriptions
@@ -83,6 +91,7 @@
8391
})
8492
}
8593
},
94+
8695
beforeDestroy: function () {
8796
if (this._obSubscriptions) {
8897
this._obSubscriptions.forEach(unsub)
@@ -165,26 +174,51 @@
165174
if (!hasRx()) {
166175
return
167176
}
177+
178+
var handle = binding.value
168179
var event = binding.arg
169-
var stream = binding.value
170180
var streamName = binding.expression
171181

172-
if (isSubject(stream)) {
173-
var onNext = (stream.next || stream.onNext).bind(stream) // Rx4 Rx5
174-
el._ob$ = Rx.Observable.fromEvent(el, event).subscribe(function (evt) {
175-
onNext({ event: evt })
176-
})
177-
} else {
182+
if (isSubject(handle)) {
183+
handle = { subject: handle }
184+
} else if (!handle || !isSubject(handle.subject)) {
178185
warn(
179186
'Invalid Subject found in directive with key "' + streamName + '".' +
180-
streamName + ' should be an instance of Rx.Subject',
187+
streamName + ' should be an instance of Rx.Subject or have the ' +
188+
'type { subject: Rx.Subject, data: any }.',
181189
vnode.context
182190
)
191+
return
192+
}
193+
194+
var subject = handle.subject
195+
var next = (subject.next || subject.onNext).bind(subject)
196+
handle.subscription = Rx.Observable.fromEvent(el, event).subscribe(function (e) {
197+
next({
198+
event: e,
199+
data: handle.data
200+
})
201+
})
202+
203+
// store handle on element with a unique key for identifying
204+
// multiple v-stream directives on the same node
205+
;(el._rxHandles || (el._rxHandles = {}))[getKey(binding)] = handle
206+
},
207+
208+
update: function (el, binding) {
209+
var handle = binding.value
210+
var _handle = el._rxHandles && el._rxHandles[getKey(binding)]
211+
if (_handle && handle && isSubject(handle.subject)) {
212+
_handle.data = handle.data
183213
}
184214
},
185-
unbind: function (el, binding, vnode) {
186-
if (el._ob$) {
187-
unsub(el._ob$)
215+
216+
unbind: function (el, binding) {
217+
var key = getKey(binding)
218+
var handle = el._rxHandles && el._rxHandles[key]
219+
if (handle) {
220+
unsub(handle.subscription)
221+
el._rxHandles[key] = null
188222
}
189223
}
190224
})

0 commit comments

Comments
 (0)