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))
}