Vraag Vuex-weergavegegevens die worden opgehaald uit REST API


Voor zo'n component

<template>
    <div>
        <router-link :to="{name:'section', params: { sectionId: firstSectionId }}">Start</router-link>
    </div>
</template>

<script lang="ts">
    import { mapActions } from "vuex"

    export default {
        mounted() {
            this.getSectionId()
        },
        computed: {
            firstSectionId() {
                return this.$store.state.firstSectionId
            }
        },
        methods: mapActions(["getSectionId"])
    }
</script>

Op te slaan:

const store: any = new Vuex.Store({
    state: {
        firstSectionId: null
    },
    // actions,
    // mutations
})

Ik heb een webverzoek in de getSectionId actie en het asynchroon ophalen van gegevens en roept een mutatie op die zal vullen firstSectionId in staat. Tijdens initiële render firstSectionId is null en ik krijg de waarschuwing dat de vereiste parameter ontbreekt tijdens het renderen van router-link.

Het is hier geen probleem om toe te voegen v-if="firstSectionId". Maar wat is in het algemeen de aanpak voor het ophalen van gegevens van een server voor weergave? Op dit moment controleren al mijn componenten of er in de winkel gegevens aanwezig zijn voordat ze worden gerenderd, is het normaal of is er een betere manier om te wachten op het laden van gegevens voordat deze wordt weergegeven?


16
2018-01-12 09:16


oorsprong


antwoorden:


Eén benadering voor het asynchroon ophalen van gegevens is om te gebruiken belofte in vuex store acties.

Vue.http.get(API_URL)
.then((response) => {
  //use response object     
})
.catch((error => {
    console.log(error.statusText)
}))

Om aan te tonen dat ik een verzoek doe aan deze route. U kunt zien hoe de respons eruit zou moeten zien. Laten we het antwoordobject opslaan in de array state.users.

store.js

const store = new Vuex.Store({
  state: {
    users: []
  },  
  mutations: {
    FETCH_USERS(state, users) {
        state.users = users
    }
  },
  actions: {
    fetchUsers({ commit }, { self })  {         
        Vue.http.get("https://jsonplaceholder.typicode.com/users")
        .then((response) => {
            commit("FETCH_USERS", response.body);
            self.filterUsers(); 
        })
        .catch((error => {
            console.log(error.statusText)
        }))
    }
  }
})

export default store

Je hebt gemerkt dat dat zo is self.filteruser() methode na commit. Dat is cruciaal moment. Daarvoor zijn we dat ook het plegen van een mutatie, wat een synchrone operatie is en we zijn er zeker van dat we onze respons in store.state zullen hebben die gebruikt kan worden in filterUsers() methode (vergeet niet om zelf parm door te geven)

Users.vue

import store from "../store/store"

export default {
  name: 'users',
  created() {
    this.$store.dispatch("fetchUsers", { self: this })       
  },
  methods:{
    filterUsers() {
      //do something with users
       console.log("Users--->",this.$store.state.users)       
    }
  }
}

Betere manieren (ES6 & ES7)

ES6 Beloftes voor asynchroon programmeren

//User.vue
created() {
  this.$store.dispatch("fetchUser").then(() => {
    console.log("This would be printed after dispatch!!")
   })
}

//store.js
actions: {
    fetchUser({ commit }) {
        return new Promise((resolve, reject) => {
            Vue.http.get("https://jsonplaceholder.typicode.com/users")
            .then((response) => {
                commit("FETCH_USERS", response.body);
                resolve();
            })
            .catch((error => {
                console.log(error.statusText);
            }));
        });
    }
}

ES7: async / wachten 

Om weg te geraken van callback hell, en om asynchroon programmeergebruik te verbeteren async functie, en dat kan await op een belofte. Code lijkt veel gemakkelijker te volgen (zoals synchroon), maar code is niet leesbaar voor browsers, dus je hebt Babel-transponder nodig om het uit te voeren.

actions: {
  async actionA ({ commit }) {
    commit('gotData', await getData())
  },
  async actionB ({ dispatch, commit }) {
    await dispatch('actionA') // wait for actionA to finish
    commit('gotOtherData', await getOtherData())
  }
}

39
2018-01-19 17:40



In mijn ervaring kun je een paar controles overslaan als je de status opgeeft met een lege waarde van hetzelfde type als het verwachte resultaat (als je natuurlijk weet wat je kunt verwachten), bijvoorbeeld als je een reeks items hebt, begin dan met [] in plaats van null omdat het niet zal breken v-for richtlijnen .length controles en vergelijkbare toegangspogingen met gegevens.

Maar over het algemeen, toe te voegen v-if is heel normaal om te doen. er is een sectie hierover in de vue-router documentatie en controleren of eigenschappen al dan niet bestaan, is precies wat het suggereert. Een andere mogelijke oplossing die het noemt, is het binnenhalen van gegevens beforeRouteEnter bewaker, wat verzekert dat u altijd bij het onderdeel zult komen met uw gegevens al beschikbaar.

Uiteindelijk zijn beide oplossingen correct en is de beslissing daartussen meer een UX / UI-vraag.


1
2018-01-14 15:38



Ik had vergelijkbare vereisten voor locaties en de api van Google Maps. Ik moest mijn locaties ophalen van de API, deze in een lijst laden en die vervolgens in een kaartonderdeel gebruiken om de markeringen te maken. Ik haalde de gegevens in een Vuex-actie met axios, laadde die in mijn staat met een mutatie en gebruikte toen een getter om de resulterende array op te halen in de gemonteerde levenscyclushaak. Dit resulteerde in een lege array zoals geactiveerd voordat de async-actie werd opgelost.

Ik heb store.subscribe gebruikt om het op deze manier op te lossen:

<template>
  <div class="google-map" :id="mapName"></div>
</template>

<script>
import GoogleMapsLoader from 'google-maps';
import { mapGetters } from 'vuex';

export default {
  name: 'google-map',
  props: ['name'],
  computed: {
    ...mapGetters({
      locations: 'locations/locations',
    }),
  },
  data() {
    return {
      mapName: `${this.name}-map`,
    };
  },
  mounted() {
    this.$store.subscribe((mutation, state) => {      
      if (mutation.type === 'locations/SAVE_LOCATIONS') {
        GoogleMapsLoader.KEY = 'myKey';
        GoogleMapsLoader.load((google) => {
          /* eslint-disable no-new */
          const map = new google.maps.Map(document.getElementById('locations-map'));

          // loop through locations and add markers to map and set map boundaries
          const bounds = new google.maps.LatLngBounds();

          // I access the resulting locations array via state.module.property
          state.locations.locations.forEach((location) => {
            new google.maps.Marker({
              position: {
                lat: location.latitude,
                lng: location.longitude,
              },
              map,
            });
            bounds.extend({
              lat: location.latitude,
              lng: location.longitude,
            });
          });

          map.fitBounds(bounds);
        });
      }
    });
  },
};

0
2018-04-21 02:29