I am currently experiencing an issue where the computed() property is not able to get data. Although data was already initiated in created() property. Am I doing it wrong? Please advise how I can fix this issue.
const randomPlayers = {
template:
`
<input type="text" v-model="search_player">
<div v-for="player in modPlayers" v-if="list_of_random_players!=null">
<p>{{player.firstname}}</p>
<p>{{player.lastname}}</p>
<div>
`,
props: ['data'],
data (){
return{
list_of_random_players: null,
search_player: null
}
},
created(){
this.get_random_players()
},
computed: {
modPlayers(){
return this.list_of_random_players.filter( person => {
return !this.search_player ||
( person.firstname.toLowerCase().indexOf(this.search_player.toLowerCase()) > -1 || person.lastname.toLowerCase().indexOf(this.search_player.toLowerCase()) > -1)
})
}
},
methods: {
get_random_players: function(){
$.post(
url:'random_url'
data: {
players: data
}
).done((success)=>{
this.list_of_random_players: JSON.parse(success)
})fail((err)=>{
console.log(err)
})
}
}
}
I get the following two errors:
(1) TypeError: Cannot read property 'filter' of null
(2) TypeError: this.list_of_random_players.filter is not a function
From Vue: "When a Vue instance is created, it adds all the properties found in its data object to Vue’s reactivity system. When the values of those properties change, the view will “react”, updating to match the new values."
So data is a function that returns an object but as mentioned by #Sovalina you are not returning it so you cannot access its properties. You need to add return and change null to []:
data() {
return {
list_of_random_players: [],
search_player: []
}
},
or you can do without return and like a regular object:
data: {
list_of_random_players: [],
search_player: []
}
When your Vue component is used multiple times, it is better to use it like a function(first case).
"When defining a component, data must be declared as a function that returns the initial data object. Why? Because there will be many instances created using the same definition. If we still use a plain object for data, that same object will be shared by reference across all instance created! By providing a data function, every time a new instance is created we can call it to return a fresh copy of the initial data."
Reference:link
It might be just a typo but you need to add : to methods as well.
Related
I am facing an issue where I have some template HTML in a component that relies on the computed getter of a Vuex method. As you can see in the template, I am simply trying to show the output of the computed property in a <p> tag with {{ getNumSets }}.
As I update the state with the UPDATE_EXERCISE_SETS mutation, I can see in the Vue devtools that the state is updated correctly, but the change is not reflected in the <p> {{ getNumSets }} </p> portion.
Template HTML:
<template>
...
<v-text-field
v-model="getNumSets"
placeholder="S"
type="number"
outlined
dense
></v-text-field>
<p>{{ getNumSets }}</p>
...
</template>
Component Logic:
<script>
...
computed: {
getNumSets: {
get() {
var numSets = this.$store.getters['designer/getNumSetsForExercise']({id: this.id, parent: this.parent})
return numSets
},
set(value) { // This correctly updates the state as seen in the Vue DevTools
this.$store.commit('designer/UPDATE_EXERCISE_SETS', {
id: this.exerciseId,
parentName: this.parent,
numSets: parseInt(value),
date: this.date
})
}
}
...
</script>
Vuex Store Logic:
...
state: {
designerBucket: []
},
getters: {
getNumSetsForExercise: (state) => (payload) => {
var numSets = 0
for (var i = 0; i < state.designerBucket.length; i++) {
if (state.designerBucket[i].id == payload.id) {
numSets = state.designerBucket[i].numSets
}
}
return numSets
}
},
mutations: {
UPDATE_EXERCISE_SETS(state, payload) {
state.designerBucket.forEach(exercise => {
if (exercise.id == payload.id) {
exercise.numSets = payload.numSets
}
})
}
}
Any insight is very appreciated!
P.S. I have also tried using a for (var i=0...) loop, looping over the indices and then using Vue.set() to set the value. This did update the value in the store as well, but the computed property is still not updating the template.
This turned into a bit of a long-winded answer, but bear with me.
Here's my hunch: since you're returning a function from your Vuex getter, Vue isn't updating your computed property on state changes because the function never changes, even if the value returned from it would. This is foiling the caching mechanism for computed properties.
Reactivity for Arrow Function Getters
One of the things to keep in mind when creating a getter like this, where you return an arrow function:
getNumSetsForExercise: (state) => (payload) => {
var numSets = 0
for (var i = 0; i < state.designerBucket.length; i++) {
if (state.designerBucket[i].id == payload.id) {
numSets = state.designerBucket[i].numSets
}
}
return numSets
}
...is that you're no longer returning actual state data from your getter.
This is great when you're using it to pull something from state that depends on data that's local to your component, because we don't need Vue to detect a change, we just need the function to access current state, which it does fine.
BUT, it may also lead to the trap of thinking that updating state should update the getter, when it actually doesn't. This is really only important when we try to use this getter in a computed property like you have in the example, due to how computed properties track their dependencies and cache data.
Computed Caching and Dependency Detection
In Vue, computed properties are smarter than they first seem. They cache their results, and they register and track the reactive values they depend on to know when to invalidate that cache.
As soon as Vue calculates the value of a computed property, it stores it internally, so that if you call the property again without changing dependencies, the property can return the cached value instead of recalculating.
The key here for your case is the dependency detection– your getter has three dependencies that Vue detects:
get() {
var numSets = this.$store.getters['designer/getNumSetsForExercise']({id: this.id, parent: this.parent})
return numSets
},
The getter: this.$store.getters['designer/getNumSetsForExercise']
this.id
this.parent
None of these values change when <v-text-field> calls your setter.
This means that Vue isn't detecting any dependency changes, and it's returning the cached data instead of recalculating.
How to Fix it?
Usually, when you run into these sorts of dependency issues, it's because the design of the state could be improved, whether by moving more data into state, or by restructuring it in some way.
In this case, unless you absolutely need designerBucket to be an array for ordering purposes, I'd suggest making it an object instead, where each set is stored by id. This would simplify the implementation by removing loops, and remove the need for your getter altogether:
...
state: {
designerBucket: {}
},
mutations: {
UPDATE_EXERCISE_SETS(state, payload) {
// Need to use $set since we're adding a new property to the object
Vue.set(state.designerBucket, payload.id, payload.numSets);
}
}
Now, instead of invoking a getter, just pull designerBucket from state and access by this.id directly:
<script>
...
computed: {
getNumSets: {
get() {
return this.$store.state.designerBucket[this.id];
},
set(value) {
this.$store.commit('designer/UPDATE_EXERCISE_SETS', {
id: this.exerciseId,
parentName: this.parent,
numSets: parseInt(value),
date: this.date
});
}
}
...
</script>
This should allow Vue to detect changes correctly now, and prevent the stale cache problem from before.
Edited: First import mapGetters from 'vuex' like this on the top of the script tag.
import { mapGetters } from "vuex"
Now in your computed object, add mapGetters and pass arguments to the getter method inside the get() method like this:-
computed: {
...mapGetters('designer',['getNumSetsForExercise']),
getNumSets: {
get() {
var numSets = this.getNumSetsForExercise({id: this.id, parent: this.parent})
return numSets
},
set(value) { // This correctly updates the state as seen in the Vue DevTools
this.$store.commit('designer/UPDATE_EXERCISE_SETS', {
id: this.exerciseId,
parentName: this.parent,
numSets: parseInt(value),
date: this.date
})
}
}
And see if it works.
I'm new to Vue.js and trying to display data that is requested from a server. I have created a new Vue app and defined a method:
methods: {
getData: function() {
// making a request, parsing the response and pushing it to the array.
console.log(arr);
return arr;
}
}
The method works OK and I can log the array to the console on a button click.
<button v-on:click="getData">Get me some data</button>
However, I'm not sure how to actually use this array in the app. I would like save it to a property and then display it for the user. At first, I was thinking that I could use computed properties like this:
computed: {
values: function() {
return this.getData;
}
}
... and display it to the user with a for loop:
<p v-for="value in values">{{ value }}></p>
At least this solution did not produce the desired result. I've probably misunderstood some of Vue's logic here.
You need to use data property.
data() {
return {
values: []
}
},
methods: {
getData: function() {
this.values = arr;
}
}
And loop the values
I'm dispatching an array of objects to the Veux store and trying to get the data using "getters" from Vuex. It returns undefined. It is however working for a boolean variable in the same Vuex. For the array, it is not giving a proper result
I am using Vuex to update an array of objects across vue components.
I am dispatching the array of objects
committing the array through "actions"
setting and updating the value through "mutations"
Declaring the array through "state"
finally, from the Vue component, accessing the array through "getters"
I expect Vuex to return the array of objects when I access using "getters"
The store code is as given below
export default new Vuex.Store({
state: {
loading: false,
compoData: []
},
mutations: {
setLoading(state, payload) {
state.loading = payload
},
setCompoData(state, payload) {
state.compoData = payload.data
}
},
actions: {
setLoading({
commit
}, payload) {
commit('setLoading', payload)
},
setCompoData({
commit
}, payload) {
commit('setCompoData', payload)
},
},
getters: {
loading(state) {
return state.loading
},
compoaData(state){
return state.compoData
}
}
})
Vue component code is as given below. I am dispatching the array of objects "compoData" from the component to the store
someFunction(){
.....
some code
.....
this.$store.dispatch('setLoading',false)
this.$store.dispatch('setCompoData',{data:this.compoData})
},
Getting the values from store
gettingCompoData(){
console.log(this.$store.getters.compoData)
},
Though it works fine with the computed variable "loading", it does not work with the "compoData" array of objects .
I tested it with the below function
test(){
console.log(this.compoData, this.loading);
this.$store.dispatch('setCompoData',{data:this.compoData})
console.log(this.$store.getters.compoData, this.$store.getters.loading)
},
And this is the screenshot of the result I got this log result from the "test" function
It is clear that I am not sending an undefined array. But what I am getting from the store getters is undefined. Strangely, it is working for the boolean variable "loading" and it does return a valid result. I'm baffled as to why it does not return the array of objects. please help me out...
Any help would be appreciated. Thank you
you have a typo.
in store you defined a getter compoaData and when getting values from store you're looking for compoData
Can I rely on this used inside data factory function as it was current component object instance? I couldn't find in docs what this is in data().
data() {
return {
results: [],
apiResource: this.$resource('url...'), // <-- this used here
loading: true,
}
},
Simple test shows that this is VueComponent instance here, but the question is if the framework allows using it this way.
Yes, you can rely on this in the data factory function pointing to the component, depending on how you define the function. It's the primary way of initializing local data with values from properties, for example.
props:["value"],
data(){
return {
localValue: this.value
}
}
If, however, you defined your data function with an arrow function, this will not be the component.
props:["value"],
data: () => {
// 'this' is NOT the component
return {
localValue: this.value // results in undefined
}
}
I think no
Perhaps you need
data() {
return {
results: [],
set apiResource(v){},
get apiResource()( return this.$resource('url...')), // <-- this used here
loading: true,
}
},
I have the code (vuejs2) -
Vue.component('competetion-list', {
template: `<div>{{totalCompetetions}}</div>`,
props: ['values'],
data: function () {
return { totalCompetetions: this.values.length}
}
})
Nothing is printed on the page but if I change the template value to
template: `<div>{{this.values.length}}</div>`
it prints 15. What am I doing wrong and how can I pass the props to the data?
Any help is much appreciated.
I was unable to assign the prop values to data totalCompetetions in the following way -
data: function () {
return { totalCompetetions: this.values.length}
}
But I was able to do it using the watch, computed, and methods properties.
With watch property -
watch: {
values: function(){
this.totalCompetitions = this.values;
}
}
With computed property -
computed:{
competition:{
get: function(){
return this.values.length;
}
}
With methods property -
methods:{
competitionn: function(){
return this.values.length;
}
}
But for computed and methods properties, I needed to set totalCompetetions in the following way -
For computed -
template: `<div><p>{{totalCompetitions = competition}}</p></div>` //without parenthesis
For methods -
template: `<div><p>{{totalCompetitions = competition()}}</p></div>` //with parenthesis
You code does work.
I guess the problem is your parent component. Did you pass the values correctly? for example:
<competetion-list :values="[1, 2, 3]"></competetion-list>
Besides, for your case I'd say computed properties is a better solution.
computed: {
totalCompetetions () {
return this.values.length
}
}
From the data() method, you should be able to reference the component's properties using this.
Try following:
Vue.component('competetion-list', {
template: `<div>{{totalCompetetions}}</div>`,
props: ['values'],
data: function () {
var data = { totalCompetetions: this.values.length}
return data
}
})
As validly mentioned in the comment, if values array is changing later, you may have to put a watcher on the prop and inside watcher, set totalCompetetions as this.values.length.