How to get array length in vuejs - javascript

This is my script tag
<script>
export default {
data() {
return {
blogs: [],
};
},
created() {
this.paginate_total = this.blogs.length / this.paginate;
},
};
</script>
these the response I get in my console
{
"blogs": [
{
"_id": "63243272c988e721db51de9c",
},
{
"_id": "63243cb8a8189f8080411e65",
},
]
}
error i get in my console
Cannot read properties of undefined (reading 'length')
Please what I'm I doing wrong

Place this line in mounted instead of created
this.paginate_total = this.blogs.length / this.paginate;
because this blog is not available in created yet that's why it is undefined.

You can't access the object array length in mounted or created lifecycles so I just used watch property to get the object length
watch: {
blogs(blogs) {
this.paginate_total = blogs.length / this.paginate;
}
}

For row counts (as for table pagination etc.) i would use a computed property like:
computed: {
paginate_total() {
return this.blogs.length;
},
}

Related

Filter Array Using Partial String Match in Javascript

I have an array of objects where the value I need to filter on is buried in a long string. Array looks like:
{
"data": {
"value": "{\"cols\":[\"parent_sku\"],\"label\":\"Style\",\"description\":\"Enter Style.\",\"placeholderText\":\"Style 10110120103\"}",
"partnerId": 1
}
},
So if I wanted to grab all the partnerId objects where value includes parent_sku how would I do that?
console.log(data.value.includes('parent_sku') returns cannot read property 'includes' of null.
EDIT:
Didn't think this mattered, but judging by responses, seems it does. Here's the full response object:
Response body: {
"data": {
"configurationByCode": [
{
"data": {
"value": "{\"cols\":[\"parent_sku\"],\"label\":\"Style\",\"description\":\"Enter Style.\",\"placeholderText\":\"Style 10110120103\"}",
"partnerId": 1
}
}
I'm passing that into a re-usable function for filtering arrays:
const parentSkuPartners = filterArray(res.body.data.configurationByCode, 'parent_sku');
Function:
function filterArray(array, filterList) {
const newList = [];
for (let i = 0; i < array.length; i += 1) {
console.log('LOG', array[i].data.value.includes('parent_sku');
}
}
The problem is somewhere else. The code you've tried should work to find if a value contains a string – I've added it the snippet below and you'll see it works.
The issue is how you are accessing data and data.value. The error message clearly states that it believes that data.value is null. We would need to see the code around it to be able to figure out what the problem is. Try just logging to console the value of data before you run the includes function.
const data = {
"value": "{\"cols\":[\"parent_sku\"],\"label\":\"Style\",\"description\":\"Enter Style.\",\"placeholderText\":\"Style 10110120103\"}", "partnerId": 1
};
console.log('includes?', data.value.includes('parent_sku'));
You can use data.value.includes('parent_sku') as you have suggested. The issue here is that your object is nested inside an unnamed object.
try:
"data": {
"value": "{\"cols\":[\"parent_sku\"],\"label\":\"Style\",\"description\":\"Enter Style.\",\"placeholderText\":\"Style 10110120103\"}",
"partnerId": 1
}
The problem was some of the values for value were null. Adding an extra conditional fixed it:
if (array[i].data.value !== null) {
Use lodash includes, and lodash filter like
let configurationByCode = [{
data: {
value: {
cols:["parent_sku"],
label:"Style",
description:"Enter Style.",
placeholderText:"Style 10110120103"
},
"partnerId": 1
}
}, {
data: {
value: {
cols:["nothing"],
label:"Style",
description:"Enter Style.",
placeholderText:"Style 10110120103"
},
"partnerId": 2
}
}];
let wantedData = _.filter(configurationByCode, (config) => {
return _.includes(config.data.value.cols, 'parent_sku');
});
console.log( wantedData );
https://jsfiddle.net/76cndsp2/

update dynamic object in redux using spread operator

I am trying to update an object in redux using spread operator but I am not being able to.
Initial state is an empty object because category is received dynamically from api call.
pages and data are both objects which i want to update using spread operator (or whatever works best)
state = {
[category]: {
pages: {
key: value(array)
},
data: {
key: value(array)
}
}
}
At my reducer I try to update it like this
return {
...state,
[category]: {
...state[category],
pages: { ...state[category].pages, pages },
data: { ...state[category].data, doctors },
total: total,
},
};
but i get "error: TypeError: Cannot read property 'pages' of undefined"
What am I doing wrong and how can I update them correctly?
Because state.category is undefined when you fetch this category for the first time. You can fix it like that:
return {
...state,
[category]: state.category
? {
...state[category],
pages: { ...state[category].pages, pages },
data: { ...state[category].data, doctors },
total: total,
}
: {
pages,
data: doctors,
total,
},
};

Vuex Getters doesn't call state

I trying to return back sorted data (which is the already defined state) in a list with the help of a getter, then assign it to the html list in my vue, but it seems it's empty when I check with the vuex tools.
I don't know what am doing wrong.
Below is my store.js file
export default {
namespaced: true,
state:{
displayChatMessages: [],
},
mutations:{
create(state, payload) {
state.displayChatMessages.push(payload)
},
reset(state){
state.displayChatMessages = []
},
},
actions :{
getAllData:({commit}, payload) => {
commit('create',payload)
},
},
getters:{
filteredChatMessages: state => (chatID) => {
return state.displayChatMessages[0]
.filter(el => el.groupid === chatID).sort((l,r)=> l.timestamp - r.timestamp)
},
},
}
Then, after, I call it in the computed area like below :
...mapGetters('chatMessages',['filteredChatMessages']),
Then , I call the Getter inside my function , like below :
getFilteredMessages: function() {
let vm = this
return vm.filteredChatMessages(vm.groupID)
},
Then afterwards, then I set the getFilteredMessages() to the list , getFilteredMessages() , is also defined in the computed section.
But when I look into my vuex tools , I don't see it as an array :
What am I doing wrong ?

Vuex doesn't react with complex object

I've started to use Vuex in order to remplace the EventBus, because the data in my app has started to get a little bit complex.
In my context, I have a question entity, with multiple answers, when the user insert another answer I want to show the last one; (here I use two different components: one to show the answers and other to answer the question) but when the server response OK with the new answer, and the mutation change the state.answers, the computed property doesn't react and doesn't show the new answer:
Here is my data structure:
"answers": {
"118": {
"id": 118,
"description": "objective",
"created_at": "2019-11-12T19:12:36.015Z",
"dojo_process_id": 1,
"question_id": 1,
"user_id": 10
}
"127": {
"id": 127,
"description": "asdddd",
"created_at": "2019-11-12T19:38:19.233Z",
"dojo_process_id": 1,
"question_id": 1,
"user_id": 10
},
"128": {
"id": 128,
"description": "asddddasddd",
"created_at": "2019-11-12T20:00:17.572Z",
"dojo_process_id": 1,
"question_id": 1,
"user_id": 10
}
},
Here is the code for my store:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);
export const store = new Vuex.Store({
state: {
...
answers: {},
...
},
getters: {
findBy: state=> filter => {
let result= Object.values(state[filter.model]).
filter(data => data[filter.field] === filter.criteria);
return result;
}
},
mutations: {
setAnswers(state, answers) {
state.answers = answers;
},
setAnswer(state, answer) {
state.answers[answer.id] = answer;
},
},
actions: {
replaceCompleteProcess(context, data) {
...
context.commit('setAnswers',data.answers);
...
},
cleanCompleteProcess(context) {
...
context.commit('setAnswers',{});
...
},
saveAnswer(context, answer) {
context.commit('setAnswer', answer);
}
}
});
And this is how the script of my component is structured:
export default {
name: "show-question",
computed: {
question: function () {
return this.$store.getters.question(this.questionId)
},
answers: function () {
return this.$store.getters.findBy({
model: 'answers',
field: 'question_id',
criteria: this.questionId,
sort: true
});
},
answer: function () {
if (this.answers && this.answers.length > 0) {
return this.answers[0].description;
} else {
return '';
}
}
},
props: {
questionId: {
type: [Number],
default: 0
}
},
data() {
return {
sending: false,
answerData: this.answer
}
},
methods: {
sendAnswer () {
this.sending = true;
questionConnector.answerQuestion(this,this.question.id,this.dojoProcessId, this.answerData)
},
// this one is called from AXIOS
answerWasOK(answer) {
this.sending = false;
this.$store.dispatch('saveAnswer', answer);
this.answerData = '';
}
}
}
So, if I understand how to use Vuex, when I call this.$store.dispatch('saveAnswer', answer), the state will be updated, and the computed property answers would be updated, and I'll be able to show the new changes in the component, But it doesn't work.... the computed property just doesn't react.
I had read a lot about vuex and how "it not work well" with complex data, so I normalize my data. but it is the same... also I tried to use vuex-orm, but I have a lot of problems with the one-many relation, and I cant do it work.
EDIT: Solution
I did a small test with the ideas from the answers and it works
setAnswer(state, answer) {
let newAnswers = state.answers;
state.answers = {};
newAnswers[answer.id] = answer;
state.answers = newAnswers;
}
When you are working with Objects you have to do it like this
setAnswer(state, answer) {
Vue.set(state.answers, answer.id, answer);
},
This is clearly mentioned in the documentation.
When adding new properties to an Object, you should either:
Use Vue.set(obj, 'newProp', 123), or
Replace that Object with a fresh one. For example, using the object spread syntax we can write it like this:
state.obj = { ...state.obj, newProp: 123 }
You are storing a list of answers inside an object. Nothing wrong with that, since you know how to deal with. It turns out that Vue.js observers don't track new object attributes (which is exactly what you are doing there, creating new attributes, instead of modifying a list/array).
My first suggestion is to change this object to an array. But if you can't, due to your ORM or other reason of your project standard, you should take a look about Reactivity of Vue.js. The quickest solution, in my opinion, is to use a watcher:
https://v2.vuejs.org/v2/api/#watch
Some links that can be helpful to understand Vue.js reactivity:
Reactivity in Depth
https://v2.vuejs.org/v2/guide/reactivity.html
How to actively track an object property change?
https://forum.vuejs.org/t/how-to-actively-track-an-object-property-change/34402/1

How do you run a function when any data in an object is changed using Vuejs?

So I have the following data, and my goal is to recalculate the user's results every time data in this object is changed. Here is the data.
data() {
return {
test: 0,
userData: {
sex: null,
age: null,
feet: null,
inches: null,
activity: null,
goal: null,
},
}
}
Now I have tried to implement both watch and computed, but it seams Vue is not noticing when individual items in the object are changed. However, if I take some data out of the object it does notice the change.
Here is what I tried for watch:
watch: {
userData: function () {
console.log("Changed");
}
}
The result was nothing in the console.
For computed I tried the following:
computed: {
results: function () {
console.log(this.userData);
return this.userData.sex;
}
}
But again nothing was printed in the console.
If I tried with the test variable:
watch: {
test: function () {
console.log("Changed");
}
}
It WOULD output changed when the variable was changed. So that works because it is not an object.
Any help would be very appreciated. Again the goal is to recalculate results whenever any userData is changed.
Here is one way to do it. You need (as #match mentioned) use Vue.set() or vm.$set(). I found it was also necessary to update your watcher property to userData.sex.
new Vue({
el: "#app",
data: {
status: '',
userData: {
sex: ''
},
},
methods: {
updateValues(){
// Or Vue.set()
this.$nextTick( function(){
const newUserData = Object.assign({}, this.userData);
newUserData.sex = "Male";
this.userData = newUserData;
});
}
},
watch: {
userData: function (v) {
this.status += "userData Changed";
},
'userData.sex': function (v) {
this.status += "\nuserData.sex Changed";
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue#2.6.8/dist/vue.min.js"></script>
<div id="app">
<pre v-text="'userData.sex = ' + userData.sex"></pre>
<pre v-text="status"></pre>
<button #click="updateValues()">
Change Sex
</button>
</div>
EDIT:
Re-assigning the whole object, userData, triggers the watch.userData.
Are you actually using the results property (in your template for example)? Computed properties do not get recomputed if they are not being used.
As opposed to what #match says, I doubt you have a reactivity problem since you do not add or delete properties (they already exist in your data so they are already reactive).

Categories

Resources