I'm getting used to Vuex as I need to be able to have a store that I can easily access and update when another component is changed.
Currently, my store looks like the following:
import vue from 'vue';
import Vuex from 'vuex';
vue.use(Vuex);
export default new Vuex.Store({
state: {
users: {
columns: [],
model: [],
}
},
mutations: {
fetchUsers: function(state) {
axios.get(`/users?search_input=`)
.then(function(response) {
});
}
}
});
The columns and model is dynamically pulled down from an ajax request and my Users looks like the following:
<script>
export default {
mounted() {
this.$store.commit('fetchUsers');
},
computed: {
columns() {
return this.$store.state.users.columns;
}
model() {
return this.$store.state.users.model
}
},
}
</script>
My problem is that the application needs to have data preloaded from Ajax. For example columns are set from an ajax request in fetchUsers and inside the Users I use this.$store.commit('fetchUsers'); but is there an alternative way I can do that without using commit preferably inside the store itself?
In the store u can load ur data asynchronously via actions then commit the changes using a mutation.
import vue from 'vue';
import Vuex from 'vuex';
vue.use(Vuex);
export default new Vuex.Store({
state: {
users: {
columns: [],
model: [],
}
},
actions: {
fetchUsers: function( context ) {
axios.get(`/users?search_input=`)
.then( function( response ) {
context.commit( "FETCHUSERS", {
columns: response.columns,
model: response.model
});
});
}
}
mutations: {
FETCHUSERS: function( state, payload ) {
state.users.columns = payload.columns;
state.users.model = payload.model;
}
}
});
Dispatch the action from the component
<script>
export default {
.....
methods: {
fetchUsers: function() {
this.$store.dispatch( "fetchUsers" );
}
}
}
</script>
Mutations must be synchronous: https://vuex.vuejs.org/en/mutations.html
You should move ajax request into actions, which can be async. You get data in the action, commit mutation giving received data as a payload, and mutation assigns a value to the state property.
Alternatively, you can make async request in component method, and assign a value to the store property directly: this.$store.state.prop = value
Related
When my application is started I make two requests to get some information (company and user informations) that I will use basically in all my components. I store this information in store.js
My App.vue
<template>
<router-view></router-view>
</template>
export default{
methods: {
getCompanyIndormation(){
//Vuex function to store company information
this.setCompanyInformation(someinformation)
},
getUserInformation(){
//Vuex function to store user information
this.setUserInformation(someinformation)
}
}
}
My store.js
export default new Vuex.Store({
state: {
user: {
id: '',
name: '',
},
company: {
name: '',
subdomain: ''
}
},
mutations: {
setUserInformation: (state, obj) => {
state.user.id = obj.id;
state.user.name = obj.name;
},
setCompanyInformation: (state, obj) => {
state.company.name = obj.name;
state.company.subdomain = obj.subdomain;
}
}
})
So far everything works perfectly. My problem was when I tried to retrieve some information from the company in a mixin I own.
My mixin.js
import { mapState } from 'vuex';
const myMixin = {
computed: {
...mapState(['company'])
},
methods: {
$getCompanyUrl(){
return 'https:// '+this.company.subdomain+'/contact'
}
}
}
My problem here is that in some cases the function of my mixin normally returns the entire url, for example: https://domain1.com/contact but sometimes it returns https:///contact, that is, he didn't find it still the domain. Does anyone have any suggestions on how I can solve this problem? Do I add a watcher to see when the company's information has changed in the store or do I expect to finish all initial requirements before even rendering the router-view?
Maybe just create a getter for company url in your store.
getters: {
companyUrl: state => `https://${state.company.subdomain}/contact`,
},
And then use mapGetters anywhere else. The getters are meant to be watching the state changes.
This is my store.js code which contains an array named posts which is getting the data from the api
export const store=new vuex.Store({
state:{
posts:[],
},
mutations:{
getallpost(state,posts) {
state.posts = posts
}
},
actions: {
getallpost: ({commit}) =>{
var self =this;
axios.get('https://jsonplaceholder.typicode.com/posts')
.then(function (response) {
commit(self.posts=response.data)
commit(console.log('Data: ',response.data));
})
.catch(function (error) {
console.log(error);
});
},
}
});
This is my mainpage.js code: here i am mapping the actions and the state still i am not getting the data displayed on my window
import {mapActions} from 'vuex'
import {mapState} from 'vuex'
import {store} from "../../store/store";
import axios from "axios";
export default {
name: 'Mainpage',
components:{Counter,Result,store},
data(){
return{
posts:[],
}
},
methods:{
...mapActions([
'getallpost']),
...mapState([
'getallpost'
])
}
};
They way you are committing is incorrect. The first argument is a string of the mutation name, the second one is the value.
Your commit in your store should look like that in your action:
commit('setPosts', response.data)
Note that I've renamed your mutation from getallpost to setPosts as this makes it semantically more accurate and avoids naming collisions, so basically:
mutations:{
setPosts(state, posts) {
state.posts = posts
}
},
That should do the job of storing the data. Also, get rid of the var self = this. When using store you never want to use this, the current state of the store will always be given to your mutation through the parameter in the function, to ensure that everything runs synchronous without side-effects.
So far so good. Now in order to be able to receive the fetched posts from your store, you need a getter in your store module:
getters: {
getPosts: state => state.posts,
},
Finally add the getter to your component's methods:
...mapGetters({
storePosts: 'getPosts'
})
You can now use the posts in your template by simply using {{ storePosts }}
I took the back-end response token and store it in store.js as a state.
How do I pass that token value to the main.js? I do want to attach it to the Authorization header. This is my code.
store.js
state:{
idToken:"a sample id token"
}
main.js
axios.defaults.headers.common['Authorization']=
How do I pass that token value to the Authorization header.
Thank you.
#Shubham Patel You should watch changes of the returnToken. Then, do hearder reset in the watcher. The token refreshing should also be made with mutations.
Maybe something like:
Main.js:
import { mapGetters } from 'vuex'
export default {
computed: {
...mapGetters([
'returnToken'
])
},
watch: {
returnToken(newToken) {
this.$axios.defaults.headers.common['Authorization'] = newToken
}
}
}
Store.js:
export default new Vuex.Store({
state: {
idToken:"a sample id token"
},
getters: {
returnToken: state => {
return state.idToken
}
},
mutations: {
updateToken(state, {token}) {
state.idToken = token
}
}
})
Then somewhare else:
let token = await loadToken()
store.commit('updateToken', {token})
You can write a getter in Vuex which will return your current authentication token.
const store = new Vuex.Store({
state: {
idToken:"a sample id token"
},
getters: {
returnToken: state => {
return state.idToken
}
}
})
Then import the getter using
import { mapGetters } from 'vuex'
export default {
computed: {
...mapGetters([
'returnToken'
])
}
}
Then write
axios.defaults.headers.common['Authorization']=this.returnToken()
Reference
I'm new to Vue.js and looking for the equivalent of a service in AngularJS, specifically for storing data once and getting it throughout the app.
I'll be mainly storing the results of network requests and other promised data so I don't need to fetch again on very state.
I'm using Vue.JS 2.0 with Webpack.
Thanks!
I think what u are seeking for is vuex, which can share data from each component.
Here is a basic demo which from my code.
store/lottery.module.js
import lotteryType from './lottery.type'
const lotteryModule = {
state: {participantList: []},
getters: {},
mutations: {
[lotteryType.PARTICIPANT_CREATE] (state, payload) {
state.participantList = payload;
}
},
actions: {
[lotteryType.PARTICIPANT_CREATE] ({commit}, payload) {
commit(lotteryType.PARTICIPANT_CREATE, payload);
}
}
};
export default lotteryModule;
store/lottery.type.js
const PARTICIPANT_CREATE = 'PARTICIPANT_CREATE';
export default {PARTICIPANT_CREATE};
store/index.js
Vue.use(Vuex);
const store = new Vuex.Store();
store.registerModule('lottery', lotteryModule);
export default store;
component/lottery.vue
<template>
<div id="preparation-container">
Total Participants: {{participantList.length}}
</div>
</template>
<script>
import router from '../router';
import lotteryType from '../store/lottery.type';
export default {
data () {
return {
}
},
methods: {
},
computed: {
participantList() {
return this.$store.state.lottery.participantList;
}
},
created() {
this.$store.dispatch(lotteryType.PARTICIPANT_CREATE, [{name:'Jack'}, {name:'Hugh'}]);
},
mounted() {
},
destroyed() {
}
}
</script>
You don't need Vue-specific services in Vue2 as it is based on a modern version of JavaScript that uses Modules instead.
So if you want to reuse some services in different locations in your code, you could define and export it as follows:
export default {
someFunction() {
// ...
},
someOtherFunction() {
// ...
}
};
And then import from your Vue code:
import service from 'filenameofyourresources';
export default {
name: 'something',
component: [],
data: () => ({}),
created() {
service.someFunction();
},
};
Note that this is ES6 code that needs to be transpiled to ES5 before you can actually use it todays browsers.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export
I'm just starting to learn Vuex here. Until now I've been storing shared data in a store.js file and importing store in every module but this is getting annoying and I'm worried about mutating state.
What I'm struggling with is how to import data from firebase using Vuex. From what I understand only actions can make async calls but only mutations can update the state?
Right now I'm making calls to firebase from my mutations object and it seems to be working fine. Honestly, all the context, commit, dispatch, etc. seems a bit overload. I'd like to just be able to use the minimal amount of Vuex necessary to be productive.
In the docs it looks like I can write some code that updates the state in the mutations object like below, import it into my component in the computed property and then just trigger a state update using store.commit('increment'). This seems like the minimum amount necessary to use Vuex but then where do actions come in? Confused :( Any help on the best way to do this or best practices would be appreciated!
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
}
})
My code is below
store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);
const db = firebase.database();
const auth = firebase.auth();
const store = new Vuex.Store({
state: {
userInfo: {},
users: {},
resources: [],
postKey: ''
},
mutations: {
// Get data from a firebase path & put in state object
getResources: function (state) {
var resourcesRef = db.ref('resources');
resourcesRef.on('value', snapshot => {
state.resources.push(snapshot.val());
})
},
getUsers: function (state) {
var usersRef = db.ref('users');
usersRef.on('value', snapshot => {
state.users = snapshot.val();
})
},
toggleSignIn: function (state) {
if (!auth.currentUser) {
console.log("Signing in...");
var provider = new firebase.auth.GoogleAuthProvider();
auth.signInWithPopup(provider).then( result => {
// This gives you a Google Access Token. You can use it to access the Google API.
var token = result.credential.accessToken;
// The signed-in user info.
var user = result.user;
// Set a user
var uid = user.uid;
db.ref('users/' + user.uid).set({
name: user.displayName,
email: user.email,
profilePicture : user.photoURL,
});
state.userInfo = user;
// ...
}).catch( error => {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
// The email of the user's account used.
var email = error.email;
// The firebase.auth.AuthCredential type that was used.
var credential = error.credential;
// ...
});
} else {
console.log('Signing out...');
auth.signOut();
}
}
}
})
export default store
main.js
import Vue from 'vue'
import App from './App'
import store from './store'
new Vue({
el: '#app',
store, // Inject store into all child components
template: '<App/>',
components: { App }
})
App.vue
<template>
<div id="app">
<button v-on:click="toggleSignIn">Click me</button>
</div>
</template>
<script>
import Hello from './components/Hello'
export default {
name: 'app',
components: {
Hello
},
created: function () {
this.$store.commit('getResources'); // Trigger state change
this.$store.commit('getUsers'); // Trigger state change
},
computed: {
state () {
return this.$store.state // Get Vuex state into my component
}
},
methods: {
toggleSignIn () {
this.$store.commit('toggleSignIn'); // Trigger state change
}
}
}
</script>
<style>
</style>
All AJAX should be going into actions instead of mutations. So the process would start by calling your action
...which commits data from the ajax callback to a mutation
...which is responsible for updating the vuex state.
Reference: http://vuex.vuejs.org/en/actions.html
Here is an example:
// vuex store
state: {
savedData: null
},
mutations: {
updateSavedData (state, data) {
state.savedData = data
}
},
actions: {
fetchData ({ commit }) {
this.$http({
url: 'some-endpoint',
method: 'GET'
}).then(function (response) {
commit('updateSavedData', response.data)
}, function () {
console.log('error')
})
}
}
Then to call your ajax, you will have to call the action now by doing this:
store.dispatch('fetchData')
In your case, just replace this.$http({...}).then(...) with your firebase ajax and call your action in the callback.