Working on a Vuejs application whereby I use Vuex for state management between the components.In Vuex store, I have an action that fetches some data from an API (which works fine) then populate it to the state (via a mutation). Next, I pass the updated state to the component using getters.
The problem is there is a problem populating data to the state from the action. In the DOM I have tried fetching via computed property or using the getter but get empty string
Vuex Store
const getDefaultState = () => {
return {
clientDeposit: ''
}
}
//state
const state = getDefaultState();
//getters
const getters = {
getDeposit: (state) => state.clientDeposit
}
//actions
const actions = {
fetchClients({ commit}) {
const clientId ="23"
axios.post('/api/motor/fetchClients', {
ClientId: clientId,
})
.then((response)=> {
//console.log(response); //returns data
const deposit = response.data;
commit('setIpfDeposit', deposit);
})
}
}
//mutations
const mutations = {
setIpfDeposit: (state, value) => (state.clientDeposit = value)
}
export default {
state,
getters,
actions,
mutations
}
Component
<template>
<div>
<button onclick="fetchClients()">Fetch Clients</button>
Deposit (Via computed property) : {{ fetchDeposit }}
Deposit (from getter) : {{ getDeposit }}
</div>
</template>
<script>
import { mapGetters , mapActions } from "vuex";
import axios from "axios";
export default {
name: "",
data() {
return {
}
},
computed: {
...mapGetters([
"getDeposit"
]),
fetchDeposit(){
return this.getDeposit
},
},
methods:{
...mapActions([
"fetchClients"
])
}
};
</script>
<style scoped>
</style>
You need to fetch the data first.
Import mapActions from vuex
import {mapActions, mapGetters} from 'vuex';
Bring in the fetchClients method in your component's methods object
methods:{
... mapActions(['fetchClients']),
}
Then in your component's created life cycle method call the fetchClients method
created(){
this.fetchClients();
}
Related
My project has /store.index.ts. I want to get data from api in actions block. I got data but can't assign data to property in state. I used interface because project has typescript and i used vuexStore. I wanted to work the axios.get in vuex store then i can't assign data to property of state .
/store/index.ts page:
import { createStore } from "vuex";
import axios from "axios";
export interface State {
tasks: any;
}
export default createStore<State> {
state: {
tasks: [],
},
getters: {
getAd(state) {
return state.tasks;
},
},
mutations: {
setTasks: (state, tasks) => {
state.tasks = tasks;
},
},
actions: {
async fetchProfile({ commit }) {
const response = await axios.get(
"https://5dd471358b5e080014dc51d2.mockapi.io/users/"
);
commit("setTasks", response.data);
return response.data;
},
},
modules: {},
};
newcomponent.vue page:
<template>
<div>
<h1>B12 Interactive</h1>
<div>
<h2>Skill Rating System</h2>
</div>
<div><input placeholder="Person/App/Rate" /></div>
<div>
<div class="vl"></div>
<hr />
<div class="v2"></div>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent, ref } from "vue";
import { useStore } from "vuex";
export default defineComponent({
setup() {
const store = useStore();
const test = ref(
store.dispatch("fetchProfile")
); /*i can get data this line but
can't use with v:bind:key="item.id" in template block as v-for loop.This line is working in console.log() Array.length is higher than zero */
console.log(
"store:",
store.state.tasks
); /*i can't get data because data can't
assign to property in state. this line isn't working in console.log() Array.length is zero */
return {
test,
};
},
});
</script>
I'm trying to fetch data from an external API and display the data in my component but it is returning an empty array when I actually have data in my API.
Inside my module I have the following:
import axios from 'axios';
const state = {
countries = []
}
const getters = {
allCountries: (state) => state.countries;
}
const actions = {
//Fecth all the countries from the API
async fetchCountries({ commit }) {
const response = await axios.get('URL');
commit('setCountries', response.data);
},
}
const mutations = {
setCountries: (state, countries) => (state.countries = countries),
}
export default {
state,
getters,
actions,
mutations,
};
Component:
<template>
<div v-for="country in allCountries" :key="country.id">
<small>{{country.name}}</small>
</div>
</template>
<script>
import { mapGetters} from 'vuex';
export default{
name: 'CompCountry',
computed: mapGetters(['allCountries'])
}
</script>
You're missing to dispatch the action, to do that you should run it in a life cycle hook like mounted :
<template>
<div v-for="country in allCountries" :key="country.id">
<small>{{country.name}}</small>
</div>
</template>
<script>
import { mapGetters} from 'vuex';
export default{
name: 'CompCountry',
computed:{ ...mapGetters(['allCountries']},
mounted(){
this.$store.dispatch('fetchCountries')
}
}
</script>
I am building an authentication page with Vue.js, Vuex, and AWS Amplify.
This auth page is based off Erik Hanchett's AWS Auth Example (https://github.com/ErikCH/Aws-auth-example/blob/master/src/components/HelloWorld.vue). Erik's original demo utilized Vuex for state management, but for the sake of simplicity only employs the state handler in the store.js file.
I am attempting to reconfigure this demo so that the various methods and hooks in HelloWorld.vue are set up to also dispatch actions and commit mutations.
So far, I have been successful in setting up the findUser() method in HelloWorld.vue to dispatch actions, pass user and signedIn as payloads to their respective action handlers, and then commit mutations.
However, my issue now pertains to the computed property in the HelloWorld component.
Erik's original demo returns the state directly to the component using return this.$store.state.signedIn as seen in the computed property. Based on my experience with Vuex in other projects, I would normally use a mapState helper to map directly to the state.
Is it correct in this project to use this.$store.state.signedIn to return the state? Or should I use mapState? If so, how can I reconfigure this computed property in order to employ mapState to map directly to signedIn?
My code is below:
HelloWorld.vue
<template>
<div class="hello">
<div v-if="!signedIn">
<amplify-authenticator></amplify-authenticator>
</div>
<div v-if="signedIn">
<Home></Home>
</div>
</div>
</template>
<script>
import { Auth } from 'aws-amplify'
import { AmplifyEventBus } from 'aws-amplify-vue';
import { mapState } from 'vuex'
import Home from '../components/Home.vue'
export default {
name: 'HelloWorld',
components: {
Home
},
data() {
return {
login: '',
password: ''
}
},
props: {
msg: String,
},
created(){
this.findUser();
AmplifyEventBus.$on('authState', info => {
if(info === "signedIn") {
this.findUser();
} else {
this.$store.state.signedIn = false;
this.$store.state.user = null;
}
});
},
computed: {
signedIn(){
return this.$store.state.signedIn;
}
},
methods: {
async findUser() {
try {
const user = await Auth.currentAuthenticatedUser();
let signedIn = true
this.$store.dispatch('setUser', user)
this.$store.dispatch('setSignedIn', signedIn)
}
catch(err) {
let signedIn = false
this.$store.dispatch('setSignedIn', signedIn)
}
}
}
}
</script>
Store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
user: null,
signedIn: false
},
mutations: {
setUser(state, user) {
state.user = user
},
setSignedIn(state, signedIn) {
state.signedIn = signedIn
}
},
actions: {
setUser: (context, user) => {
context.commit('setUser', user)
},
setSignedIn: (context, signedIn) => {
context.commit('setSignedIn', signedIn)
}
}
})
Home.vue
<template>
<div class="goodbye">
<h1>HOME</h1><br>
<amplify-sign-out></amplify-sign-out>
</div>
</template>
<script>
import { Auth } from 'aws-amplify'
export default {
name: 'Home',
data() {
return {
login: '',
password: ''
}
},
props: {
msg: String,
},
methods: {
signOut() {
Auth.signOut()
}
}
}
</script>
The mapState helper is just sugar syntax for not repeating multiple times the whole this.$store.state.foo piece of code.
You can certainly use mapState like this
import { mapState } from 'vuex'
computed: mapState([
// map this.signedIn to this.$store.state.signedIn
'signedIn'
])
Or like this if you want to also use local properties besides the ones of mapState
import { mapState } from 'vuex'
computed:
localComputed () { /* ... */ },
...mapState([
// map this.signedIn to this.$store.state.signedIn
'signedIn'
])
Here are the docs for more information on this.
I have a simple Vuex module with one object: selectedEvent.
I am able to update my selected event using:
<Event :event="selectedEvent" />
However, I am unable to update the visibility of this property using a computed getter defined in the module:
<Event :event="selectedEvent" v-show="isEventSelected" />
My computed values in App.js:
computed: mapState({
selectedEvent: state => state.events.selectedEvent,
isEventSelected: state => state.events.isEventSelected
})
I'm aware that Vue has trouble observing some Object/ Array changes, so I have used Vue.set in my mutation. I have also attempted to move v-show inside the Event component, with no success.
If I move the getter logic inside the v-show, it works fine (however it's messy), e.g.:
<Event :event="selectedEvent" v-show="selectedEvent.hasOwnProperty('id')" />
I'm fairly new to Vue - What am I missing here?
store/modules/events.js:
import { EVENT_SELECT } from "./types";
import Vue from "vue";
const state = {
selectedEvent: {}
};
const getters = {
selectedEvent: state => {
return state.selectedEvent;
},
isEventSelected: state => {
return state.selectedEvent.hasOwnProperty("id");
}
};
const actions = {
setSelectedEvent({ commit }, selectedEvent) {
commit(EVENT_SELECT, selectedEvent);
}
};
const mutations = {
[EVENT_SELECT](state, selectedEvent) {
Vue.set(state, "selectedEvent", selectedEvent);
}
};
export default {
namespaced: true,
state,
getters,
actions,
mutations
};
App.vue:
<template>
<div id="app">
<b-container>
<Calendar />
<Event :event="selectedEvent" v-show="selectedEvent.hasOwnProperty('id')"/>
</b-container>
</div>
</template>
<script>
import Calendar from "./components/Calendar.vue";
import Event from "./components/Event.vue";
import { mapState } from "vuex";
export default {
name: "app",
components: {
Calendar,
Event
},
computed: mapState({
selectedEvent: state => state.events.selectedEvent,
isEventSelected: state => state.events.isEventSelected
})
};
</script>
In your store, isEventSelected is a getter, not a state property so you should use mapGetters, eg
import { mapState, mapGetters } from 'vuex'
// snip
computed: {
...mapState('events', ['selectedEvent']),
...mapGetters('events', ['isEventSelected'])
}
I'm going crazy, I have a working api that sends data, I connected it to a VueJS app and it was working fine. I'm trying to implement Vuex and I'm stuck. Here's my store.js file
import Vue from 'vue';
import Vuex from 'vuex';
import axios from 'axios'
Vue.use(Vuex);
const state = {
message: "I am groot",
articles: []
}
const getters = {
getArticles: (state) => {
return state.articles;
}
}
const actions = {
getArticles: ({ commit }, data) => {
axios.get('/articles').then( (articles) => {
commit('GET_ARTICLES', articles);
console.log(articles); // Trying to debug
}, (err) => {
console.log(err);
})
}
}
const mutations = {
GET_ARTICLES: (state, {list}) => {
state.articles = list;
}
}
const store = new Vuex.Store({
state,
getters,
mutations,
actions,
mutations
});
console.log(store.state.articles); // this lines works but data is empty
export default store
The console.log within axios call doesn't run and store.state.articles is empty. I must be missing something. I'm just trying to console the articles data on page load...
Please help, I'm near insanity :)
Component :
<template>
<div class="container">
<h1>Test component yo !</h1>
<p>{{ message }}</p>
</div>
</template>
<script>
export default {
name: 'Test',
computed: {
message() {
return this.$store.state.message
}
},
mounted: () => {
this.$store.dispatch('getArticles')
}
}
</script>
App.js :
import Vue from 'vue';
import ArticlesViewer from './articles_viewer.vue';
import UserArticles from './user_articles.vue';
import App from './app.vue'
import store from './store'
new Vue({
el: '#app-container',
store,
render: h => h(App)
})
You define the mounted lifecycle hook of your component using an arrow function.
As per the documentation:
Don’t use arrow functions on an instance property or callback (e.g. vm.$watch('a', newVal => this.myMethod())). As arrow functions are bound to the parent context, this will not be the Vue instance as you’d expect and this.myMethod will be undefined.
You should define it like so:
mounted: function () {
this.$store.dispatch('getArticles');
}
Or, use the ECMAScript 5 shorthand:
mounted() {
this.$store.dispatch('getArticles');
}
Now, your dispatch method will be called correctly, populating your articles array.