How to call a function when store has data - vue.js

I have to create a video player object but I need the stream object to be present before I create the video player.
The this.stream is populated by vuex data store. but I find the mounted() and created() methods don't wait for store data to be present.
Here is the Player.vue component:
import Clappr from 'clappr';
import { mapActions, mapGetters } from 'vuex';
import * as types from '../store/types';
export default {
name: 'streams',
data() {
return {
player: null
};
},
computed: {
...mapGetters({
stream: types.STREAMS_RESULTS_STREAM
}),
stream() {
return this.$store.stream;
}
},
methods: {
...mapActions({
getStream: types.STREAMS_GET_STREAM
})
},
mounted() {
this.getStream.call(this, {
category: this.$route.params.category,
slug: this.$route.params.slug
})
.then(() => {
this.player = new Clappr.Player({
source: this.stream.url,
parentId: '#player',
poster: this.stream.poster,
autoPlay: true,
persistConfig: false,
mediacontrol: {
seekbar: '#0888A0',
buttons: '#C4D1DD'
}
});
});
}
};
Is there a way to wait for this.stream to be present?

You can subscribe on mutation event, For example if you have a mutation in your store called setStream you should subscribe for this mutation. Here's a code snippet of how to subscribe in mounted method.
mounted: function () {
this.$store.subscribe((mutation) => {
if (mutation.type === 'setStream') {
// Your player implementation should be here.
}
})
}

Related

How to use this data from Vuex inside the component?

What is the best way to use this data from the Vuex store's Action in the needed component?
import axios from 'axios'
export default {
namespaced: true,
state: {
items: []
},
actions: {
fetchCategories ({state, commit}) {
return axios.get('/api/v1/categories')
.then(res => {
const categories = res.data
commit('setItems', {resource: 'categories', items: categories}, {root: true})
return state.items
})
}
}
}
Component
export default {
components: {
ThreadCreateModal,
ThreadList
},
data () {
return {
...
}
},
computed: {
...
},
created () {
...
},
methods: {
...
}
</script>
Where and how should I use that action for binding the data in this component?
Use mapState by import it from vuex and call it in computed:
computed: {
...mapState(['items']), // if you dont use namespace
...mapState("your_module_name", ['items'] ) // if you use namespace
},
then you can access it by this.items.
However, you can access it directly this.$store.state.items

Correct way to cache data in vuex

I am trying to asynchronously load data into vuex that is static but is used by multiple routes.I only want to fetch the data once and only when a route that needs it is visited. This is what I'm currently doing but I'm not sure if this is the correct convention or if there is a better/more Vueish way.
// store.js
export default new Vuex.Store({
state: {
_myData: null,
},
getters: {
myData: (state) => new Promise((resolve,reject) => {
if(state._myData){
resolve(state._myData);
} else {
axios.get('http://myData.com/')
.then(myData => {
state._myData = myData;
resolve(state._myData);
});
}
})
}
});
// ProfilePage.vue
<template>
<div>{{myData}}</div>
</template>
<script>
export default {
data() {
return {
myData:null
}
},
async created(){
this.myData = await this.$store.getters.myData;
}
}
</script>
// AboutPage.vue
<template>
<div>{{myData}}</div>
</template>
<script>
export default {
data() {
return {
myData:null
}
},
async created(){
this.myData = await this.$store.getters.myData;
}
}
</script>
There is a correct way to do what you want but it is not the way you are doing it. Vue is quite strict on "Do not mutate vuex store state outside mutation handlers."
This means you should only alter the store state through a mutation, then use your getter only to get the data. You should also use an action to commit the mutation. So for what you are trying to do you should try it like this.
// AnyPage.vue
<template>
<div>{{myData}}</div>
</template>
<script>
export default {
data() {
return {
myData:null
}
},
async created(){
if(this.$store.state._myData === null) {
await this.$store.dispatch('getData')
}
this.myData = this.$store.getters.myData;
}
}
</script>
then in your store:
// store.js
export default new Vuex.Store({
state: {
_myData: null,
},
getters: {
myData: (state) => state._myData,
}
mutations: {
SET_DATA(state, data) {
state._myData = data
}
}
actions: {
getData({ context }){
axios.get('http://myData.com/')
.then(res => {
context.commit('SET_DATA', res)
})
}
}
}
});
You should read up in the docs which covers it all pretty well.
Action handlers receive a context object which exposes the same set of methods/properties on the store instance, so you can call context.commit to commit a mutation, or access the state and getters via context.state and context.getters.
https://vuex.vuejs.org/guide/actions.html
try this:
// AnyPage.vue
<template>
<div>{{myData}}</div>
</template>
<script>
export default {
computed: {
myData () {
return this.$store.state.myData
}
},
mounted () {
this.$store.dispatch('getData')
}
}
</script>
in store file:
// store.js
export default new Vuex.Store({
state: {
myData: null,
},
mutations: {
SET_DATA(state, data) {
state.myData = data
}
}
actions: {
getData({ context }){
if (context.state.myData === null) {
axios.get('http://myData.com/')
.then(res => {
context.commit('SET_DATA', res)
})
}
}
}
}
});

VueJS - vue-charts.js

I am trying to pass data I fetch from API to vue-chartjs as props, I am doing as in the documentation but it does not work.
Main component
<monthly-price-chart :chartdata="chartdata"/>
import MonthlyPriceChart from './charts/MonthlyPriceChart'
export default {
data(){
return {
chartdata: {
labels: [],
datasets: [
{
label: 'Total price',
data: []
}
]
},
options: {
responsive: true,
maintainAspectRatio: false
}
}
},
components: {
MonthlyPriceChart
},
created() {
axios.get('/api/stats/monthly')
.then(response => {
let rides = response.data
forEach(rides, (ride) => {
this.chartdata.labels.push(ride.month)
this.chartdata.datasets[0].data.push(ride.total_price)
})
})
.catch(error => {
console.log(error)
})
}
}
In response I have an array of obejcts, each of which looks like this:
{
month: "2018-10",
total_distance: 40,
total_price: 119.95
}
Then I want to send the data somehow to the chart so I push the months to chartdata.labels and total_price to chartdata.datasets[0].data.
chart component
import { Bar } from 'vue-chartjs'
export default {
extends: Bar,
props: {
chartdata: {
type: Array | Object,
required: false
}
},
mounted () {
console.log(this.chartdata)
this.renderChart(this.chartdata, this.options)
}
}
console.log(this.chartdata) outputs my chartsdata object from my main component and the data is there so the data is passed correctly to chart but nothing is rendered on the chart.
The documentation says this:
<script>
import LineChart from './LineChart.vue'
export default {
name: 'LineChartContainer',
components: { LineChart },
data: () => ({
loaded: false,
chartdata: null
}),
async mounted () {
this.loaded = false
try {
const { userlist } = await fetch('/api/userlist')
this.chartData = userlist
this.loaded = true
} catch (e) {
console.error(e)
}
}
}
</script>
I find this documentation a bit vague because it does not explain what I need to pass in chartdatato the chart as props. Can you help me?
Your issue is that API requests are async. So it happens that your chart will be rendered, before your API request finishes. A common pattern is to use a loading state and v-if.
There is an example in the docs: https://vue-chartjs.org/guide/#chart-with-api-data

Vue - Data not computed in time before mount

I'm learning Vue and I've run into a problem where my data returns undefined from a computed method. It seems that the data is not computed by the time the component is mounted, probably due to the get request - wrapping my this.render() in a setTimeout returns the data correctly. Setting a timeout is clearly not sensible so how should I be doing this for best practice?
Home.vue
export default {
created() {
this.$store.dispatch('retrievePost')
},
computed: {
posts() {
return this.$store.getters.getPosts
}
},
methods: {
render() {
console.log(this.comments)
}
},
mounted() {
setTimeout(() => {
this.render()
}, 2000);
},
}
store.js
export const store = new Vuex.Store({
state: {
posts: []
},
getters: {
getPosts (state) {
return state.posts
}
},
mutations: {
retrievePosts (state, comments) {
state.posts = posts
}
},
actions: {
retrievePosts (context) {
axios.defaults.headers.common['Authorization'] = 'Bearer ' + context.state.token
axios.get('/posts')
.then(response => {
context.commit('retrievePosts', response.data)
})
.catch(error => {
console.log(error)
})
}
}
})
It is because axios request is still processing when Vue invokes mounted hook(these actions are independent of each other), so state.posts are undefined as expected.
If you want to do something when posts loaded use watch or better computed if it's possible:
export default {
created() {
this.$store.dispatch('retrievePost')
},
computed: {
posts() {
return this.$store.getters.getPosts
}
},
methods: {
render() {
console.log(this.comments)
}
},
watch: {
posts() { // or comments I dont see comments definition in vue object
this.render();
}
}
}
P.S. And don't use render word as methods name or something because Vue instance has render function and it can be a bit confusing.

How can i have access to the dispatch event from the created method in vue?

I'm trying to have access to the data from the component, i can have access to it using the template but i actually want to be able to have access to the dispatch users details from the created method.
How can i have access to it?
<template>
<div>
<input type='text' class='e_pearl ep_pearl form-control' placeholder='Perle..' autoComplete='false' autoFocus spellCheck='false' :value='user.pearl_color' >
</div>
</template>
<script>
var colors = '#006064';
export default {
mixins: [userMixin],
components: {
Sidebar,
'swatches-picker': Swatches
},
methods: {
created(){
let {
session: { email },
$store: { dispatch }
} = this
dispatch('userDetails', email)
console.log(this.$data.colors); // access to data from created
},
data () {
return {
colors
}
},
mounted(){
$('.ep_password').focus()
}
}
</script>
I tried:
console.log(this.$store.state.user);
{ob: Observer}
session
:
(...)
userDetails
:
(...)
module
import actions from '../actions/user-a'
import $ from 'jquery'
let d = $('.data')
export default {
state: {
session: {
id: d.data('session'),
email: d.data('email')
},
userDetails: {}
},
mutations: {
USER_DETAILS: (state, payload) => state.userDetails = payload,
},
actions,
}
action
import { post } from 'axios'
export default {
userDetails: async (context, payload) => {
console.log(payload);
let { data } = await post('/api/get-details', { email: payload })
context.commit('USER_DETAILS', data)
},
}
If i try console.log(this.$store.state.userDetails);
Data are not available however i can see them from this.$store.state.user
But no luck!