bind a promise to component in Vue - javascript

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>

Related

How to update a vue ChartJS?

Hello I would like to understand how to update my vue.chart.js. I make a GET request every 1 second and I would like to be able to update the display to show the latest value. But I don't see how to do it.
Does anyone know how to do it?
Here is my code :
<template>
<div>
<v-col>
<v-card class="mx-auto" v-if="this.arrTemperature.length > 0">
<v-card-title class="text-h6 font-weight-black">Température</v-card-title>
<line-chart ref="chartTemp" :chartData="arrTemperature" :options="chartOptions" :chartColors="blueChartColors"
label="Température" />
</v-card> <br>
</v-col>
</div>
</template>
<script>
import LineChart from "../components/LineChart";
import moment from "moment";
export default {
components: {
LineChart,
},
data() {
return {
arrTemperature: [],
// =============================== CHART TEMPERATURE ===============================
mounted(){
setInterval(this.getTemperature, 1000)
},
methods: {
async getTemperature() {
const tag_id = encodeURIComponent('bts_d02c2b7d9098aaa2');
const url = this.$api.getRESTApiUri() + `/temperature/last_id/${tag_id}`;
return fetch(url)
.then(res => res.text())
.then((result) => {
const data = JSON.parse(result);
data.forEach(d => {
let date = moment(d.time).format("DD-MM-YYYY HH:mm:ss");
const {
temperature,
} = d;
this.arrTemperature.push({
date,
y: data[0].temperature
});
console.log(date)
console.log(data[0].temperature)
this.ChartData = { ...this.arrTemperature }
});
})
.catch((error) => {
console.log(error)
});
},
}
};
</script>
here is my display:

Undefined prop in child component

I have a parent component containing three child components. The first child component is a form. On a submit event it passes data to both the second and third child components via the parent component using props. However in one of the child components, the prop is always undefined. I think it's a timing issue, but using v-if does not seem to solve the issue.
The Parent Component:
<template>
<div>
<patents-searchform v-on:form-submit="processForm"></patents-searchform>
<patents-word-cloud
v-if="searched"
v-show="searched"
:patentsQuery="patentsQuery"
:livePage="livePage"
v-on:pageChange="handlePageChange"
/>
<patents-search-results
v-if="searched"
v-show="searched"
ref="resultsRef"
:livePage="livePage"
:results="patentsQueryResult"
v-on:pageChange="handlePageChange"
</div>
</template>
export default {
data() {
return {
livePage: 1,
searched: false,
queryform: 'initVal',
patentsQueryResult: {},
searching: false,
patentsQuery: {}
};
},
components: {
'patents-searchform': PatentsSearchForm,
'patents-searchresults': PatentsSearchResults,
'patents-word-cloud': PatentsWordCloud,
},
methods: {
handlePageChange(value) {
console.log('Homepage::handlePageChange', value)
this.queryform.page = value;
this.livePage = value;
this.fetchData();
},
processForm(formData) {
this.queryform = formData;
this.fetchData();
this.patentsQuery['query'] = this.queryform['query']
this.patentsQuery['searchMode'] = this.queryform['searchMode']
this.searched = true;
},
fetchData() {
const path = '/flask/searchPatentsNEW';
this.searching = true;
if (this.queryform !== 'initVal') {
axios.post(path, this.queryform)
.then((res) => {
this.patentsQueryResult = res.data;
this.searching = false;
})
.catch((error) => {
console.log(error);
});
}
}
}
};
The child component (PatentSearchResults) in which the props work correctly:
<template>
<b-container>
<b-card>
<a id="sTitleCard">Search Results</a>
<div id="quickStats" style="margin-left: 5%" v-if="this.results.stats">
{{results.stats.totalAuthors}} inventors across {{results.stats.docCount}} patents
({{results.stats.totalQueryTime}} seconds)
</div>
</b-card>
</b-container>
</template>
<script>
export default {
name: 'Results',
props: ['results', 'livePage'],
computed: {
thisAuthorPage() {
if (this.results.authors !== null) {
return this.results.authors; //this.results works fine
}
console.log('no authors')
return [];
},
},
methods: {
},
};
</script>
And the child component where the props are undefined:
<template>
<div>
<b-card id="patentWordCloudCard" bg-variant="light">
<b-container>
<b-form id="queryForm" #submit="onSubmit" #reset="onReset" novalidate>
<b-row class="d-flex flex-row-reverse">
<b-button id="btnSubmit" type="submit" variant="primary">Generate query word cloud</b-button>
</b-row>
</b-form>
</b-container>
</b-card>
</div>
</template>
<script>
export default {
name: 'Patents Word Cloud',
props: ['patentsQuery'],
data() {
return{
form: {
query: this.patentsQuery.query,
searchMode: this.patentsQuery.searchMode
},
show: true,
}
},
mounted() {
console.log(this.patentsQuery) //undefined
},
computed() {
console.log(this.patentsQuery) //undefined
},
methods: {
onSubmit(evt) {
evt.preventDefault();
console.log(this.patentsQuery) //undefined
}
}
}
</script>
Is it a timing issue where the word cloud component is mounted before patentsQuery is defined? If so, why did v-if not delay the component, as searched is false until after patentsQuery is defined.
It was a timing issue, I was able to access patentsQuery with the following code:
<template>
<div>
<b-card id="patentWordCloudCard" bg-variant="light">
<b-container>
<b-form id="queryForm" align-h="center" #submit="onSubmit" #reset="onReset" novalidate>
<b-row align-h="center">
<b-button align-h="center" id="btnSubmit" type="submit" variant="primary">Generate query word cloud</b-button>
</b-row>
</b-form>
</b-container>
</b-card>
</div>
</template>
<script>
import axios from 'axios';
export default {
name: 'Patents Word Cloud',
props: ['patentsQuery'],
methods: {
onSubmit(evt) {
evt.preventDefault();
const path = 'flask/patentWordCloud';
this.searching = true
axios.post(path, this.patentsQuery).then((res) => {
console.log('Patent Word Cloud Data')
console.log(res.data)
this.patentResults = res.data;
})
.catch((error) => {
console.log(error)
})
}
}
}
</script>

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 })
},
}

Components are hidden only on page reload(vue js)

Good evening. With user authorization, two components should be hidden in the header without rebooting (via axios). But they only hide on reboot. With logout operation happens the same thing. What shall I do to take the necessary result?
header.vue
<template>
<li v-if="!user.name" class="nav-item">
<router-link to="/login"><b>Login</b></router-link>
</li>
<hr />
<li v-if="!user.name" class="nav-item">
<router-link to="/signup"><b>Registration</b></router-link>
</li>
</template>
<script>
...
computed: {
token() {
return this.token = localStorage.getItem('token')
}
},
mounted(){
const self = this;
window.axios.defaults.headers.common['Authorization'] = `Bearer ${this.token}`
axios.get('/api/user').then(function (response) {
self.user = response.data
})
.catch(function (error) {
console.log(error);
})
},
methods: {
logout()
{
axios.post('/api/logout').then(() => {
localStorage.removeItem('token')
this.$router.push('/login')
}).catch((errors) => {
console.log(errors)
})
}
}
}
</script>
login.vue
<template>
...
</template>
...
methods: {
loginUser(){
const self = this;
axios.post('/api/login', this.form).then((response) => {
localStorage.setItem('token', response.data)
console.log('logged');
self.$router.push({ name: "CV"});
}).catch((error)=>{
self.validate = error.response.data.errors;
})
}
}
}
</script>

Using vuex store in Child Component don't work

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');
}
}
}

Categories

Resources