Vuex AJAX example
by shamaleyte
HTML
<script src="https://unpkg.com/vuex"></script>
<div id="vue-comm-involvement">
<div>
<input :value="$store.state.url" @input="$store.set('url', $event.target.value)">
</div>
<div :class="{item:true, loading:involvement.loading, error:involvement.error}">
<div v-if="involvement.loading" class="big">Loading...</div>
<template v-else>
<div v-if="involvement.error">{{involvement.error.message}}</div>
<div v-else-if="involvement.data.total && involvement.data.offset" v-html="involvement.data.value.message"></div>
<json v-else :data="involvement.data"></json>
</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.hubapi.com/hubdb/api/v2/tables/847225/rows?portalId=4158429&attorney_name=joseph-s-johnston',
involvement: []
},
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: {
fetchData(eh, n) {
store.set(`involvement.${n}.loading`, true)
store.set(`involvement.${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(`involvement.${n}.data`, res.objects)
})
.catch(error => {
store.set(`involvement.${n}.error`, error)
})
.then(()=> {
store.set(`involvement.${n}.loading`, false)
})
}
},
getters: {
involvement: state => n => {
if (!state.involvement[n]) {
store.dispatch('fetchData', n);
}
console.log("n : " + state.involvement[n]);
return state.involvement[n]
}
}
})
store.set = (path, val) => store.commit('set', [path, val]) // lazy :)
new Vue({
el: '#vue-comm-involvement',
store: store,
data: {
n: 0
},
computed: {
involvement() {
return this.$store.getters.involvement(this.n)
}
},
components:{
json:{
name:'json',
props:['data'],
template:
`<ul class="json" v-if="typeof data == 'object'">
<li v-for="item, key in data">
<ul>
<li v-for="next, key in item.values">{{key}} : {{ next }}</li>
</ul>
</li>
</ul>
<div v-else>{{data}}</div>`
}
}
})
function delay(ms){
return new Promise(resolve => setTimeout(resolve, ms))
}