Using vuex store in Child Component don't work - javascript

I've an Problem with the vuex-Store. Theres one state in my store which is not be updated, when the Action is called. Maybe anyone can support me here? The problem is the state of "selectedHive". The axios-call is working well and get the correct response. But the Object would not be updated in store.
Here are the involved files:
Store:
import merge from 'vuex'
import axios from 'axios'
export const state = () => ({
selectedHive: {},
hivesList: []
})
export const mutations = {
set(state, hives) {
state.hivesList = hives
},
add(state, value) {
merge(state.hivesList, value)
},
remove(state, { hive }) {
state.hivesList.splice(state.hivesList.indexOf(hive), 1)
},
setHive(state, hive) {
state.selectedHive = hive
console.table(state.selectedHive)
}
}
export const actions = {
async get({ commit }) {
await this.$axios.get('http://localhost:8080/api/v1/hives').then((res) => {
if (res.status === 200) {
commit('set', res.data)
}
})
},
async show({ commit }, params) {
await this.$axios
.get(`http://localhost:8080/api/v1/hives/${params.id}`)
.then((res) => {
if (res.status === 200) {
console.log('ID: ' + params.id)
commit('setHive', res.data)
}
})
},
async set({ commit }, hive) {
await commit('set', hive)
},
async getHive({ commit }, params) {
console.log('getHive called' + params)
return await axios
.get(`http://localhost:8080/api/v1/hives/${params}`)
.then((res) => {
console.log(res.data)
console.log(typeof res.data)
commit('setHive', res.data)
})
.catch((err) => {
console.log(err)
})
}
}
Component:
<template>
<div class="div-box">H: {{ selectedHive }}</div>
</template>
<script>
import { mapState, mapActions } from 'vuex'
export default {
props: {
hiveid: {
type: String,
required: true
}
},
async fetch({ store }) {
this.getHive(this.hiveid)
console.log('Passing: ' + this.hiveid)
await store.dispatch('hives/getHive', this.hiveid)
},
computed: {
...mapState({
selectedHive: (state) => state.hive.selectedHive
})
},
created() {
console.log('id: ' + this.hiveid)
this.getHive(this.hiveid)
},
methods: {
...mapActions('hives', ['getHive'])
}
}
</script>
<style scoped>
.div-box {
/* width: 49%; */
border: 1px solid black;
padding: 10px;
}
</style>
parent page:
<template>
<div>
<h1>Locations</h1>
<!-- <div>LOCATIONS liste: {{ locationList }}<br /><br /></div>
<div>Selected LOCATION: {{ selectedLocation }}<br /><br /></div> -->
<div v-for="loc in locationList" :key="loc.id">
<div class="div-box">
u-Id: {{ loc._id }} <br />Name: {{ loc.name }} <br />
Adresse: {{ loc.adress }} <br />
Koordinaten: {{ loc.longitude }} , {{ loc.latitude }} Völker: <br />
<div v-for="hive in loc.hives" :key="hive._id">
{{ hive._id }}
<hiveIcon :hiveid="hive.hiveId" />
</div>
</div>
<br /><br />
</div>
</div>
</template>
<script>
import { mapState } from 'vuex'
import hiveIcon from '#/components/hiveIcon'
export default {
components: {
hiveIcon
},
computed: {
...mapState({
locationList: (state) => state.locations.locationsList,
selectedLocation: (state) => state.locations.selectedLocation,
hivesList: (state) => state.hives.hivesList,
selectedHive: (state) => state.hives.selectedHive
})
}
}
</script>
<style scoped>
.div-box {
/* width: 49%; */
border: 1px solid black;
padding: 10px;
}
</style>

I would guess, that it's something related to your state structure and how you access it.
You have
export const state = () => ({
selectedHive: {},
hivesList: []
})
in your state, but when mapping you access hive before selectedHive:
...mapState({
selectedHive: (state) => state.hive.selectedHive
})
Try to access it directly, like: selectedHive: (state) => state.selectedHive
EDIT:
Could you try to setup a watcher on that selectedHive?
watch: {
selectedHive: {
deep: true,
handler() {
console.log('selectedHive has changed');
}
}
}

Related

Passing data to child component using props not working in vue js

i've been learning vuejs for 2 weeks and i'm stuck a day to this problem when i passing a reactive data to child component using axios
here's the code :
<template>
<div class="wrapper">
<div class="box-container">
<TreeChart
:json="tree"
:class="{ landscape: landscape.length }"
#click-item="clickItem"
/>
</div>
</div>
</template>
<script lang="ts">
import TreeChart from "#/components/TreeChart.vue";
import { defineComponent, ref } from "vue";
import { useRouter } from "vue-router";
import http from "#/common-setup/http";
export default defineComponent({
props: ["treeUid"],
setup(props) {
const landscape = ref([]);
let tree = ref({} as any);
const router = useRouter();
if (
props.treeUid === undefined ||
props.treeUid === null ||
props.treeUid === ""
) {
router.push({ name: "NotFoundResource" });
}
let response = http
.get(`/wv/tree/show/${props.treeUid}`)
.then((response) => {
if (response.data.response.length === 0) {
tree.value = {};
router.push({ name: "emptyTree" });
}
tree.value = response.data.response[0];
})
.catch((err) => {
console.log(err);
})
.then(() => {
console.log("done");
});
function clickItem(item: any) {
console.log(item);
}
return { tree, landscape, clickItem };
},
components: {
TreeChart,
},
});
</script>
<style scoped>
.wrapper {
#apply flex;
flex-direction: row;
#apply relative;
width: 100%;
}
.box-container {
margin: 0 auto;
}
</style>
here is setup api on child component
import { ref, watch, getCurrentInstance } from "vue";
import { TreeTypes } from "#/types/Tree";
export default function treeChart(props?: any) {
let treeData = ref({} as any);
const { ctx: _this }: any = getCurrentInstance();
// console.log(props.json);
watch(
props.json,
(props) => {
let showKey = function (jsonData: any) {
jsonData.extend = jsonData.extend === void 0 ? true : !!jsonData.extend;
// console.log(jsonData);
if (Array.isArray(jsonData.children)) {
jsonData.children.forEach((c: any) => {
showKey(c);
});
}
return jsonData;
};
if (props) {
treeData = showKey(props);
}
},
{ immediate: true }
);
function toggleTree(treeData: any): void {
treeData.extend = !treeData.extend;
_this.$forceUpdate();
}
return { treeData, toggleTree };
}
if i use dummy data not data from axios it will working and it become more weird when in hot reload mode, i tried to console it the data from axios suddenly appear.
is there something wrong how i pass the data with axios to the child component ?

Vuex global state change does not trigger re-render component in v-for loop in Nuxt

I have difficult to use vuex global state combine with re-render child-component in Vue.js.
The global state is mutated but does not re-render its data in v-for loop.
All list of data is rendered, but when the new data changes, component in /blog does not change data in it.
Here is some code:
/store/index.js
export const state = () => ({
allData: [],
})
export const getters = {
getAllData: (state) => state.allData,
}
export const mutations = {
GET_DATAS(state, payload) {
state.allData = payload
},
UPDATE_DATA(state, payload) {
const item = state.allData[payload.index]
Object.assign(item, payload)
},
}
export const actions = {
getDatas({ commit, state }, payload) {
return fetch(`URL_FETCH`)
.then((data) => data.json())
.then((data) => {
commit('GET_DATAS', data)
})
.catch((err) => console.log(err))
},
updateData({ commit, state }, payload) {
commit('UPDATE_DATA', payload)
},
}
in /layouts/default.vue
beforeCreate() {
this.$store.dispatch('getDatas').then(() => {
connectSocket()
})
},
methods: {
connectSocket() {
// connect & received message from socket
// received message from socket
this.$root.$emit('updateData', {
index: 12,
price: 34,
change: 56,
percent: 78,
})
},
},
and in /pages/blog/index.vue
<template>
<div>
<div
v-for="index in getAllData"
:key="index.name"
class="w-100 grid-wrapper"
>
<div>{{ index.price }}</div>
<div>{{ index.change }}</div>
<div>{{ index.percent }}</div>
</div>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
data() {
return {}
},
computed: {
...mapGetters(['getAllData']),
},
mounted() {
this.$root.$on('updateData', (item) => {
this.$store.dispatch('updateData', {
index: item.index,
price: item.price,
percent: item.percent,
change: item.change,
})
})
},
}
</script>
Here is a complete example on how to use Vuex and load the data efficiently into a Nuxt app (subjective but using good practices).
/pages/index.vue
<template>
<div>
<main v-if="!$fetchState.pending">
<div v-for="user in allData" :key="user.id" style="padding: 0.5rem 0">
<span>{{ user.email }}</span>
</div>
</main>
<button #click="fakeUpdate">Update the 2nd user</button>
</div>
</template>
<script>
import { mapState, mapActions } from 'vuex'
export default {
data() {
return {
mockedData: {
name: 'John Doe',
username: 'jodoe',
email: 'yoloswag#gmail.com',
phone: '1-770-736-8031 x56442',
website: 'hildegard.org',
},
}
},
async fetch() {
await this.setAllData()
},
computed: {
...mapState(['allData']),
},
methods: {
...mapActions(['setAllData', 'updateData']),
fakeUpdate() {
this.updateData({ index: 1, payload: this.mockedData })
},
},
}
</script>
/store/index.js
import Vue from 'vue'
export const state = () => ({
allData: [],
})
export const mutations = {
SET_ALL_DATA(state, payload) {
state.allData = payload
},
UPDATE_SPECIFIC_DATA(state, { index, payload }) {
Vue.set(state.allData, index, payload)
},
}
export const actions = {
async setAllData({ commit }) {
try {
const httpCall = await fetch('https://jsonplaceholder.typicode.com/users')
const response = await httpCall.json()
commit('SET_ALL_DATA', response)
} catch (e) {
console.warn('error >>', e)
}
},
updateData({ commit }, { index, payload }) {
commit('UPDATE_SPECIFIC_DATA', { index, payload })
},
}

bind a promise to component in Vue

App.vue
<template>
<v-app>
<v-main>
<Header :token="token" :users="users"/>
<router-view/>
</v-main>
</v-app>
</template>
<script>
import Header from './components/layout/Header'
import axios from 'axios';
export default {
name: 'App',
components: {
Header
},
data() {
return { token: null, users: [] };
},
created(){
this.token = this.getToken();
this.users = this.getUsers();
},
methods:{
getToken(){
axios.get("http://someURL.com")
.then(res => {
console.log("token = ", res)
return res;
});
},
getUsers(){
axios.get("http://someURL.com")
.then(res => {
let users = res.data.map(({username}) => username);
console.log("users = ", users)
return users;
});
}
}
};
</script>
Header.vue
<template>
<header class="header">
<v-toolbar dark>
<h1>TITLE</h1>
<v-spacer></v-spacer>
<div>
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
</div>
</v-toolbar>
<p v-if="users">{{users}}</p>
<p v-else>No Data</p>
</header>
</template>
<script>
export default {
name:"Header",
props: ['token', 'users'],
data: () => ({
}),
mounted(){
this.onStart();
},
methods:{
onStart(){
console.log("insideHeader = ", this.users)
}
}
}
</script>
<style scoped>
#nav{
float: right;
}
.header a{
color: #fff;
padding-right: 5px;
text-decoration: none;
}
</style>
So basically what my problem is, I am fetching some data on App.vue using axios, then binding that data on Header.vue, So when the app loads first the binded value will be undefined, only after some time the data is fetched from API. But even then the value remains undefined in Header. Any solutions?
Api calls are async in nature. hence make use of async/await..
<template>
<v-app>
<v-main>
<Header :token="token" :users="users"/>
<router-view/>
</v-main>
</v-app>
</template>
<script>
import Header from './components/layout/Header'
import axios from 'axios';
export default {
name: 'App',
components: {
Header
},
data() {
return { token: null, users: [] };
},
async created(){
this.token = await this.getToken();
this.users = await this.getUsers();
},
methods:{
getToken(){
return axios.get("http://someURL.com")
.then(res => {
console.log("token = ", res)
return res.data;
});
},
getUsers(){
return axios.get("http://someURL.com")
.then(res => {
let users = res.data.map(({username}) => username);
console.log("users = ", users)
return users;
});
}
}
};
</script>
Don't try to return a value from promise callback, you could use async/await to return the response :
<template>
<v-app>
<v-main>
<Header :token="token" :users="users"/>
<router-view/>
</v-main>
</v-app>
</template>
<script>
import Header from './components/layout/Header'
import axios from 'axios';
export default {
name: 'App',
components: {
Header
},
data() {
return { token: null, users: [] };
},
created(){
this.token = this.getToken();
this.users = this.getUsers();
},
methods:{
async getToken(){
let res=await axios.get("http://someURL.com")
console.log("token = ", res)
return res.data;
},
async getUsers(){
let res=await axios.get("http://someURL.com")
let users = res.data.map(({username}) => username);
console.log("users = ", users)
return users;
}
}
};
</script>

how to add Firebase to a Vuex todo list app?

I am building a simple Vue.js todo list app using Vue.js, Vuex, and Firebase. The Vuex store dispatches, commits, and returns the inputted data just as it should, but I want to be able to connect the app to a Firestore database. So far, I have managed to set up the app so that data is pushed into the collection, but I also want the database to return a snapshot of the firestore data to the DOM, as well as to enable deleting of data from database. I have experience with these Firestore methods in simple non Vuex-projects, but am not sure how to synthesize Firestore methods with a Vuex store. How can I do this? Here is what I have so far. Thanks so much!
<!--GetTodo.vue-->
<template>
<div id="get-todo" class="container">
<input class="form-control" :value="newTodo" #change="getTodo" placeholder="I need to...">
<button class="btn btn-primary" #click="addTodo">Add New Post</button>
<ul class="list-group">
<li class="list-group-item" v-for="todo in todos">
{{todo.body}}
<div class="btn-group">
<button type="button" #click="remove(todo)" class="btn btn-default btn-sm">
<span class="glyphicon glyphicon-remove-circle"></span> Remove
</button>
</div>
</li>
</ul>
</div>
</template>
<script>
export default {
methods: {
getTodo(e) {
this.$store.dispatch('getTodo', e.target.value)
},
addTodo() {
this.$store.dispatch('addTodo')
this.$store.dispatch('clearTodo')
},
remove(todo){
this.$store.dispatch('removeTodo', todo)
}
},
computed: {
todos(){
return this.$store.getters.todos
},
newTodo() {
return this.$store.getters.newTodo
}
}
}
</script>
<style>
</style>
//store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);
import db from '../firebase';
export default new Vuex.Store({
state: {
todos: [],
newTodo: ''
},
mutations: { //syncronous, committed
GET_TODO(state, todo){
state.newTodo = todo
},
ADD_TODO(state){
state.todos.push({
body: state.newTodo,
completed: false
})
db.collection('messages').add({
content: state.newTodo
})
},
REMOVE_TODO(state, todo){
var todos = state.todos
todos.splice(todos.indexOf(todo), 1)
},
CLEAR_TODO(state){
state.newTodo = ''
}
},
actions: { //asyncronous, dispatched
getTodo({commit}, todo){
commit('GET_TODO', todo)
},
addTodo({commit}){
commit('ADD_TODO')
},
removeTodo({commit}, todo){
commit('REMOVE_TODO', todo)
},
clearTodo({commit}){
commit('CLEAR_TODO')
}
},
getters: {
newTodo: state => state.newTodo,
todos: state => state.todos.filter((todo) => {
return !todo.completed
})
}
})
<!--App.vue-->
<template>
<div id="app" class="container">
<GetTodo></GetTodo>
</div>
</template>
<script>
import GetTodo from './components/GetTodo.vue'
export default {
components: {
GetTodo
}
}
</script>
<style>
body {
font-family: Helvetica, sans-serif;
}
li {
margin: 10px;
}
</style>
You can make sync in your mutation, see the example below:
source: https://www.codewall.co.uk/how-to-create-a-real-time-to-do-list-app-with-vue-vuex-firebase-tutorial/
import Vue from 'vue'
import Vuex from 'vuex'
import { db } from '#/main'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
items: null
},
getters: {
getItems: state => {
return state.items
}
},
mutations: {
setItems: state => {
let items = []
db.collection('items').orderBy('created_at').onSnapshot((snapshot) => {
items = []
snapshot.forEach((doc) => {
items.push({ id: doc.id, title: doc.data().title })
})
state.items = items
})
}
},
actions: {
setItems: context => {
context.commit('setItems')
}
}
})
import { db } from '#/main'
export default {
name: 'home',
beforeCreate: function () {
this.$store.dispatch('setItems')
},
data: function () {
return {
myTodo: '',
errors: ''
}
},

How do I call Axios on prop change in Vue?

On the change of the value id, I would like to make a JSON call via Axios and update necessary parts of the page. How do I do that? Currently, I have mounted and activated and they do not seem to be working...
Code:
const Home = {
template: `
<div class="user">
<h2>user {{ id }}</h2>
<h2>{{ info }}</h2>
bet
</div>
`,
props: {
id: {
type: String,
default: 'N/A'
}
},
data () {
return {
info: null
}
},
activated () {
axios
.get('https://api.coindesk.com/v1/bpi/currentprice.json',
{ params: { id: id }}
)
.then(response => (this.info = response))
},
mounted() {
axios
.get('https://api.coindesk.com/v1/bpi/currentprice.json')
.then(response => (this.info = 'response'))
}
}`
You can listen to id prop change by using watch:
watch: {
id: function(newId) {
axios
.get('https://api.coindesk.com/v1/bpi/currentprice.json',
{ params: { id: newId }}
)
.then(response => (this.info = response))
}
}
Here is a little demo based on the code that you shared that shows how watch reacts to id prop change. Wrapper component below is solely for demonstration purpose as something that triggers id value change.
const Home = {
template: `
<div class="user">
<h2>user {{ id }}</h2>
<h2>{{ info }}</h2>
bet
</div>
`,
props: {
id: {
default: 'N/A'
}
},
data () {
return {
info: null
}
},
mounted() {
axios
.get('https://api.coindesk.com/v1/bpi/currentprice.json')
.then(response => (this.info = 'response'))
},
watch: {
id: function(newId) {
console.log(`watch triggered, value of id is: ${newId}`);
axios
.get('https://api.coindesk.com/v1/bpi/currentprice.json',
{ params: { id: newId }}
)
.then(response => (this.info = response))
}
}
}
const Wrapper = {
template: '<div><home :id="id" /></div>',
components: { Home },
data() {
return {
id: 0
}
},
mounted() {
const limit = 5;
const loop = (nextId) => setTimeout(() => {
console.log(`#${nextId} loop iteration`);
if (nextId < limit) {
this.id = nextId;
loop(nextId + 1);
}
}, 3000);
loop(this.id);
}
}
new Vue({
el: '#app',
components: { Wrapper }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.min.js" ></script>
<div id="app">
<wrapper />
</div>

Categories

Resources