A simplified approach to using streams with Vue.
npm i vue-streams rxjs@rc
rxjs v6 is recommended (currently in Release Candidate)
Install as a plugin:
import VueStreams from "vue-streams"
import { Subject, BehaviorSubject } from "rxjs"
Vue.use(VueStreams, { Subject, BehaviorSubject })
<template>
<div>
<button @click="click$">Click me</button>
{{random$}}
</div>
</template>
<script>
import { fromMethod } from "vue-streams";
import { map } from "rxjs/operators";
export default {
sources: {
click$: fromMethod //infer the method name "click$" from the key
},
subscriptions: ({ click$ }) => ({
random$: click$.pipe(map(() => Math.random()))
}) //template subscribes to each key of the returned object
};
</script>
<template>
<div id="demo">
<label>Search <input type="text" v-model="term"></label>
<button @click="term = ''">Clear</button>
<h2 v-if="noResults$">
{{message$}}
</h2>
<transition-group tag="div" name="fade" class="people">
<div v-for="person of people$" :key="person.id">
<h2>{{person.name}}</h2>
<img :src="https://github.com/johnlindquist/vue-streams/raw/master/`${URL}/${person.image}`" alt="">
</div>
</transition-group>
</div>
</template>
<script>
import { fromWatch } from "vue-streams"
import { merge } from "rxjs"
import {
switchMap,
map,
mapTo,
pluck,
partition,
debounceTime,
share
} from "rxjs/operators"
import { ajax } from "rxjs/ajax"
const URL = `https://foamy-closet.glitch.me`
export default {
data() {
return { URL, term: "sky" }
},
sources: {
term$: fromWatch("term")
},
subscriptions: ({ term$ }) => {
const [search$, empty$] = term$.pipe(
debounceTime(250),
partition(term => term.length)
)
const people$ = merge(
search$.pipe(
switchMap(term =>
ajax(`${URL}/people?name_like=${term}`).pipe(
share(),
pluck("response")
)
)
),
empty$.pipe(map(() => []))
)
const noResults$ = people$.pipe(map(result => result.length === 0))
const message$ = merge(
noResults$.pipe(mapTo("No results 😢")),
empty$.pipe(mapTo("Please type something 👍"))
)
return { people$, noResults$, message$ }
}
}
</script>
<style>
#demo {
font-family: "Avenir";
}
.people {
display: flex;
flex-wrap: wrap;
}
.people > * {
padding: 0.25rem;
}
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.5s;
}
.fade-enter,
.fade-leave-to {
opacity: 0;
}
</style>
<template>
<div id="demo" @mousemove="x = $event.x">
<h3>{{x$}}</h3>
<button @click="one$">One</button>
<button @click="two">Two</button>
<button @click="load$">Load Random</button>
<input type="text" v-model="text">
<button v-on:click="show = !show">
Toggle
</button>
<transition name="fade" @enter="enter$">
<p v-if="show">hello</p>
</transition>
<h2>
{{message$}}
</h2>
<MiniComp v-if="show"></MiniComp>
</div>
</template>
<script>
import { merge, interval } from "rxjs"
import { ajax } from "rxjs/ajax"
import { map, mapTo, switchMap, pluck, throttleTime } from "rxjs/operators"
import { fromMethod, fromWatch, fromEmit } from "vue-streams"
const MiniComp = {
sources: {
mounted$: fromEmit("hook:mounted"),
beforeDestroy$: fromEmit("hook:beforeDestroy")
},
subscriptions: ({ mounted$, beforeDestroy$ }) => {
mounted$.subscribe(value => console.log("hello!"))
beforeDestroy$.subscribe(value => console.log("BYE! 🤪"))
},
render(h) {
return (
<div>
<h2>MiniComp</h2>
</div>
)
}
}
export default {
components: {
MiniComp
},
data() {
return {
show: false,
text: "john"
}
},
sources: {
one$: fromMethod,
two$: fromMethod("two"),
load$: fromMethod,
enter$: fromMethod,
text$: fromWatch("text"),
x: fromWatch
},
subscriptions: ({ one$, two$, load$, enter$, text$, x }) => {
const buttons$ = merge(
one$.pipe(mapTo(1)),
two$.pipe(mapTo(2)),
enter$.pipe(mapTo("fade in..."))
)
const date$ = interval(4000).pipe(map(() => new Date().toString()))
const person$ = load$.pipe(
switchMap(() =>
ajax(
`https://foamy-closet.glitch.me/people/${Math.floor(
Math.random() * 10
)}`
).pipe(pluck("response", "name"))
)
)
const message$ = merge(person$, text$, date$, buttons$)
return {
message$,
x$: x.pipe(throttleTime(100))
}
}
}
</script>
<style>
#demo {
font-family: "Avenir";
}
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.5s;
}
.fade-enter,
.fade-leave-to {
opacity: 0;
}
</style>
.prevent