How to use state in data function in vue - vue.js

Having problem to use state in data function of Vue.
I tried this
this.items = this.$store.state.search_items
but it always results in an empty array like this
[__ob__: Observer]
length: 0
__ob__: Observer {value: Array(0), dep: Dep, vmCount: 0}
__proto__: Array
Any help would be appreciated. Thank you !!
Here is the mutation from which I am stating search_items
this is working because I am able to see state.search_items in Vue dev tool
SET_WORKSPACES(state, payload) {
state.workspaces = payload;
var items = [];
if (state.workspaces.length) {
state.workspaces.forEach(function(workspace) {
// Adding workspaces
if (workspace.portfolios.length) {
items = [...items, ...workspace.portfolios];
}
// Adding projects
if (workspace.projects.length) {
items = [...items, ...workspace.projects];
// Adding tasks
workspace.projects.forEach(function(project) {
if (project.tasks.length) {
items = [...items, ...project.tasks];
}
});
}
});
}
state.search_items = items;
}
Data property
data() {
return {
results: [],
keys: ['title','description'],
list:this.items,
}
},

If the state is updating correctly, the time taking for setting the state is making the issue. That means, the the function you're using to assign the state to data is rendering before the state set.
You can use a computed function that returning the state
computed:{
items : function(){
return this.$store.state.search_items;
}
}
Now you can use the computed function name items same like a data variable.

Related

How to pass an array of objects to child component in VueJS 2.x

I am trying to send an array containing arrays which in turn contains objects to one component from another, but the content from the array seems to be empty in the child component.
I have tried sending the data as a String using JSON.Stringify() and also as an array
My parent component:
data: function(){
return{
myLineItems : []
}
},
created(){
this.CreateLineItems();
},
methods:{
CreateLineItems(){
let myArrayData = [[{"title":"Title1","value":2768.88}],[{"title":"Title2","value":9}],[{"title":"Title3","value":53.61},{"title":"Title4","value":888.77},{"title":"Title5","value":1206.11},{"title":"Title6","value":162.5}]]
this.myLineItems = myArrayData;
}
}
My parent component's template:
/*
template: `<div><InvoiceChart v-bind:lineItems="myLineItems"></InvoiceChart></div>`
My child component:
const ChildComponent= {
props: {
lineItems: {
type: Array
}
},
mounted() {
console.log(this.lineItems);
}
};
The parent component is created as so (inside a method of our main component):
var ComponentClass = Vue.extend(InvoiceDetails);
var instance = new ComponentClass({
propsData: { invoiceid: invoiceId }
});
instance.$mount();
var elem = this.$refs['details-' + invoiceId];
elem[0].innerHTML = "";
elem[0].appendChild(instance.$el);
If I try to do a console.log(this) inside the childcomponent, I can see the correct array data exist on the lineItems property..but i can't seem to access it.
I have just started using VueJS so I haven't quite gotten a hang of the dataflow here yet, though I've tried reading the documentation as well as similar cases here on stackoverflow to no avail.
Expected result: using this.lineItems should be a populated array of my values sent from the parent.
Actual results: this.lineItems is an empty Array
Edit: The problem seemed to be related to how I created my parent component:
var ComponentClass = Vue.extend(InvoiceDetails);
var instance = new ComponentClass({
propsData: { invoiceid: invoiceId }
});
instance.$mount();
var elem = this.$refs['details-' + invoiceId];
elem[0].innerHTML = "";
elem[0].appendChild(instance.$el);
Changing this to a regular custom vue component fixed the issue
Code - https://codesandbox.io/s/znl2yy478p
You can print your object through function JSON.stringify() - in this case all functions will be omitted and only values will be printed.
Everything looks good in your code.
The issue is the property is not correctly getting passed down, and the default property is being used.
Update the way you instantiate the top level component.
Try as below =>
const ChildComponent= {
props: {
lineItems: {
type: Array
}
},
mounted() {
console.log(this.lineItems);
}
};

Vue.js 2: action upon state variable change

I am using a simple state manager (NOT vuex) as detailed in the official docs. Simplified, it looks like this:
export const stateholder = {
state: {
teams: [{id: 1, name:'Dallas Cowboys'}, {id: 2, name:'Chicago Bears'}, {id: 3, name:'Philadelphia Eagles'}, {id:4, name:'L.A. Rams'}],
selectedTeam: 2,
players: []
}
getPlayerList: async function() {
await axios.get(`http://www.someapi.com/api/teams/${selectedTeam}/players`)
.then((response) => {
this.state.players = response.data;
})
}
}
How can I (reactively, not via the onChange event of an HTML element) ensure players gets updated (via getPlayerList) every time the selectedTeam changes?
Any examples of simple state that goes a little further than the official docs? Thank you.
Internally, Vue uses Object.defineProperty to convert properties to getter/setter pairs to make them reactive. This is mentioned in the docs at https://v2.vuejs.org/v2/guide/reactivity.html#How-Changes-Are-Tracked:
When you pass a plain JavaScript object to a Vue instance as its data
option, Vue will walk through all of its properties and convert them
to getter/setters using Object.defineProperty.
You can see how this is set up in the Vue source code here: https://github.com/vuejs/vue/blob/79cabadeace0e01fb63aa9f220f41193c0ca93af/src/core/observer/index.js#L134.
You could do the same to trigger getPlayerList when selectedTeam changes:
function defineReactive(obj, key) {
let val = obj[key]
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter() {
return val;
},
set: function reactiveSetter(newVal) {
val = newVal;
stateholder.getPlayerList();
}
})
}
defineReactive(stateholder.state, 'selectedTeam');
Or you could set it up implicitly using an internal property:
const stateholder = {
state: {
teams: [/* ... */],
_selectedTeam: 2,
get selectedTeam() {
return this._selectedTeam;
},
set selectedTeam(val) {
this._selectedTeam = val;
stateholder.getPlayerList();
},
players: []
},
getPlayerList: async function() {
/* ... */
},
};
Your question is also similar to Call a function when a property gets set on an object, and you may find some more information there.
You could use v-on:change or #change for short to trigger getPlayerList.
Here a fiddle, simulating the request with setTimeout.

How to update an object in 'state' with react redux?

In my reducer, suppose originally I have this state:
{
"loading": false,
"data": {
"-L1LwSwW97KkwdSnYvsc": {
"likeCount": 10,
"liked": false, // I want to update this property
"commentCount": 5
},
"-L1EY2_fqzn7sM1Mbf_F": {
"likeCount": 8,
"liked": true,
"commentCount": 22
}
}
}
Now, I want to update liked property inside -L1LwSwW97KkwdSnYvsc object, which is inside data object and make it true. This is what I've been trying, but apparently, it's wrong, because after I've updated the state, the componentWillReceiveProps function inside a component that listens to the state change does not get triggered:
var { data } = state;
data['-L1LwSwW97KkwdSnYvsc'].liked = !data['-L1LwSwW97KkwdSnYvsc'].liked;
return { ...state, data };
Could you please specify why it's wrong and how I should change it to make it work?
You're mutating state! When you destructure:
var { data } = state;
It's the same as:
var data = state.data;
So when you do:
data[…].liked = !data[…].liked
You're still modifying state.data which is in turn mutating state. That's never good - use some nested spread syntax:
return {
...state,
data: {
...state.data,
'-L1LwSwW97KkwdSnYvsc': {
...state.data['-L1LwSwW97KkwdSnYvsc'],
liked: !state.data['-L1LwSwW97KkwdSnYvsc'].liked
}
}
};
Using spread operator is good until you start working with deeply nested state and/or arrays(remember spread operator does a shallow copy only).
I would rather recommend you starting working with immutability-helper instead. It is a React recommendation and it will let your code more readable and bug free.
Example:
import update from "immutability-helper";
(...)
const toggleLike = !state.data["-L1LwSwW97KkwdSnYvsc"].liked
return update(state, {
data: {
"-L1LwSwW97KkwdSnYvsc": {
like: {
$set: toggleLike
}
}
}
})

Watch all properties of a reactive data in Vue.js

I had an API call to the backend and based on the returned data, I set the reactive data dynamically:
let data = {
quantity: [],
tickets: []
}
api.default.fetch()
.then(function (tickets) {
data.tickets = tickets
tickets.forEach(ticket => {
data.quantity[ticket.id] = 0
})
})
Based on this flow, how can I set watcher for all reactive elements in quantity array dynamically as well?
You can create a computed property, where you can stringify the quantity array, and then set a watcher on this computed property. Code will look something like following:
computed: {
quantityString: function () {
return JSON.stringify(this.quantity)
}
}
watch: {
// whenever question changes, this function will run
quantityString: function (newQuantity) {
var newQuantity = JSON.parse(newQuantity)
//Your relevant code
}
}
Using the [] operator to change a value in an array won't let vue detect the change, use splice instead.

Vuejs2 - computed property in components

I have a component to display names. I need to calculate number of letters for each name.
I added nameLength as computed property but vuejs doesn't determine this property in loop.
var listing = Vue.extend({
template: '#users-template',
data: function () {
return {
query: '',
list: [],
user: '',
}
},
computed: {
computedList: function () {
var vm = this;
return this.list.filter(function (item) {
return item.toLowerCase().indexOf(vm.query.toLowerCase()) !== -1
})
},
nameLength: function () {
return this.length; //calculate length of current item
}
},
created: function () {
this.loadItems();
},
methods: {
loadItems: function () {
this.list = ['mike','arnold','tony']
},
}
});
http://jsfiddle.net/apokjqxx/22/
So result expected
mike-4
arnold-6
tony-4
it seems there is some misunderstanding about computed property.
I have created fork from you fiddle, it will work as you needed.
http://jsfiddle.net/6vhjq11v/5/
nameLength: function () {
return this.length; //calculate length of current item
}
in comment it shows that "calculate length of current item"
but js cant get the concept of current item
this.length
this will execute length on Vue component it self not on that value.
computed property work on other property of instance and return value.
but here you are not specifying anything to it and used this so it wont able to use any property.
if you need any more info please comment.