Infinite loop warning in vue table rendering - vue.js

I tried calling a function while rendering a table, and based on condition in the function i assigned that value and displayed it using string interpolation, but i am getting infinite loop error.
Below is url for code
jsfiddle.net/amit_bhadale/5kct0au1/2/
Below is function
checkData(day, time){
let that = this
let result = this.serverData.some(a=>{
if(a.Day === day && a.Time === time){
that.cellId = a.id
// This is giving infinite loop error
// if i chnage it to this then it works
// that.cellId = 'Some normal string'
}
return (a.Day === day && a.Time === time)
})
return result
}
HTML part
<table>
<tr v-for="(time, i) in time" :key="i">
<td v-for="(day, j) in day" :key="j">
<span v-if="checkData(day, time)">
</span>
<span v-else>
No data in this cell
</span>
</td>
</tr>
</table>

Dont update props multiple times with different values within in render cycle.
To seperate those you can just put it into a single component:
e.g.:
{
props: ['time', 'day', 'serverData'],
computed: {
cellValue(){
let val = "No data in this cell"
this.serverData.some(a=>{
if(a.Day === this.day && a.Time === this.time){
val = a.id;return true;
}
})
return val
}
}
}
<template>
<span>cellValue</span>
</template>

Related

validating multiple fields with same name not working in vue.js

I would like to validate multiple fields with same name but for some reason its not working.If i make first input blank,nothing happens.If i make second input blank ,i get errors message on first input.Not sure what i am doing wrong.
<tr v-for="(name, index) in form"
:key="index">
<td>
<div>
<input
class="input"
:class="{
'is-danger': hasErrorName[index] === true
}"
#keyup.prevent="validateField('name',index)"
type="text"
v-model="name.description"
>
</div>
<span v-if="hasErrorName[index] === true"
class="help is-danger">
{{ msgName[index] }}
</span>
</td>
</tr>
data() {
return {
form: {},
hasErrorName: {},
msgName: {},
};
},
method() {
validateField(field,index) {
if (field === 'name') {
if (!this.form[index].description) {
this.hasErrorName[index] = true;
this.msgName[index] = 'Name is required.';
} else {
this.hasErrorName[index] = false;
this.msgName[index] = null;
}
}
}

Vue 2 list band members by instrument name but only display the instrument name once

I am able to list the band members by instrument name, but want to display the instrument name only once, instead of showing the instrument name for each person in the bassoon group, for instance. My thought was to extract a set of distinct instrument names, and do a v-if comparing the member's instrument name to the iterated set value, and only display the member if the member's data property instrumentName matches the current value of the instrument names set. With the code below, I'm getting an error "instNames is not defined". I'm still learning Vue and Nuxt, and am trying to get away from WebMatrix, the original platform for my website. The other thing I need is to attach an instrumentFilename (picture) to each distinct instrumentName. Any help is greatly appreciated!
<template>
<div class="block bg-blue-200 w-96 p-6 m-auto">
<table>
<tr v-for="inst in instNames">
<td>{{ inst.instrumentName }} <img :src="'/images/' + inst.instrumentFilename" >
<td v-for="member in members" :key="member.id">
<div v-if="member.instrumentName == inst.instrumentName"> </div>
<div>{{ member.firstname }} {{ member.lastname }} </div>
<div v-if="$auth.loggedIn" class="block">
<p v-if="!member.email == ' '" class="ml-10"> Email: {{ member.email }} </p>
<p v-if="!member.telephone == ' '" class="ml-10"> Home: {{ member.telephone }} </p>
<p v-if="!member.telephone2 == ' '" class="ml-10">Cell: {{ member.telephone2 }}</p>
<p v-if="!member.street == ' '" class="ml-10">Address: {{ member.street}}</p>
<p v-if="!member.city == ' '" class="ml-28">{{ member.city}}, {{ member.zip}}</p>
</div>
</td>
</tr>
</table>
</div>
</template>
<script>
export default {
async asyncData(context){
const { data } = await context.$axios.get('/api/members')
return {
members : data
}
},
computed: {
instNames: function() {
instNames = [...new Set(this.members.map(x => x.instrumentName))]
return instNames
}
}
}
</script>
The above comments were invaluable, so a million thanks. I still had trouble with the uniqueness of my return array though; apparently, a Set counts multiple references to an object as unique. So I wound up using this code:
computed: {
instNames: function() {
const instName = this.members.map
(({ instrumentName, instrumentFilename }) => ({ instrumentName, instrumentFilename}))
const instNameArray = []
const instNameSet = new Set()
for (const object of instName) {
const objectJSON = JSON.stringify(object)
if(!instNameSet.has(objectJSON)) {
instNameArray.push(object)
}
instNameSet.add(objectJSON)
}
console.log(instNameArray)
return instNameArray
}
}
Your computed property tries to assign instNames, but that's not declared anywhere. However, you don't really need the local variable because it's not used. You can just return the calculated set:
export default {
computed: {
instNames: function() {
//instNames = [...new Set(this.members.map(x => x.instrumentName))]
//^^^^^^^^^ ❌ not declared
// Option 1: Declare it with `let` or `const`
const instNames = [...new Set(this.members.map(x => x.instrumentName))]
return instNames
// Option 2: Just return the calculation
return [...new Set(this.members.map(x => x.instrumentName))]
}
}
}
But your template tries to render instrumentName and instrumentFilename off of the computed instNames (an array of strings, not objects). Assuming those values are from this.members, you could update the computed value to return those fields:
export default {
computed: {
instNames: function() {
const instName = this.members.map(x => ({
instrumentName: x.instrumentName,
instrumentFilename: x.instrumentFilename,
}))
// remove duplicates here ...
return instName
}
}
}
Lastly, your template v-if conditions aren't properly checking for non-empty strings. For !member.email == ' ', the ! operator has precedence, and it checks member.email for a falsy value, which is the empty string for String. Then it compares it to a single space, which would always be false because a space is not falsy. You can replace that entire comparison with a simple check for a truthy value:
<p v-if="member.email">...</p>
<p v-if="member.telephone">...</p>
<p v-if="member.telephone2">...</p>
<p v-if="member.street">...</p>
<p v-if="member.city">...</p>

Form collection validation with dates and string - Vuelidate

I am trying to validate series of dates with something like this.
const data = [
{begin: new Date('2019-12-01'), place: '2'},
{begin: new Date('2019-12-03'), place: '3'}
... more values
];
// Elements inside data can be added or removed but will have at least one.
Here data[1][begin] should be more than or equal to data[0][begin] and data[1][place] should not equal to data[0][place]. Is there anyway to achieve this. Documentation talks about dynamic validation but I am not sure how I can achieve this with collection.
You can consider implementing a custom validation in the form submit event listener.
This can be achieved by looping through your array of objects and compare items in pairs.
HTML
<form
id="app"
#submit="checkForm"
action="/someurl"
method="post"
>
<table border="1">
<tr v-for="(item,index) in dates" :key="index">
<td>
{{index}}
</td>
<td>
{{formatDate(item.begin)}}
</td>
<td>
{{item.place}}
</td>
</tr>
</table>
<input type="date" v-model="dateEntry"/>
<input type="text" v-model="placeEntry"/>
<button type="button" #click="addEntry">Add</button>
<p>
<br>
<input
type="submit"
value="Submit"
>
</p>
<p v-for="error in errorList">
{{error}}
</p>
</form>
JS
new Vue({
el: "#app",
data: {
errorList: [],
dateEntry: null,
placeEntry: null,
dates: [
{begin: new Date('2019-12-01'), place: '2'},
{begin: new Date('2019-12-03'), place: '3'}
]
},
methods: {
addEntry: function(){
if(this.dateEntry == null || this.dateEntry == "")
return false;
if(this.placeEntry == "")
return false;
this.dates.push({
begin: new Date(this.dateEntry),
place: this.placeEntry
});
this.dateEntry = null;
this.placeEntry= "";
},
checkForm: function(e){
var isValid = true;
var index = 0;
var nextIndex = 1;
this.errorList = [];
while(nextIndex < this.dates.length){
if(nextIndex < this.dates.length){
var isValidDate = this.validDate(this.dates[nextIndex].begin,this.dates[index].begin);
var isValidPlace = this.validPlace(this.dates[nextIndex].place,this.dates[index].place);
if(!isValidDate){
this.errorList.push("Invalid date on index " + nextIndex);
}
if(!isValidPlace){
this.errorList.push("Invalid place on index " + nextIndex);
}
}
index++;
nextIndex++;
}
if(!this.errorList.length){
this.errorList.push("All dates are valid");
return true;
}
e.preventDefault();
},
formatDate: function(date){
return date.toDateString();
},
validPlace: function(curPlace, prevPlace){
return curPlace != prevPlace;
},
validDate: function(curDate,prevDate){
try{
return curDate.getTime() >= prevDate.getTime();
}catch(e){
return false;
}
}
}
})
Check out this JS Fiddle that I created to illustrate my suggestion.
On the other hand, if you are building the array during runtime, then you can apply the validation before it gets added into the array.

Infinite loop with v-for inside a v-for

Can someone help me find out how I can achieve a loop inside a loop to print out a report of people and peoples annual leave.
Basically I have this:
<div v-for="user_list in user_list_filtered()">
<div class="user_heading"><h2>{{ user_list.first_name }}</h2></div>
<div class="report_content" v-for="user_leave in user_leave_filtered(user_list['.key'])">
<div class="first_date_content">
{{ user_leave.start_time | formatDate }}
</div>
<div class="days_taken_content">
{{ checkLSL(user_leave.hours, user_leave.type, false) }}
</div>
</div>
<div class="leave_content">
<div class="total_leave_title">
Total Leave Taken
</div>
<div class="total_hours">
{{ getTotalLeave() }}
</div>
</div>
</div>
So this iterates through a Firebase DB and gets all of the users, it then loops through each user one by one and using the [.key] finds that users leave and loops through all of the leave as it outputs. Then onto the next user and there leave.
While this works it will continue to loop infinitely and says in my console.
vue.esm.js?efeb:591 [Vue warn]: You may have an infinite update loop in a component render function.
Can someone explain maybe a better way to do this without the infinite loop or a solution to avoid it constantly looping?
Thanks
EDIT
Filter functions
user_leave_filtered(userPassed) {
var self = this
return this.userLeave.filter(function(i) {
if (i.users_id === userPassed &&
((i.start_time >= self.getUnix(self.firstDate) && i.start_time <= self.getUnix(self.lastDate)) ||
(self.firstDate === null || self.firstDate === '' || self.lastDate === null || self.lastDate === ''))) {
return true
} else {
return false
}
})
},
user_list_filtered() {
var self = this
return this.userList.filter(function(i) {
var passed = false
if (self.userToShow === i['.key'] || self.userToShow === 'All') {
// Track whether to filter out this leave or not
self.userLeave.forEach(function(element) {
if (element.users_id === i['.key']) {
passed = true
}
})
}
return passed
})
},
The cause was a function (checkLSL()) I was using to calculate some totals through the loops. I have some info on how to fix and seems to be working fine.
Thanks

VueJS Computed Data Search Not Functioning

I am attempting to create a search function in my Vue.js 2 application. However, even though my algorithm is giving me the right results, I am not getting the proper filter. Right now, whenever I run a search, I get nothing on the page. Here is my code:
computed:{
filteredSmoothies: function(){
return this.smoothies.filter(smoothie => {
var stuff = smoothie.title.split(" ").concat(smoothie.description.split(" ")).concat(smoothie.category)
var sorted = [];
for (var i = 0; i < stuff.length; i++) {
sorted.push(stuff[i].toLowerCase());
}
console.log(sorted)
if(this.search != null){
var indivthings = this.search.split(" ")
indivthings.forEach(item => {
if(sorted.includes(item.toLowerCase())){console.log("true")} else {console.log("false")}
if(sorted.includes(item.toLowerCase())){return true}
})
} else {
return true
}
})
}
}
Here is my relevant HTML:
<div class="container">
<label for="search">Search: </label>
<input type="text" name="search" v-model="search">
</div>
<div class="index container">
<div class="card" v-for="smoothie in filteredSmoothies" :key="smoothie.id">
<div class="card-content">
<i class="material-icons delete" #click="deleteSmoothie(smoothie.id)">delete</i>
<h2 class="indigo-text">{{ smoothie.title }}</h2>
<p class="indigo-text">{{ smoothie.description }}</p>
<ul class="ingredients">
<li v-for="(ing, index) in smoothie.category" :key="index">
<span class="chip">{{ ing }}</span>
</li>
</ul>
</div>
<span class="btn-floating btn-large halfway-fab pink">
<router-link :to="{ name: 'EditSmoothie', params: {smoothie_slug: smoothie.slug}}">
<i class="material-icons edit">edit</i>
</router-link>
</span>
</div>
</div>
As the discussions in the comments, the root cause should be:
you didn't return true/false apparently inside if(this.search != null){}, it causes return undefined defaultly.
So my opinion is use Javascript Array.every or Array.some. Also you can use for loop + break to implement the goal.
Below is one simple demo for Array.every.
computed:{
filteredSmoothies: function(){
return this.smoothies.filter(smoothie => {
var stuff = smoothie.title.split(" ").concat(smoothie.description.split(" ")).concat(smoothie.category)
var sorted = [];
for (var i = 0; i < stuff.length; i++) {
sorted.push(stuff[i].toLowerCase());
}
console.log(sorted)
if(this.search != null){
var indivthings = this.search.split(" ")
return !indivthings.every(item => { // if false, means item exists in sorted
if(sorted.includes(item.toLowerCase())){return false} else {return true}
})
} else {
return true
}
})
}
or you can still use forEach, the codes will be like:
let result = false
var indivthings = this.search.split(" ")
indivthings.forEach(item => {
if(sorted.includes(item.toLowerCase())){result = true}
})
return result
filteredSmoothies is not returning a list. You iterate over to see if indivthings is contained within sorted, but you don't do anything with that information. I think you need to modify your sorted list based on the results of indivthings.