I'm new to vue.js and learning on my own with the vue doc, youtube videos and such. I've been searching for a while and looking at youtube tutorials and haven't found an answer so far, hope you guys will be able to help me.
So here's my issue, I'm building a web app and I need to display a list of objects dynamically but it doesn't show the first time I'm loading that page. I have to go to some other route and come back to see it, so I guess I'm misunderstanding some life cycle or something from that field of expertise...
I'm using the vuex to store and retrieve my data as seen below :
import Vue from 'vue';
const state = {
journees: {},
};
const getters = {
getJourneeList(state) {
return state.journees;
}
};
const mutations = {
GET_LIST(state, journees) {
state.journees = journees;
}
};
const actions = {
getJourneesUser({commit}) {
Vue.axios.get('/journee/')
.then( res => {
commit('GET_LIST', res.data)
})
.catch((err) => console.log(err))
}
};
export default {
state,
getters,
mutations,
actions
};
And then I'm getting it in my vue like this:
<template>
<v-container>
<v-card v-for="heure in heures" :key="heure._id">
<v-card-title>{{ heure }}</v-card-title>
</v-card>
</v-container>
</template>
<script>
export default {
name: "TimeList",
data() {
return {
heures: this.$store.getters.getJourneeList,
}
},
created() {
this.$store.dispatch('getJourneesUser');
}
}
</script>
You need to use mapState and use it inside computed value because then computed value will response to change in state. You do not need getter but if you want here is the version with getter. It should be like this if your store module called journees:
without getter
<template>
<v-container>
<v-card v-for="heure in journees" :key="heure._id">
<v-card-title>{{ heure }}</v-card-title>
</v-card>
</v-container>
</template>
<script>
import { mapState } from "vuex";
export default {
name: "TimeList",
computed: {
...mapState(["journees"])
},
created() {
this.$store.dispatch("getJourneesUser");
},
};
</script>
with getter
<template>
<v-container>
<v-card v-for="heure in getJourneeList" :key="heure._id">
<v-card-title>{{ heure }}</v-card-title>
</v-card>
</v-container>
</template>
<script>
import { mapGetters } from "vuex";
export default {
name: "TimeList",
computed: {
...mapGetters(["getJourneeList"])
},
created() {
this.$store.dispatch("getJourneesUser");
},
};
</script>
Related
I wanna display the numerical data od Vue.js data-table sorted in
descending order from the initial state.
I wanna make below picture from the initial state.
My Goal screen shot
In descending order is the data on the number of cases people in the middle of picture.
I'm having a hard time not knowing what part of the code to modify.
Would you please tell me?
<template>
<v-data-table
:headers="headers"
:items="covidList"
:items-per-page="5"
class="elevation-1"
></v-data-table>
</template>
<script>
import $axios from 'axios';
export default {
data(){
return{
headers:[
{
text:'area',
align:'start',
sortable:'false',
value:'name_ja',
},
{text:'cases',value:'cases'},
{text:'deaths',value:'deaths'},
],
}
},
async asyncData({ $axios }){
const response = await $axios.get('https://covid19-japan-web-api.now.sh/api/v1/prefectures')
return{
covidList:response.data
}
}
}
</script>
use this solution:
<template>
<v-data-table
:headers="headers"
:items="covidList"
:items-per-page="5"
:sort-by.sync="columnName"
:sort-desc.sync="isDescending"
class="elevation-1"
></v-data-table>
</template>
<script>
import $axios from 'axios';
export default {
data(){
return{
columnName:'cases',
isDescending:true,
headers:[
{
text:'area',
align:'start',
value:'name_ja',
},
{text:'cases',value:'cases'},
{text:'deaths',value:'deaths'},
],
}
},
async asyncData({ $axios }){
const response = await $axios.get('https://covid19-japan-web-api.now.sh/api/v1/prefectures')
return{
covidList:response.data
}
}
}
</script>
I have an initial (starting) page where the user can press a button to login. Also when in that page I want to know if the user's already logged in or not, so I'm using mapState to access the isLoggedIn state variable of my store.
In code:
// Initial.vue
<template>
<v-container
app
fluid
>
<v-parallax
src="https://cdn.vuetifyjs.com/images/backgrounds/vbanner.jpg"
height="1000"
>
<v-layout
row
wrap
>
<!-- LOGIN-->
<v-toolbar
flat
light
dense
color="transparent"
>
<v-spacer></v-spacer>
<v-toolbar-items>
<v-btn
medium
color="lime lighten-2"
#click="login"
class="font-weight-bold title text-uppercase"
>
LOGIN
</v-btn>
</v-toolbar-items>
</v-toolbar>
<v-layout
align-center
column
>
<h1 class="display-2 font-weight-thin mb-3 text-uppercase lime--text lighten-2" >My page</h1>
<h4 class="subheading">{{isLoggedIn}}</h4>
</v-layout>
</v-layout>
</v-parallax>
</v-container>
</template>
<script>
import VContainer from "vuetify/lib/components/VGrid/VContainer";
import VLayout from "vuetify/lib/components/VGrid/VLayout";
import VBtn from "vuetify/lib/components/VBtn/VBtn";
import VToolbar from "vuetify/lib/components/VToolbar/VToolbar";
import VParallax from "vuetify/lib/components/VParallax/VParallax";
import { mapState } from 'vuex';
export default {
name: "Initial",
components: {
VContainer,
VLayout,
VBtn,
VToolbar,
VParallax
},
//Upon returning to this page isLoggedIn is false eventhough isLoggedIn has been updated to store
computed: mapState({
isLoggedIn: state => state.isLoggedIn
}),
methods: {
login() {
this.$auth.login();
},
}
}
</script>
<style scoped>
</style>
After the user logs in (via auth0) he gets redirected to the registration form. Upon form submit the data are sent to the back-end, I'm invoking a REGISTER action to update the store state and then redirect to /home. The code that does that is:
//CompleteSignup.vue
this.$store.dispatch(REGISTER,registerFormData)
.then(() => this.$router.push('/home'));
And my store is this:
//index.js
import Vue from 'vue';
import Vuex from 'vuex';
import {APIService} from '#/common/api.service';
import {REGISTER} from "./actions.type";
import {SET_ERROR, SET_AUTH} from "./mutations.type";
Vue.use(Vuex);
export default new Vuex.Store({
strict: process.env.NODE_ENV !== 'production',
state: {
errors: null,
user: {},
isLoggedIn: false
},
getters: {
currentUser(state) {
return state.user;
},
},
actions: {
[REGISTER]({commit}, registerData) {
return new Promise((resolve, reject) => {
APIService.create(registerData)
.then(({data}) => {
commit(SET_AUTH, data);
resolve(data);
})
.catch(({response}) => {
commit(SET_ERROR, response.data.errors);
reject(response);
});
});
}
},
mutations: {
[SET_ERROR](state, error) {
state.error = error;
},
[SET_AUTH](state, user) {
state.isLoggedIn = true;
state.user = user.user_id;
state.errors = {};
}
}
})
When I'm in /home I can see from the Vue devtools that my store variables are correctly updated:
The problem appears when I'm navigating back to the /initial page. I would expect that when the user would go back to that view, the computed value isLoggedIn would return true; but instead I'm getting the default store value (false).
Why is mapState not returning true for the isLoggedIn store variable?
Also -though I'm not sure it's related- I noticed that in this scenario, vue devtools is showing again the first Base state of the attached screenshot as the active one (with the same timestamp too). It's as if the going back action re-triggered the original state instead of creating a new one.
For example, I have a file named App.vue and there I have navigation drawer component. And I have a file named Home.vue with the toolbar component. The thing is that I need to toggle navigation drawer state(true or false) from the Home.vue's toolbar component(also, the navigation drawer component is rendered in Home.vue). The code below doesn't return any error and doesn't change the navigation drawer visibility. Also, if set state manually in store.js, navigation drawer follows it. Can anyone please help?
store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
drawer: false
},
mutations: {
toggleDrawer: function(state) {
return state.drawer = !state.drawer;
}
},
actions: {
toggleDrawer({ commit }) {
commit('toggleDrawer');
}
},
getters: {
active: (state) => {
return state.drawer;
}
}
})
App.vue
<v-navigation-drawer v-model="drawer"> ... </v-navigation-drawer>
<script>
import store from './store'
export default {
data: function() {
return {
drawer: this.$store.state.drawer
}
}
}
</script>
Home.vue
<v-toolbar-side-icon #click="$store.commit('toggleDrawer')"> ... </v-toolbar-side-icon>
<script>
import store from '../store'
export default {
data: function() {
return {
drawer: this.$store.state.drawer // Seems like I don't need it here
}
}
}
</script>
This is an older post, but in case anyone come looking for the answer, this seems to work.
from the Vuex guide, Form Handling section, Two-way Computed Property
I modified the codesandbox provided by Sovalina (thanks!) link
The other way is to use v-model
<v-navigation-drawer v-model="drawer" app>
with the two way computed property, instead of mapGetters
<script>
export default {
computed: {
drawer: {
get () {
return this.$store.state.drawer
},
set (value) {
this.$store.commit('toggleDrawer', value)
}
}
}
}
</script>
You can use the navigation drawer's property permanent instead of v-model (to avoid mutate your store outside vuex) and use the getter active you defined.
App.vue:
<template>
<v-app >
<v-navigation-drawer :permanent="active">
...
</v-navigation-drawer>
</v-app>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
computed: {
...mapGetters([
'active'
])
},
}
</script>
Home.vue:
<template>
<v-toolbar-side-icon #click="toggle"> ... </v-toolbar-side-icon>
</template>
<script>
export default {
methods: {
toggle() {
this.$store.dispatch('toggleDrawer')
}
}
}
</script>
Note: as you defined an action toggleDrawer in your store you can use dispatch instead of commit.
Live example here
I've created component to search posts from API by query from URL, but if I change the query I receive strange flickering effect. Count of flicks grows by one after every change. How to solve it? Vue Devtools shows that loadPosts() is called: 1time, 2 times, 3times and so on. Where's the mistake? Reloading component gives the same effect.
<template>
<v-app>
<main-header/>
<v-layout class="mx-auto default--container">
<v-flex xs12 ma-2>
<h2 class="display-2 text-xs-center main-page--header">
<span class="text__red">W</span>yniki wyszukiwania dla:
<span class="text__red">{{this.$route.query.s}}</span>
</h2>
<article-list-sample v-for="i in articles.length" :key="`${i}`" />
</v-flex>
</v-layout>
<main-footer />
</v-app>
</template>
<script>
import MainHeader from './MainHeader';
import MainFooter from './MainFooter';
import ArticleListSample from './ArticleListSample';
import API from '../api';
export default {
components: {
ArticleListSample,
MainFooter,
MainHeader
},
name: 'search',
data: () => ({
articles: []
}),
methods: {
loadPosts() {
API.get(`posts?search=${this.$route.query.s}`)
.then(response => this.articles = response['data'])
}
},
mounted() {
this.loadPosts();
},
updated() {
this.loadPosts();
}
};
</script>
<style scoped>
</style>
To add on this, the infinite loop was created because updated was triggered whenever you were changing this.articles which would then start an other asynchronous call.
I solved it. Just need to add watcher instead of updated() method.
watch: {
'$route' () {
this.loadPosts();
}
},
So I am trying to use multiple instances of a component inside a parent component. I am currently using a Vuetify.js tab-control to display each component.
For each child component I want to load different dynamic data from a external Rest API.
My preferd way to approach this is to pass a prop down to child's so that they can figure out what data to load. I have a vuex store for each type of child, but would like to reuse template for both, since the data is identical besides values.
Parent/Wrapper
<template>
<div>
<v-tabs fixed-tabs>
<v-tab>
PackagesType1
</v-tab>
<v-tab>
PackagesType2
</v-tab>
<v-tab-item>
<packages packageType="packageType1"/>
</v-tab-item>
<v-tab-item>
<packages packageType="packageType2"/>
</v-tab-item>
</v-tabs>
</div>
</template>
<script>
import Packages from './Packages'
export default {
components: {
Packages
}
}
</script>
Named child component
<template>
<v-container fluid>
<v-data-table v-if="packageType1"
:headers="headers"
:items="packageType1"
>
<div>
//ITEM TEMPLATE
</div
</v-data-table>
<v-data-table v-else-if="packagesType2"
:items="packagesType2"
>
<div>
//ITEM TEMPLATE
</div
</v-data-table>
</v-container>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
data () {
return {
name: 'packages'
}
},
props: ['packageType'],
computed: {
...mapGetters('packageModule1', ['packageType1']),
...mapGetters('packageModule2', ['packagesType2'])
},
methods: {
getPackageByType () {
if (this.packageType === 'packageType1') {
this.getPackageType1()
return
}
this.getPackageType2()
},
getPackageType1 () {
this.$store.dispatch('chocoPackagesModule/getPackageType1')
.then(_ => {
this.isLoading = false
})
},
getPackageType2 () {
this.$store.dispatch('packagesType2Module/getPackageType2')
.then(_ => {
this.isLoading = false
})
}
},
created () {
this.getPackageByType()
}
}
</script>
Right now it renders only packageType1 when loading.
How can I easily differ those types without duplicating everything?
Should I just hard differ those types? Make separate child-components and role with it that way? I am new to this, but would love as much reuse as possible.