vuex-aspect subscription example
by frissdiegurke
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Subscription vuex-aspect example</title>
<script type="text/javascript" src="https://unpkg.com/vuex"></script>
<script type="text/javascript" src="https://unpkg.com/vuex-aspect/dist/vuex-aspect.umd.js"></script>
</head>
<body>
<div id="app">
<h2>Vuex Aspect Subscription Example</h2>
<p>
A server subscription push is simulated by a random timeout between 100ms and 2s. UserIDs <code>(x+2)%4 = 0</code>
result in errors.
</p>
<div class="button-group">
<button @click="incrementUser">Next UserID</button>
<button @click="toggleConsumption">{{consume ? "Unmount consumer component" : "Mount consumer component"}}</button>
</div>
<table>
<tbody>
<tr>
<td><pre><code>user$error</code></pre></td>
<td><pre><code>{{user$error || "undefined"}}</code></pre></td>
</tr>
<tr>
<td><pre><code>user</code></pre></td>
<td><pre><code>{{user || "undefined"}}</code></pre></td>
</tr>
<tr>
<td><pre><code>consumer</code></pre></td>
<td><consumer v-if="consume"></consumer></td>
</tr>
</tbody>
</table>
</div>
<script type="text/javascript" src="./script.js"></script>
</body>
</html>
CSS
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
p {
font-size: small;
font-style: italic;
}
p code {
font-size: medium;
}
.button-group {
display: flex;
margin: 1em 0;
}
.button-group > button {
flex-grow: 1;
flex-basis: 50%;
margin: 0 0.5em;
}
table {
width: 100%;
}
tr {
border-bottom: 1px solid #e1e1e1;
}
tr:last-child {
border-bottom: none;
}
td {
width: 50%;
padding: 0.2em 1em;
}
pre {
background: #005fa533;
border: 1px solid #005fa533;
padding: 0.1em 0.2em;
margin: 0.1em 0.2em;
}
Vue
// aspect definition
const myUserAspect = new VuexAspect.Aspect({
variables({state}) { return {id: state.userId}; },
supply({variables, isInitial}) {
isInitial || unsubscribe();
subscribe(variables, (err, data) => { this.inject(data, err); });
},
disable: unsubscribe,
});
// consumer component
const consumer = Vue.component("consumer", {
mixins: [myUserAspect.mixin],
template: "<pre><code>nom nom nom...</code></pre>",
});
// main component
new Vue({
el: "#app",
components: {consumer},
methods: Vuex.mapMutations(["toggleConsumption", "incrementUser"]),
computed: Vuex.mapState(["consume", "userId", "user", "user$error"]),
store: new Vuex.Store({
plugins: [VuexAspect.install],
state: {userId: 0, consume: false},
aspects: {user: myUserAspect},
mutations: {
toggleConsumption(state) { state.consume = !state.consume; },
incrementUser(state) { state.userId = state.userId + 1; },
},
}),
});
// simulated server (while subscribed push one result every 100ms to 2s)
let timeout = null;
let count = 0;
function subscribe(variables, next) {
timeout = setTimeout(response, 100 + Math.random() * 1900);
function response() {
if ((variables.id + 2) % 4 === 0) {
next({count, status: 404, message: "User #" + variables.id + " does not exist."});
} else {
next(null, {count, id: "user-" + variables.id, name: "Jon Doe " + variables.id});
}
count++;
timeout = setTimeout(response, 100 + Math.random() * 1900);
}
}
function unsubscribe() { clearTimeout(timeout); }