Vuex AJAX example
HTML
<script src="https://unpkg.com/vuex"></script>
<div id="vue">
<div>
<button @click="n--" :disabled="n <= 0">-</button>
<span style="margin:0px 5px">{{n}}</span>
<button @click="n++" :disabled="n >= 807">+</button>
<input :value="$store.state.url" @input="$store.set('url', $event.target.value)">
</div>
<div :class="{item:true, loading:stuff.loading, error:stuff.error}">
<div v-if="stuff.loading" class="big">Loading...</div>
<template v-else>
<div v-if="stuff.error">{{stuff.error.message}}</div>
<div v-else-if="stuff.data.value && stuff.data.value.joke" v-html="stuff.data.value.joke"></div>
<json v-else :data="stuff.data"></json>
<button @click="$store.dispatch('fetchStuff',n)">Refetch</button>
</template>
</div>
<pre><button @click="$store.set('stuff',[])">Reset all</button><b>state:</b>{{$store.state}}</pre>
</div>
SCSS
body{
font-size:18px;
margin:0;
}
#vue{
display:flex;
flex-direction:column;
padding:30px;
box-sizing:border-box;
}
button, input{
border:2px solid grey;
background:inherit;
color:inherit;
border-radius:4px;
padding:5px 10px;
outline:none;
transition:0.2s;
&:hover{
border-color:#5af;
}
&[disabled]{
color:lightgrey;
border-color:lightgrey;
}
}
button{
cursor:pointer;
}
input{
float:right;
width:300px;
}
.item{
background:white;
padding:30px;
margin:30px 0px 60px 0px;
box-shadow:0px 2px 5px rgba(0,0,0,0.5);
transition:0.4s;
min-height:60px;
text-align:center;
flex-shrink:0;
&.loading{
opacity:0.5;
}
&.error div{
color:red;
}
.big{
font-size:2em;
}
button{
margin-top:10px;
}
}
pre{
background:#678;
color:#eee;
padding:20px;
white-space:pre-wrap;
box-shadow:inset 0px 1px 5px rgba(0,0,0,0.5);
margin:0;
position:relative;
button{
position:absolute;
top:20px;
right:20px;
border-color:lightgrey;
}
}
ul.json{
list-style:none;
padding:0;
border:2px solid lightgrey;
border-radius:8px;
background:white;
b{
padding-right:10px;
margin-right:auto;
~ ul{
margin-top:5px;
}
}
li{
padding:10px;
display:flex;
flex-wrap:wrap;
border-bottom:1px solid lightgrey;
&:last-child{
border:none;
}
> ul {
width:100%
}
}
div{
overflow:auto;
}
}
JavaScript
const store = new Vuex.Store({
state: {
url:'https://api.icndb.com/jokes/random?limitTo=[nerdy]',
stuff: []
},
mutations: {
set: (state, [pathString, val]) => {
const path = pathString.split('.')
path.reduce((memo, key, i) => {
if (i + 1 === path.length) {
Vue.set(memo, key, val)
} else if (!memo.hasOwnProperty(key)) {
Vue.set(memo, key, {})
}
return memo[key]
}, state)
}
},
actions: {
fetchStuff(eh, n) {
console.log('actions')
store.set(`stuff.${n}.loading`, true)
store.set(`stuff.${n}.error`, false)
Promise.all([fetch(store.state.url), delay(500)]) //wait at least 500ms, for demo
.then(res => res[0].json())
.then(res => {
store.set(`stuff.${n}.data`, res)
})
.catch(error => {
store.set(`stuff.${n}.error`, error)
})
.then(()=> {
store.set(`stuff.${n}.loading`, false)
})
}
},
getters: {
stuff: state => n => {
console.log(n)
console.log(state)
/* if (!state.stuff[n]) {
store.dispatch('fetchStuff', n)
} */
return state.stuff[n]
}
}
})
store.set = (path, val) => store.commit('set', [path, val]) // lazy :)
new Vue({
el: '#vue',
store: store,
data: {
n: 0
},
created() {
console.log('created')
this.$store.dispatch('fetchStuff', this.n)
},
computed: {
stuff() {
return this.$store.getters.stuff(this.n)
}
},
components:{
json:{
name:'json',
props:['data'],
template:
`<ul class="json" v-if="typeof data == 'object'">
<li v-for="item, key in data">
<b v-if="!Array.isArray(data)">{{key}}</b>
<json :data="item"></json>
</li>
</ul>
<div v-else>{{data}}</div>`
}
}
})
function delay(ms){
return new Promise(resolve => setTimeout(resolve, ms))
}