Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Parametized getter in Vuex - trigger udpate

Tags:

vuejs2

vuex

My Vuex store has a collection of data, say 1,000 records. There is a getter with a parameter, getItem, taking an ID and returning the correct record.

I need components accessing that getter to know when the data is ready (when the asynchronous fetching of all the records is done).

However since it's a parametized getter, Vue isn't watching the state it depends on to know when to update it. What should I do?

I keep wanting to revert to a BehaviorSubject pattern I used in Angular a lot, but Vuex + rxJS seems heavy for this, right?

I feel I need to somehow emit a trigger for the getter to recalculate.

store.js

import Vue from 'vue'
import Vuex from 'vuex'
import VueResource from 'vue-resource'

Vue.use(VueResource);

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    numberOfPosts : -1,
    posts : {}, //dictionary keyed on slug
    postsLoaded : false,
  },
  getters : {
    postsLoaded : function(state){
      return state.postsLoaded;
    },
    totalPosts : function(state){
      return state.numberOfPosts;
    },
    post: function( state ){
      return function(slug){
        if( state.posts.hasOwnProperty( slug ) ){
          return state.posts.slug;
        }else{
          return null;
        }
      }
    }
  },
  mutations: {
    storePosts : function(state, payload){
      state.numberOfPosts = payload.length;
      for( var post of payload ){
        state.posts[ post.slug ] = post;
      }
      state.postsLoaded = true;
    }
  },
  actions: {
    fetchPosts(context) {
      return new Promise((resolve) => {
        Vue.http.get(' {url redacted} ').then((response) => {
          context.commit('storePosts', response.body);
          resolve();
        });
      });
    }
  }
})

Post.vue

 <template>
      <div class="post">
        <h1>This is a post page for {{ $route.params.slug }}</h1>
        <div v-if="!postsLoaded">LOADING</div>
        <div v-if="postNotFound">No matching post was found.</div>
        <div v-if="postsLoaded && !postNotFound" class="post-area">
          {{ this.postData.title.rendered }}
        </div>
      </div>
    </template>

<script>
import { mapGetters } from 'vuex'

export default {
  name: 'post',
  data : function(){
    return {
      loading : true,
      postNotFound : false,
      postData : null
    }
  },
  mounted : function(){
    this.postData = this.post( this.$route.params.slug );
    if ( this.postData == null ){
      this.postNotFound = true;
    }
  },
  computed : mapGetters([
    'postsLoaded',
    'post'
  ])
}
</script>

As it stands, it shows the "post not found" message because when it accesses the getter, the data isn't ready yet. If a post isn't found, I need to distinguish between (a) the data is loaded and there isn't a post that matches, and (b) the data isn't loaded so wait

like image 990
tmdesigned Avatar asked Nov 08 '22 02:11

tmdesigned


1 Answers

I suspect the problem lies with how your are setting the posts array in your storePosts mutation, specifially this line:

state.posts[ post.slug ] = post

VueJs can't track that operation so has no way of knowing that the array has updated, thus your getter is not updated.

Instead your need to use Vue set like this:

Vue.set(state.posts, post.slug, post)

For more info see Change Detection Caveats documentation

like image 187
Felix Eve Avatar answered Nov 15 '22 15:11

Felix Eve