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>
Related
import axios from "axios";
export const routerid = async (itemId) =>
await axios.get("https://fakestoreapi.com/products?limit=" + itemId);
<template>
<div>
<div v-for="(item, key) in user" :key="key">
{{ item.price }} <br />
{{ item.description }} <br />
</div>
</div>
</template>
<script>
import { routerid } from "./routerid";
export default {
name: "User",
components: {},
data() {
return {
lists: [],
};
},
mounted() {
if (this.$route.params.id)
routerid(this.$route.params.id).then((r) => {
let obj = r.data;
this.lists = [{ ...obj }];
});
},
computed: {
user: function () {
return this.lists.filter((item) => {
return item.id === this.$route.params.id;
});
},
},
};
</script>
And this is my complete code:- https://codesandbox.io/s/late-brook-eg51y3?file=/src/components/routerid.js
Above is my api call, with url query like url...../?limit=" + id
Above is the logic , which i tried. But not sure whats wrong with code. getting blank screen.
please provide some suggestions, on how to call. and please go through my code once, if there is any other issues. Thanks
It's all about spread operator, you should spread object inside array correctly, below example works fine.
<template>
<div>
<div v-for="(item, key) in user" :key="key">
{{ item.price }} <br />
{{ item.description }} <br />
</div>
</div>
</template>
<script>
import { routerid } from "./routerid";
export default {
name: "User",
components: {},
data() {
return {
lists: [],
};
},
mounted() {
if (this.$route.params.id)
routerid(this.$route.params.id).then((r) => {
let obj = r.data;
//changed from [{...obj}] to [...obj]
this.lists = [...obj];
});
},
computed: {
user: function () {
return this.lists.filter((item) => {
return item.id === this.$route.params.id;
});
},
},
};
</script>
You have 2 problems.
1 - Firstly you're using user instead of lists in the for loop.
2 - Secondly you're using spread operator on the retuned data which is already an array so you don't need to do that.
<template>
<div>
<div v-for="(item, key) in lists" :key="key">
{{ item.price }} <br />
{{ item.description }} <br />
</div>
</div>
</template>
<script>
import { routerid } from "./routerid";
export default {
name: "User",
components: {},
data() {
return {
lists: [],
};
},
mounted() {
if (this.$route.params.id)
routerid(this.$route.params.id).then((r) => {
this.lists = r.data;
});
},
computed: {
user: function () {
return this.lists.filter((item) => {
return item.id === this.$route.params.id;
});
},
},
};
</script>
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 })
},
}
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');
}
}
}
Vue component won't re-render array items after its value was set externally. State chenges but v-for element is not showing the changes.
I have a component that renders items from array. I also have buttons to change the array length and it works well: '+' adds one line and '-' removes the last line. The problem starts when I set the array data from a fetch method. Data is displayed but '+' and '-' buttons don't work.
Here's a link to codesanbox https://codesandbox.io/s/q9jv524kvw
/App.vue
<template>
<div id="app">
<button #click="downloadTemplate">Load data</button>
<HelloWorld :formData="formData" />
</div>
</template>
<script>
import HelloWorld from "./components/HelloWorld";
export default {
name: "App",
components: {
HelloWorld
},
data() {
return {
fakeData: {
unloadingContactPersons: [
{
id: this.idGen("unloadingContactPersons"),
value: "123"
},
{
id: this.idGen("unloadingContactPersons"),
value: "1234"
},
{
id: this.idGen("unloadingContactPersons"),
value: "12345"
}
]
},
lengthDependentLoadings: [
"loadingDates",
"loadingAddresses",
"loadingContactPersons"
],
lengthDependentUnloadings: [
"unloadingDates",
"unloadingAddresses",
"unloadingContactPersons"
],
formData: {
unloadingContactPersons: [
{
id: this.idGen("unloadingContactPersons"),
value: ""
}
]
}
};
},
methods: {
idGen(string = "") {
// Math.random should be unique because of its seeding algorithm.
// Convert it to base 36 (numbers + letters), and grab the first 9 characters
// after the decimal.
return (
string +
"_" +
Math.random()
.toString(36)
.substr(2, 9)
);
},
addLine(id) {
console.log("id", id);
const parentName = id.split("_")[0];
const dependentArray = this.lengthDependentLoadings.includes(parentName)
? this.lengthDependentLoadings
: this.lengthDependentUnloadings;
dependentArray.forEach(objName => {
this.formData[objName]
? this.formData[objName].push({
id: this.idGen(objName),
value: ""
})
: null;
});
console.log("--length", this.formData.unloadingContactPersons.length);
},
removeLine(id) {
const parentName = id.split("_")[0];
const dependentArray = this.lengthDependentLoadings.includes(parentName)
? this.lengthDependentLoadings
: this.lengthDependentUnloadings;
dependentArray.forEach(objName => {
this.formData[objName] ? this.formData[objName].pop() : null;
});
console.log("--length", this.formData.unloadingContactPersons.length);
},
downloadTemplate(link) {
// fake fetch request
const getFunctionDummy = data =>
new Promise(resolve => setTimeout(resolve.bind(null, data), 1500));
// data setter
getFunctionDummy(this.fakeData).then(result => {
// set our data according to the template data
const templateKeys = Object.keys(result);
const templateData = result;
this.formData = {};
templateKeys.forEach((key, index) => {
let value = templateData[key];
console.log(value);
if (Array.isArray(value)) {
console.log("array", value);
this.formData[key] = value.map((item, id) => {
console.log("---from-template", item);
return {
id: this.idGen(key),
value: item.value
};
});
} else {
this.formData[key] = {
id: this.idGen(key),
value
};
}
});
});
}
},
mounted() {
// takes id number of item to be added
this.$root.$on("addLine", ({ value }) => {
console.log("---from-mounted", value);
this.addLine(value);
});
// takes id number of item to be removed
this.$root.$on("removeLine", ({ value }) => {
this.removeLine(value);
});
},
beforeDestroy() {
this.$root.$off("addLine");
this.$root.$off("removeLine");
}
};
</script>
/HelloWorld.vue
<template>
<div class="hello">
<div class="form-item">
<div class="form-item__label">
<label :for="formData.unloadingContactPersons"
>Contact person on unload:</label
>
</div>
<div class="form-item__input multiline__wrapper">
<div
class="multiline__container"
#mouseover="handleMouseOver(unloadingContactPerson.id);"
v-for="unloadingContactPerson in formData.unloadingContactPersons"
:key="unloadingContactPerson.id"
>
<span
class="hover-button hover-button__remove"
#click="removeLine(unloadingContactPerson.id);"
><i class="fas fa-minus-circle fa-lg"></i>-</span
>
<input
class="multiline__input"
:id="unloadingContactPerson.id"
type="text"
v-model="unloadingContactPerson.value"
#input="emitFormData"
/>
<span
class="hover-button hover-button__add"
#click="addLine(unloadingContactPerson.id);"
><i class="fas fa-plus-circle fa-lg"></i>+</span
>
</div>
</div>
</div>
</div>
</template>
<script>
import Datepicker from "vuejs-datepicker";
import { uk } from "vuejs-datepicker/dist/locale";
export default {
name: "SubmitForm",
components: {
Datepicker
},
props: {
formData: Object
},
data: () => {
return {
uk,
hoveredItemId: null
};
},
methods: {
emitFormData() {
this.$root.$emit("submitFormData", { value: this.formData });
},
handleMouseOver(id) {
this.hoveredItemId = id;
},
addLine(id) {
// console.log("---add", id);
this.$root.$emit("addLine", {
value: id
});
},
removeLine(id) {
// console.log("---remove", id);
this.$root.$emit("removeLine", {
value: id
});
}
}
};
</script>
Just comment line no 111 of App.vue and it will work.
// this.formData = {}
The problem is that you directly mutating formData object which Vue.js cannot detect. Read more about Array Change detection [List Rendering - Vue.js]
I'm new to vue.js and I'm tryeing to build a little application, where I in one case need to pass a prop between two components. For some reason it does not work and I don't know why.
Here is the first component, the Playlist.Vue component:
<template>
<div class="playlists-con">
<div class="playlists">
<h1>Available Playlists for category: {{id}}</h1>
<ul>
<router-link v-for="playlist in playlists" :to="`details/${playlist.id}`" tag="li" active-class="active" exact>
<div class="playlist-wrapper">
<div class="imgWrap">
<img :src="playlist.images[0].url" />
</div>
<a>{{playlist.name}}</a>
</div>
</router-link>
</ul>
</div>
<div>
<router-view category="id"></router-view>
</div>
</div>
</template>
<script>
export default {
data() {
return {
id: this.$route.params.id,
playlists : []
}
},
watch: {
'$route'(to, from) {
this.id = to.params.id
}
},
methods: {
fetchPlaylist() {
this.$http.get('' + this.id + '/playlists')
.then(response => {
return response.json()
})
.then(data => {
const playlist_items = data.playlists.items;
for (let key in playlist_items) {
this.playlists.push(playlist_items[key])
}
})
}
},
created() {
this.fetchPlaylist();
}
}
</script>
from the Playlist component, I'm supposed to be able to get to the Playlist details. I also want to pass the category prop to the PlaylistDetails.vue, so I tried to do <router-view category="id"></router-view> - but that does not work.
PlaylistDetails.vue component (where I want to display the category prop, passed from the Playlist.vue component) :
<template>
<div class="playlist-details">
<router-link :to="`/categories/${category}`">Go to playlists</router-link>
<h1>Playlist Details for Playlist: <span class="playlist-name">{{playlistName}}</span></h1>
<h1>category: {{ category }}</h1>
<ul>
<li v-for="track in tracks">
<p>{{ track.track.artists[0].name}} - {{ track.track.name }}</p>
</li>
</ul>
</div>
</template>
<script>
export default {
props: ['category'],
data() {
return {
id: this.$route.params.id,
tracks : [],
playlistName: ''
}
},
watch: {
'$route'(to, from) {
this.path = from.params.path
}
},
beforeRouteEnter(to, from, next) {
if (true) {
next();
} else {
next(false);
}
},
methods: {
fetchPlaylistDetails() {
this.$http.get('https://api.spotify.com/v1/users/spotify/playlists/' + this.id)
.then(response => {
return response.json()
})
.then(data => {
const playlist_tracks = data.tracks.items;
for (let key in playlist_tracks) {
this.tracks.push(playlist_tracks[key])
}
this.playlistName = data.name;
})
}
},
created() {
this.fetchPlaylistDetails();
}
}
</script>
What am I doing wrong?
UPDATE
Here is my router configuration:
export const routes = [
{
path: '', default: App
},
{
path: '/categories/:id/playlists', props: true, component: Playlists
},
{
path: '/categories/:id/details/:id', component: PlaylistDetails, props: true, beforeEnter: (to, from, next) => {
next();
}},
{path: '*', redirect: '/'}
]
You are half way there, you defined props:true on the route, which means every dynamic property that is matched in the url would be passed as a prop, so :
//this will pass 'id' as a prop to the playlist component
{
path: '/categories/:id/playlists', props: true, component: Playlists
},
So inside the playlist component you'll have this:
props: ['id'],
data() {
return {
playlists : []
}
},
The same is true for the details component:
//change the name of the param to distinguish it from the category id
{
path: '/categories/:id/details/:detailsId', component: PlaylistDetails, props: true, beforeEnter: (to, from, next) => {
next();
}},
And in PlaylistDetails.vue:
props: ['detailsId'],
....
methods: {
fetchPlaylistDetails() {
this.$http.get('https://api.spotify.com/v1/users/spotify/playlists/' + this.detailsId)
.then(response => {
return response.json()
})
.then(data => {
const playlist_tracks = data.tracks.items;
for (let key in playlist_tracks) {
this.tracks.push(playlist_tracks[key])
}
this.playlistName = data.name;
})
}
},
There're 2 ways to pass data between non-parent components. I would recommend to take a look at them, before trying solve issue with router-view:
Using Vue.bus
Using Vuex