How to store and save data - vue.js

I start to learning store, and i want to upload image, and save it on local. So if i reloaded page image must be saved
store photoUpload.js
const state = {
photo: '1'
}
const getters = {
getPhoto: (state) => {
return state.photo
}
}
const mutations = {
setPhoto(state, photoName) {
state.photo = photoName
},
}
const actions = {
getPhoto(context, photo) {
context.commit('getPhoto', photo)
}
}
export const testings = {
namespaced: true,
state,
mutations,
getters,
actions,
};
template
<div class="upload">
<img :src="previewImage ? previewImage : 'assets/images/defaultAva.png'" class="profileImg" />
<label class="edit">
<input ref="imageInput" type="file" name="file" accept="image/png, image/jpeg, image/jpg" #change="uploadImage">
</label>
</div>
<script>
data: () => ({
previewImage: null,
}),
computed: {
getPhoto() {
return this.$store.getters["photoUpload/getPhoto"];
},
},
methods: {
uploadImage(e){
const image = e.target.files[0];
const reader = new FileReader();
reader.readAsDataURL(image);
reader.onload = e =>{
this.previewImage = e.target.result;
};
},
...mapActions("testings", ["getPhoto"]),
},
</script>
So in witch way i must to go, i can upload image to previewImage, but how can i send it to store, to save it locally?

So if i reloaded page image must be saved
The purpose of the store is to save data betweens components locally to all the application but not on page refresh.
From the documentation
A "store" is basically a container that holds your application state
The most common case is to save data from your api into global variables that can be used in all the application using getters as you did.
If you want to keep data on reload you can use other packages like vuex-persist.
In your case if you want to save image to the store you can just use the actions from the mapAction.
Here is how I do it :
methods: {
uploadImage(e){
const image = e.target.files[0];
const reader = new FileReader();
reader.readAsDataURL(image);
reader.onload = e =>{
this.previewImage = e.target.result;
this.getPhotoToStore(e.target.result) // <-- here
};
},
...mapActions({
getPhotoToStore: "myStore/getPhoto"
}),
},
And I use the actions as this :
const actions = {
getPhoto({ commit }, photo) {
commit('getPhoto', photo) // <-- commiting to the mutations
}
}

Related

How do i update my mapbox markers when the geojson data is passed in as props in a vue component

Edit 1
Here is the source code on git
I am working on an Ionic-Vue project with Mapbox integration. My issue is that I am unable to update my geojson data source to create markers on the map, the geoJson data is passed in as props. Here is the flow
On APP.vue created hook
Using capacitor gets user current location.
.then() make an Axios call to the server to fetch the JSON data
On success set the data to store via mutation
On Home.vue[parent component for TheMap.vue]
Computed value called getGeoJson which calls the getters to get geojson data from the state saved an earlier step
Bind the :marker prop with computed value getGeoJson data on the TheMap component to be sent as a prop.
Listen to the event $updatedLocation and call the API action to fetch new geojson data.
On TheMap.vue
On create Hook call the createMap().
In Method: createMap()
Get the user coords again using capacitorJS
init the mapbox map and save it to this.map variable
add Attribution using this.map.addControl
this.map.on("load",cb) inside of cb call .addSource() & then addMarkers()
Create a new marker for the current user and save it to cosnt UserLocationMarker
UserLocationMarker.on("dragend",CB) to emit a event with latest current user location
Here is the code for the same just putting the script tags rather than the whole.vue file
APP.Vue
export default {
name: "App",
data() {
return {
currentLocation: "",
};
},
created() {
Geolocation.getCurrentPosition()
.then((result) => {
this.currentLocation = [
result.coords.longitude,
result.coords.latitude,
];
this.$store.dispatch(
`lists/${POST_GEOJSON_ACTION}`,
this.currentLocation
);
})
.catch((err) => console.log(err));
},
};
Home.vue
<template>
<the-map :markers="geojson" #updatedLocation="updateMap"></the-map>
</template>
<script>
export default {
name: "Home",
computed: {
getGeoJson() {
return this.$store.getters[`lists/${GET_GEOJSON_GETTER}`];
},
},
methods: {
updateMap(center) {
this.$store.dispatch(`lists/${POST_GEOJSON_ACTION}`, center);
},
},
};
</script>
TheMap.vue
export default {
props: ["markers"],
emits: ["updatedLocation"],
data() {
return {
access_token: process.env.VUE_APP_MAP_ACCESS_TOKEN,
center: [0, 0],
map: {},
};
},
mounted() {
this.createMap();
},
methods: {
async createMap() {
try {
const coords = await Geolocation.getCurrentPosition();
this.center = [coords.coords.longitude, coords.coords.latitude];
mapboxgl.accessToken = this.access_token;
//Map Instance
this.map = new mapboxgl.Map({
container: "map",
style: "mapbox://styles/userName/API_KEY",
center: this.center,
zoom: 12,
scrollZoom: true,
});
// Custom Attribution over the map for Branding
this.map.addControl(
new mapboxgl.AttributionControl({
customAttribution: ` © Comapny Name`,
})
);
this.map.on("load", function(e) {
this.map.addSource("places", {
type: "geojson",
data: this.markers,
});
this.addMarkers();
});
const UserLocationMarker = new mapboxgl.Marker({
draggable: true,
})
.setLngLat(this.center)
.addTo(this.map);
UserLocationMarker.on("dragend", async (e) => {
this.center = Object.values(e.target.getLngLat());
this.$emit("updatedLocation", this.center);
});
} catch (err) {
console.log(err);
}
},
addMarkers() {
this.markers.features.forEach(function(marker) {
var el = document.createElement("div");
el.id = "marker-" + marker.properties.id;
el.className = "marker";
new mapboxgl.Marker(el, { offset: [0, -23] })
.setLngLat(marker.geometry.coordinates)
.addTo(this.map);
});
},
},
};
My issue here is that the TheMap.vue does get undefined | [{geojson}] as a prop however it does not load the marker on init or even after the source is changed in the parent component.
What I expect is that the map on Load uses markers prop to build a list of markers if available else show nothing[handle undefined | null`] And update that marker list if a new set of Data is injected as prop on changed location.

Detect vuex state change to execute a method inside a nuxt layout

I am trying to show vuetify snackbar alert, once I completed a form submission inside a page or vue component. I use vuex store to manage alert type and message.
my-nuxt-app/store/alerts.js
export const state = () => ({
message: '',
type: ''
});
export const getters = {
hasAlert(state) {
return state.message !== '';
},
alertMessage(state) {
return state.message;
},
alertType(state) {
return state.type;
}
};
export const mutations = {
SET_ALERT(state, payload) {
state.type = payload.type;
state.message = payload.message;
}
};
export const actions = {
setAlert({commit}, payload) {
commit('SET_ALERT', payload);
},
clearAlert({commit}) {
commit('SET_ALERT', {});
}
};
And I created a nuxt plugin to access getters globally in my application.
my-nuxt-app/plugins/alert.js
import Vue from 'vue';
import {mapGetters} from 'vuex';
const Alert = {
install(Vue, options) {
Vue.mixin({
computed: {
...mapGetters({
hasAlert: 'alerts/hasAlert',
alertType: 'alerts/alertType',
alertMessage: 'alerts/alertMessage'
})
}
});
}
};
Vue.use(Alert);
Inside my AccountForm component submit method, I am dispatching my alert information to store like below.
my-nuxt-app/components/form/AccountForm.vue
...
methods: {
async submit () {
try {
await this.$axios.patch("/settings/profile", this.form);
this.$store.dispatch('alerts/setAlert', {
type: 'success',
message: 'You have successfully updated your information.'
});
} catch (e) {
}
}
},
...
}
...
And this AccountForm.vue component is a child component of profile.vue page which is obviously inside the pages folder of my project. And also I have extended the dashboard.vue layout to this profile.vue page and to the most of the pages inside my pages directory as a common layout. Hence, I added the snackbar component into dashboard layout to show a alert message whenever required.
my-nuxt-app/layouts/dashboard.vue
<template>
...
<v-snackbar
:timeout="snackbar.timeout"
:color="snackbar.color"
:top="snackbar.y === 'top'"
:bottom="snackbar.y === 'bottom'"
:right="snackbar.x === 'right'"
:left="snackbar.x === 'left'"
:multi-line="snackbar.mode === 'multi-line'"
:vertical="snackbar.mode === 'vertical'"
v-model="snackbar.show"
>
{{ snackbar.text }}
<v-btn flat icon dark #click.native="snackbar.show = false">
<v-icon>close</v-icon>
</v-btn>
</v-snackbar>
...
</template>
<script>
...
data: () => ({
snackbar: {
show: false,
y: 'top',
x: null,
mode: '',
timeout: 6000,
color: '',
text: ''
},
}),
computed: {
availableAlert: function () {
return this.hasAlert;
}
},
watch: {
availableAlert: function(alert) {
if(alert) {
this.showAlert(this.alertType, this.alertMessage);
this.$store.dispatch('alerts/clearAlert');
}
}
},
methods: {
showAlert(type, message) {
this.snackbar.show = true;
this.snackbar.color = type;
this.snackbar.text = message;
}
}
</script>
I am getting the alert message for the first time submission of the form and after that I have to reload the page and then submit to get the alert. Please enlighten me a way to detect the vuex state change and trigger showAlert method inside the dashboard.vue accordingly.
It's most likely the way you're checking hasAlert
Your clearAlert passes an empty object, your setAlert is trying to assign properties of that empty object, while your hasAlert is checking if it's an empty string.
If you change your clearAlert to:
clearAlert({commit}) {
commit('SET_ALERT', { message: '', type: '' });
}
That should fix your issue.

in Vuex, How to load state and use data from the state when the application loads/renders first ?, I am using nuxt, Vue and Vuex as store

I am trying to load data from a JSON file into the VueX store, but the state does not get loaded until I try to refresh the VueX Store manually.
what I am trying to achieve is, before the app renders, the state should be loaded with the data.
Like before I access the homepage.
But I see on the Vue Devtools, that if set it to recording mode, then the app loads the data.
Below is code from store/index.js
//store/index.js
const exec = (method, { rootState, dispatch }, app) => {
const dispatches = [];
Object.keys(rootState).forEach(async (s) => {
dispatches.push(await dispatch(`${s}/${method}`, app));
});
return dispatches;
};
export const actions = {
nuxtServerInit(store, ctx) {
console.log('nuxtServerInit');
exec('init', store, ctx);
},
nuxtClientInit(store, ctx) {
console.log('nuxtClientInit');
exec('init', store, ctx);
},
init(store, ctx) {
console.log('nuxtInit');
exec('init', store, ctx);
},
};
store/app.js
//store/app.js
export const state = () => ({
config: {},
});
export const mutations = {
SET_CONFIG(state, config) {
state.config = config;
}
}
};
export const getters = {
config: (state) => state.config,
};
const loadConfig = ({ commit }) => {
const siteConfig = require('../config/data.json');
const appConfig = JSON.parse(JSON.stringify(siteConfig.properties));
commit('SET_CONFIG', appConfig);
};
export const actions = {
init(store, ctx) {
loadConfig(store);
},
};
Here the state is empty when the app loads. How can I access that when the app loads?
I normally call the init action of my store in the layout.
When this is too late you could also do it in a plugin, I guess.
You can use the context.store in the plugin.
// plugins/init.js
export default ({ store }) => {
store.dispatch("init")
}
// store/index.js
export actions = {
init(context) {
// ...
}
}

Vuex data just show once then won't display after reloading

why my json data display just only once and after reloading the page it won't show again.
Did I miss something here?
import axios from "axios";
const store = {
careers: []
};
const getters = {
allCareers: (state) => state.careers
};
const mutations = {
RETRIEVE_CAREERS: (state, career) => (state.careers = career),
};
const actions = {
async careers({ commit }) {
try {
const response = await axios.get('http://localhost:9001/career/jobs/');
commit('RETRIEVE_CAREERS', response.data);
} catch (err) {
console.log(err);
}
},
};
export default {
store,
getters,
mutations,
actions
}
and in my component I do this:
import { mapActions, mapGetters } from "vuex";
export default {
computed: {
...mapGetters([
"allCareers"
/* more getters here if necessary */
])
},
methods: {
...mapActions(["careers"])
},
created() {
this.careers();
}
};
and in template I just do this:
<template>
<section>
<v-card>
{{allCareers}}
</v-card>
</section>
</template>
Why it will show only once but won't show after reloading the page?
I don't see anywhere that you "persist" the fetched data. Vuex does not persist the data across reloads, it acts as an in-memory storage
You still have to persist your data to local storage of some sorts like localStorage or indexedDB.
Here is a simple solution:
const store = {
careers: JSON.parse(localStorage.getItem('careers') || '[]');
};
const mutations = {
RETRIEVE_CAREERS: (state, career) => {
state.careers = career;
localStorage.setItem('careers', JSON.stringify(career));
}
};

Using store data in Component pulls in default values, instead of updated ones

I am new to Vue, and am trying to use data from a store in a component. This data gets set when the app loads, and I have confirmed that it is getting set properly.
However, when I try to access this data from the store in a component (I'm using mapState), all I get are the default values for the store, and not the data that was set.
The store I am trying to access is:
//user.js
const defaults = {
emailAddress: '',
gender: 'N/A',
registered: false,
userName: '',
};
const state = {
user: { ...defaults, },
};
const getters = {};
const actions = {
login ({ commit, }) {
api.post(/loginurl).then((response) => {
commit('setUserInfo', { ...defaults, ...response, });
});
},
};
const mutations = {
setUserInfo (state, user) {
state.user = {...state, ...user,};
},
};
const user = {
namespaced: true,
state,
getters,
actions,
mutations,
};
export default user;
I've confirmed that all data is getting set correctly in setUserInfo.
However, when I access this in a component like so:
<template>
<div class="user-info">
<p> {{ user }} </p>
</div>
</template>
<script>
import { mapState } from 'vuex';
export default {
computed: {
...mapState('user', { user: (state) => state.user, },),
}
};
</script>
All the defaults for user print out in my template, instead of the values that were set in 'setUserInfo.
I will also add, that if I just try to access this via a computed property directly, I also get the defaults. For example:
userInfo () {
return this.$store.state.user;
},
Will also just return the defaults.
Do I need to set up a getter in the store, and then just use that in my component instead? Any help would be much appreciated!!!