I use the v-money package to mask a money input field like this:
<template v-if="serviceContract.services">
<tr
v-for="service in serviceContract.services"
:key="service.id"
>
<td>
<money
v-model.trim="service.unit_price_excl_tax"
v-bind="money"
#input="calcPricesFromUnitPrice(service)"
/>
</td>
</tr>
</template>
Methods:
calcPricesFromUnitPrice(service) {
// here I do some calculation stuff with that service variable
}
I always get the Vue warning
You may have an infinite update loop in a component render function.
I have no idea what this means or why it is giving me this warning.
Related
I'm trying to build a webapp with Vue. I have a component that serves as a template and a vue where I use the template and fill it with props.
For this I have an array from which I retrieve data from :
<MyComponent :myArray="[{dataName: 'dataValue'}] />
It works fine usually but not when I try to pass the prop as a function argument.
In MyComponent :
<table>
<tr v-for="item in myArray" :key='item.id'>
<td>
<button #click="myFunction( {{ item.dataName }} )">ClickMe</button>
</td>
</tr>
</table>
I tried to pass the argument and to pass the entire function name + argument but neither worked.
What am I doing wrong ?
Thank you,
Nehrz
I'm getting this error in my console
[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders.
Instead, use a data or computed property based on the prop's value. Prop being mutated: "catProducts"
Here is my vue component
<template>
<div>
<table class="table table-striped">
<thead>
<tr>
<th>Product Name</th>
<th>Remove</th>
</tr>
</thead>
<tbody>
<tr v-for="product in catProducts">
<td>
{{ product.name }}
</td>
<td>
<button #click="remove(product)" class="btn btn-danger">Remove</button>
</td>
</tr>
</tbody>
</table>
</div>
</template>
<script>
export default{
props: ['category', 'catProducts'],
data(){
return {
}
},
methods: {
remove(product){
axios.delete('/category/products/'+this.category.id+'/'+product.id).then(response => {
this.catProducts = response.data.products;
});
}
}
}
</script>
what response.data.products; returns is all the products that belong to that category.
Even though my code does what it supposed to do in terms of it deletes the product for that category,
I do still get the Avoid mutating a prop error in my console and I'm not sure how to fix it.
There are two things here that go wrong:
Changing/mutating the prop catProducts directly
Solution: emit an event like 'deleteProduct' to a parent components so that it can call axios.delete and then fetch a refreshed list of products and overwrite a list of products that is passed as a prop to a child component.
Returning new product list from HTTP DELETE method.
Solution: call axios.delete and if it's successful then call axios.get to get a list of products and overwrite a list of products that is passed as a prop to a child component.
After the delete call finishes
remove(product){
axios.delete('/category/products/'+this.category.id+'/'+product.id).then(response => {
this.catProducts = response.data.products;
}
);
}
Instead of mutating the prop directly emit an update event so you can use sync modifier in the parent component to update the prop.
remove(product){
axios.delete('/category/products/'+this.category.id+'/'+product.id).then(response => {
this.$emit("update:catProducts", response.data.products);
}
);
}
Then in the parent component if catProducts is component's state.
<CatProductsRender :catProducts.sync="catProducts" />
I'm running into a strange issue with a complex loop I'm attempting within my component.
<template v-for="(scorecard, scorecardIndex) in scorecards">
<template v-for="(property, propertyIndex) in properties">
<!-- Primary player -->
<tr v-if="scorecardIndex === 0"
v-bind:key="propertyIndex"> // THIS LINE
</tr>
</template>
<!-- Secondary players, should only show score -->
<tr v-if="scorecardIndex > 0"
v-bind:key="scorecardIndex">
</tr>
</template>
Where I'm setting v-bind:key="propertyIndex" I'm getting the following error within VSCode
[vue/valid-v-for]
Expected 'v-bind:key' directive to use the variables which are defined by the 'v-for' directive.
The weird thing about this is that I don't actually get an error on my vue compiler, just within VSCode. This makes me think it has something to do directly with VSCode and not necessarily Vue itself.
You're getting this error from eslint which checks your code for possible errors and bad coding style.
When you use v-for on <template>, you need to make sure you define key on each top-level element within the <template>. This is because <template> is not an actual DOM element (within a Vue template) and so each element within it will be repeated at the same level in the DOM which is why key is required on each element at that DOM tree level.
You have a nested <template>, each using v-for, so it gets a bit more complicated. You need to make sure that the key of the first <tr> uses variables defined by both v-fors. Although in your specific case, you are using v-if to limit the element being created to only the first row, so eslint is being too pedantic here. You can silence the warning like this:
<!-- eslint-disable-next-line vue/valid-v-for -->
<tr v-if="scorecardIndex === 0"
v-bind:key="propertyIndex">
</tr>
But I think you have the order around the wrong way. It makes more sense to first check if it is the first row and then repeat a bunch of <tr>s, instead of the other way around:
<template v-if="scorecardIndex === 0">
<!-- Primary player -->
<tr v-for="(property, propertyIndex) in properties"
v-bind:key="propertyIndex">
</tr>
</template>
Lastly, you will get some duplicate key warnings because both sets of <tr>s will use keys 0, 1, 2, and so on. Maybe prefix them:
<template v-for="(scorecard, scorecardIndex) in scorecards">
<template v-if="scorecardIndex === 0">
<!-- Primary player -->
<tr v-for="(property, propertyIndex) in properties"
v-bind:key="'primary-' + propertyIndex">
</tr>
</template>
<!-- Secondary players, should only show score -->
<tr v-if="scorecardIndex > 0"
v-bind:key="'secondary-' + scorecardIndex">
</tr>
</template>
So I have a Vuetify data table with a lot of items, I need to display 50/100/150 items.
The problem, when I have 50 items it takes about 2 to 3 seconds to load the entire table. My request takes about 500ms to load and vue takes 3 seconds to render. When I have 150 items it takes about 10 seconds.
Is it possible to reduce render time to at least 5 seconds with 150 items?
I'm using this table: https://vuetifyjs.com/en/components/data-tables#data-tables
My table code:
<template>
<v-data-table
:headers="headers"
:items="desserts"
:items-per-page="5"
class="elevation-1"
:items-per-page="50"
:footer-props="rowsPerPageItems"
>
<TableRow :row="item" v-for="item in clientList" :key="item.id">
</v-data-table>
</template>
// my clientList is async response from server
import { mapGetters } from "vuex";
export default {
data(){
return {
rowsPerPageItems: [50, 100, 150]
}
},
computed: {
...mapGetters["clientList"] // this comes from vuex, I don't think it's relevant. It returns about 400 client info
}
}
<!-- My component TableRow -->
<template>
<tr>
<td>
<v-checkbox v-model="row.checked" color="primary"></v-checkbox>
</td>
<td>{{ row.data }}</td>
<td>{{ row.name }}</td>
<td>
<!-- This is a jQuery plugin, I need it to keep track of every key pressed -->
<Select2
:id="row.id.toString()"
:settings="{ minimumInputLength: 3, ajax: ajaxData, placeholder: labels.descriptionList[row.description] }"
#select="handleSelect2($event)"
/>
</td>
<td v-if="this.is_expense">
<v-select
:items="labels.categoryName"
:placeholder="labels.categoryList[row.category]"
#input="updateCashflow"
v-model="row.category"
dense
></v-select>
</td>
<td v-else></td>
<td>{{ row.amount }}</td>
</tr>
</template>
Remove that component and read v-data-table documentation.
You shouldn't add v-checkbox to make it rows selectable, use show-select prop instead with v-model on v-data-table
Use item.<name> slot to add custom components into cells
In my case, I really need components inside my table so I used
<v-select v-if="mountedComponent" :items="myList"/>
<span v-else>Some default value</span>
When page mounted I set mounted to true
export default {
name: "Test",
data(){
return {
mountedComponent: false,
myList:["item 1", "item 2"]
}
},
mounted(){
this.mountedComponent= true;
}
}
This solved my performance issue, going from 10 ~ 15 seconds delay to 100ms.
I would like to iterate rows in a table based over a range. But all examples I've seen show how to do this for a constant for example,
<div>
<span v-for="n in 10">{{ n }} </span>
</di
How can I do this for a variable similar to this,
<table>
<tr v-for="(el, index) in form.repeat" :key="index">
<td>hello</td>
</tr>
</table>
This variable form.repeat is set in a form on the page.
Thanks
I had this issue week, my workaround for it was setting a variable in your data section to the number of rows you want. In my case based on the number of devices my backend returned the length of the list as part of the response. When Vue receives this response. I assign the variable that I created earlier in my data section to this number and I plug that into my v-for loop.
In the data section:
data: () => ({'num_pis': 0})
In the template part, reference the number. This is just a snippet, I'm using a v-data-table from Vuetify.
<template slot="items" slot-scope="props">
<td class="table-content-style">{{ props.item.metric_name }}</td>
<td v-for="i in num_pis">
{{ props.item[i] }}
</td>
</template>
Codepen Example