localStorage.getItem() always returns null even when value exists - vue.js

Im not sure If I am doing something wrong or if I have a typo somewhere, but I am trying to compare dates in a vue project but the value I pull from my local storage always returns null even when I can see the value clearly exists when I check my local storage. so here is the set up.
after making a request I set the expires date in local storage like so
retrieveToken({ commit }, credentials) {
return new Promise((resolve, reject) => {
axios.post('/login', {
username: credentials.username,
password: credentials.password,
})
.then(response => {
const token = response.data.access_token
const date = new Date(moment().add(30, 'seconds').toDate());
localStorage.setItem('expires_on', date)
localStorage.setItem('access_token', token)
resolve(response)
})
.catch(error => {
console.log(error.response.data)
reject(error)
})
})
},
I can then see that the expires on has been placed in my local storage
I then want to use a getter to retrieve that value like so
tokenExpires() {
return localStorage.getItem('expires_on')
},
So i can use like this
computed: {
...mapGetters(['tokenExpires']),
},
methods: {
destroySessionIfTokenIsExpired() {
const current = new Date(moment())
const expires = this.tokenExpires
const currentDate = moment(current).format('YYYYMMDDHHMMSS')
const expiresDate = moment(expires).format('YYYYMMDDHHMMSS')
console.log(this.tokenExpires)
console.log(expiresDate)
if(currentDate >= expiresDate) {
this.$store.dispatch('destroyToken')
.then(() => {
this.$router.push('/login')
alert('Your Session Has Expired, Please Log Back In')
})
} else return;
}
}
but when I run this method and console.log(this.tokenExpires) it returns null and I am not sure why. If anybody can see what I am doing wrong please let me know!!
*Update, my issue is that I am trying to watch the routes and run a comparison of timestamps to see if the session is still valid but as pointed out, the getter does not have enough time to compute the value before the method runs, so any suggestions on how I could get around that would be awesome. here is the route watch method
watch: {
'$route': function(to, from) {
this.destroySessionIfTokenIsExpired()
}
},

thanks to #YongQuan I have this solution.
methods: {
...mapActions(['destroyToken']),
destroySessionIfTokenIsExpired() {
const expiresOn = localStorage.getItem('expires_on')
const expiresDate = moment(expiresOn).format('YYYYMMDDHHMMSS')
if(expiresOn == null) return;
const current = new Date(moment())
const currentDate = moment(current).format('YYYYMMDDHHMMSS')
if(currentDate >= expiresDate) {
this.$store.dispatch('destroyToken')
this.$router.push('/login')
} else return;
}
},
watch: {
'$route': function(to, from) {
this.destroySessionIfTokenIsExpired()
}
},
Instead of using a getter I just set the `localStorage.getItem('expires_on') to a variable inside the method. Thanks #YongQuan

Related

Vuex Getter not pulling data

I have a vuex store that I am pulling data from into a component. When the page loads the first time, everything behaves as expected. Yay.
When I refresh the page data is wiped from the store as expected and pulled again into the store as designed. I have verified this is the case monitoring the state using Vuex dev tools. My getter however doesn't pull the data this time into the component. I have tried so many things, read the documentation, etc and I am stuck.
Currently I am thinking it might be an issue with the argument?...
If I change the argument in the getter, 'this.id' to an actual value (leaving the dispatch alone - no changes there), the getter pulls the data from the store. So it seems the prop, this.id has the correct data as the dispatch statement works just fine. So why then wouldn't the getter work?
this.id source - The header includes a search for the person and passes the id of the person that is selected as the id prop. example data: playerId: 60
Thoughts? Appreciate any help.
This code works on initial page load, but not on page refresh.
props: ["id"],
methods: {
fetchStats() {
this.$store.dispatch("player/fetchPlayer", this.id).then(() => {
// alert(this.id);
this.player = this.$store.getters["player/getPlayerById"](this.id);
this.loading = false;
});
}
},
This code (only changing this.id to '6' on getter) works both on initial load and page refresh.
props: ["id"],
methods: {
fetchStats() {
this.$store.dispatch("player/fetchPlayer", this.id).then(() => {
// alert(this.id);
this.player = this.$store.getters["player/getPlayerById"](6);
this.loading = false;
});
}
},
Here is the getPlayerById getter:
getPlayerById: state => id => {
return state.players.find(plr => plr.playerId === id);
},
Here is the fetchPlayer action:
export const actions = {
fetchPlayer({ state, commit, getters }, id) {
// If the player being searched for is already in players array, no other data to get, exit
if (getters.getIndexByPlayerId(id) != -1) {
return;
}
// If the promise is set another request is already getting the data. return the first requests promise and exit
if (state.promise) {
return state.promise;
}
//We need to fetch data on current player
var promise = EventService.getPlayer(id)
.then(response => {
commit("ADD_PLAYER", response.data);
commit("CLEAR_PROMISE", null);
})
.catch(error => {
console.log("There was an error:", error.response);
commit("CLEAR_PROMISE", null);
});
//While data is being async gathered via Axios we set this so that subsequent requests will exit above before trying to fetch data multiple times
commit("SET_PROMISE", promise);
return promise;
}
};
and mutations:
export const mutations = {
ADD_PLAYER(state, player) {
state.players.push(player[0]);
},
SET_PROMISE(state, data) {
state.promise = data;
},
CLEAR_PROMISE(state, data) {
state.promise = data;
}
};

Vuejs created and mounted doesn't work properly even if at the same level than methods

I'm experiencing a strange behaviour with created() and mounted() in Vue.js. I need to set 2 lists in created() - so it means those 2 lists will help me to create a third list which is a merge.
Here is the code :
// return data
created () {
this.retrieveSellOffers();
this.getAllProducts();
},
mounted () {
this.mergeSellOffersProducts();
},
methods: {
retrieveSellOffers() {
this.sellerId = localStorage.sellerId;
SellOfferServices.getAllBySellerId(this.sellerId)
.then((response) => {
this.sellOffers = response.data;
console.log("this.sellOffers");
console.log(this.sellOffers);
})
.catch((e) => {
console.log(e);
});
},
getAllProducts() {
ProductServices.getAll()
.then((response) => {
this.products = response.data;
console.log("this.products");
console.log(this.products);
})
.catch((e) => {
console.log(e);
});
},
mergeSellOffersProducts () {
console.log(this.products) // print empty array
console.log(this.sellOffers) // print empty array
for (var i = 0; i < this.sellOffers.length; i++) {
if (this.sellOffers[i].productId === this.products[i]._id) {
this.arr3.push({id: this.sellOffers[i]._id, price: this.sellOffers[i].price, description: this.products[i].description});
}
}
this.arr3 = this.sellOffers;
},
}
//end of code
So my problem is when I enter in mergeSellOffersProducts(), my 2 lists are empty arrays :/
EDIT :
This way worked for me :
async mounted() {
await this.retrieveSellOffers();
await this.getAllProducts();
this.mergeSellOffersProducts();
},
methods: {
async retrieveSellOffers() {
this.sellerId = localStorage.sellerId;
this.sellOffers = (await axios.get('link/api/selloffer/seller/', { params: { sellerId: this.sellerId } })).data;
},
async getAllProducts() {
this.products = (await axios.get('link/api/product')).data;
},
}
I think the reason is: Vue does not wait for the promises to resolve before continuing with the component lifecycle.
Your functions retrieveSellOffers() and getAllProducts() contain Promise so maybe you have to await them in the created() hook:
async created: {
await this.retrieveSellOffers();
await this.getAllProducts();
}
So I tried to async my 2 methods :
async retrieveSellOffers() {
this.sellerId = localStorage.sellerId;
this.sellOffers = (await axios.get('linkhidden/api/selloffer/', { params: { sellerId: '615b1575fde0190ad80c3410' } })).data;
console.log("this.sellOffers")
console.log(this.sellOffers)
},
async getAllProducts() {
this.products = (await axios.get('linkhidden/api/product')).data;
console.log("this.products")
console.log(this.products)
},
mergeSellOffersProducts () {
console.log("here")
console.log(this.sellOffers)
console.log(this.products)
this.arr3 = this.sellOffers;
},
My data are well retrieved, but yet when I enter in created, the two lists are empty...
You are calling a bunch of asynchronous methods and don't properly wait for them to finish, that's why your data is not set in mounted. Since Vue does not await its lifecycle hooks, you have to deal with the synchronization yourself.
One Vue-ish way to fix it be to replace your method mergeSellOffersProducts with a computed prop (eg mergedSellOffersProducts). Instead of generating arr3 it would simply return the merged array. It will be automatically updated when products or sellOffers is changed. You would simply use mergedSellOffersProducts in your template, instead of your current arr3.
If you only want to update the merged list when both API calls have completed, you can either manually sync them with Promise.all, or you could handle this case in the computed prop and return [] if either of the arrays is not set yet.
When you're trying to merge the 2 lists, they aren't filled up yet. You need to await the calls.
async created () {
await this.retrieveSellOffers();
await this.getAllProducts();
},
async mounted () {
await this.mergeSellOffersProducts();
},

Cannot parse JSON from Vuex getter in Ionic Vue

I have an Ionic Project with Vuex. I have created a store:
const store = new Vuex.Store({
state: {
user: localStorage.getItem('userdata') || {}
},
getters: {
getUser(state) {
return state.user
}
},
mutations: {
setUser(state, user) {
state.user = user
},
destroyUser(state) {
state.user = null
},
},
actions: {
retrieveUser(context) {
return new Promise((resolve, reject) => {
axios.get('v1/user')
.then(response => {
const user = response.data.data
localStorage.setItem('userdata', JSON.stringify(user))
context.commit('setUser', user)
resolve(user)
})
.catch(error => {})
})
},
}
})
This part works perfect as expected. My localstore holds the JSON string. Now i tried to return the string with the getUser getter JSON.parsed. This doesn't work, because it gives me a parse error which makes no sense, because the string works perfectly fine.
When I try to load the userdata in the vue component like this
export default {
data() {
return {
user: [],
}
},
mounted() {
this.loadUserData()
},
methods: {
loadUserData() {
let userData = JSON.parse(this.$store.getters.getUser)
this.user = userData
}
},
}
It returns the JSON Data as Proxy ( ?? )
Proxy {id: 27, name: "English", firstname: "Harriet", fullname: "Harriet English", number: null, …}
[[Handler]]: Object
[[Target]]: Object
[[IsRevoked]]: false
(it's sample data, so no real name shown ) which I cannot use.
I have also tried to use the state variable, the localstorage content, which did not work...
How can I access my JSON data?
When you save the user data after your API call, you are storing it in localStorage as JSON.stringify(user) but you are updating the store with just the raw user data. I guess you should update your API call handler to:
const user = response.data.data;
const strUser = JSON.stringify(user);
localStorage.setItem('userdata', strUser);
context.commit('setUser', strUser);
This should allow you to parse the data the way you are trying to in your component, which should work whether state.user has been initialised with the localStorage data, or if it has been updated after the API call.

VueJS data doesnt change on URL change

My problem is that when I go from one user page to another user page the info in component still remains from first user. So if I go from /user/username1 to /user/username2 info remains from username1. How can I fix this ? This is my code:
UserProfile.vue
mounted() {
this.$store.dispatch('getUserProfile').then(data => {
if(data.success = true) {
this.username = data.user.username;
this.positive = data.user.positiverep;
this.negative = data.user.negativerep;
this.createdAt = data.user.createdAt;
this.lastLogin = data.user.lastLogin;
data.invites.forEach(element => {
this.invites.push(element);
});
}
});
},
And this is from actions.js file to get user:
const getUserProfile = async ({
commit
}) => {
try {
const response = await API.get('/user/' + router.currentRoute.params.username);
if (response.status === 200 && response.data.user) {
const data = {
success: true,
user: response.data.user,
invites: response.data.invites
}
return data;
} else {
return console.log('Something went wrong.');
}
} catch (error) {
console.log(error);
}
};
Should I add watch maybe instead of mounted to keep track of username change in url ?
You can use watch with the immediate property, you can then remove the code in mounted as the watch handler will be called instead.
watch: {
'$route.params.username': {
handler: function() {
this.$store.dispatch('getUserProfile').then(data => {
if(data.success = true) {
this.username = data.user.username;
this.positive = data.user.positiverep;
this.negative = data.user.negativerep;
this.createdAt = data.user.createdAt;
this.lastLogin = data.user.lastLogin;
data.invites.forEach(element => {
this.invites.push(element);
});
}
});
},
deep: true,
immediate: true,
},
}
Your page is loaded before the data is retrieved it seems, you need put a "loading" property in the data and have a v-if="!loading" for your component then it will only render once the display is updated. Personally I would avoid watch if I can it is not great for performance of for fine grained handling.
Yes you should add wach on statement that contain user info.(you may have a problem to watch on object, so you can save user info in json, but im not sure). When user changing - call action, after recived response call mutation that should change a state, then watch this state.
And you might use better syntax to receive data from store. That is really bad idea call dispatch directly from your mouted hook, use vuex documentation to make your code better.

Axios GET not including params in Nuxt template

I want to pass an id to axios so that I can switch url dynamically.
My axios request in my template is as follows:
async asyncData({ params }) {
const { data } = await axios.get('http://localhost:8000/api/', {
params: {
id: 1
}
})
return { data }
}
The request being passed to my api is:
GET /api/?id=1
but I need
GET /api/1
What is happening here?
It looks like the asyncData function is called once when the page is loaded. I am still no wiser as to why it does not accept params in the way outlined in the docs and numerous tutorials, but it would not refresh the page because it is never called again.
To refresh the page data with a new api call, you need to return the axios promise from within the methods part of the export. The code below does the axios get request first, then adds or subtracts 1 from the id with plus and minus functions.
<script>
import axios from 'axios'
export default {
head() {
return {
title: 'Weather'
}
},
data: function() {
return { counter: 1 }
},
methods: {
plus: function(counter, data, datalength) {
this.counter += 1
axios.get('http://localhost:8000/api/' + this.counter).then(res => {
console.log(this.counter)
console.log(res.data)
return (this.data = res.data)
})
},
minus: function(counter, data) {
if (this.counter >= 2) {
this.counter -= 1
axios.get('http://localhost:8000/api/' + this.counter).then(res => {
console.log(this.counter)
console.log(res.data)
return (this.data = res.data)
})
} else {
this.counter = 1
}
}
},
async asyncData({ params, counter }) {
let { data } = await axios.get('http://localhost:8000/api/1')
return { data }
}
}
</script>
If anybody wants to elaborate or post a better solution, please go ahead - but I'm posting this because I searched so many tutorials and nothing worked until I found a way to interpret the documentation, which is certainly not beginner-friendly.