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" />
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 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.
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 have a button that creates new elements, when I create a new element I add it to the 'added' array and I do a v-for, I try to pass the index to be able to enumerate but it tells me that it is not defined
In the parent template:
<tr is="child" v-for="(item, index) in added" :key="index" v-on:remove="added.splice(index, 1)"
:id="item.id"
:name="item.name"
:lastname="item.lastname"
:pack="item.pack"
:type="item.type"
:size="item.size"
:color="item.color">
</tr>
In the child template.
<tr class="no-border-top">
<th class="align-middle" scope="row">{{ index }}</th>
</tr>
Log:
[Vue warn]: Property or method "index" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property. See: https://v2.vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.
I have 2 components. One is a list component (list.vue). The other takes a list and wraps it with other features(searchandlist). Now, from the page.vue file I would like to call SearchAndList, giving it the list, and the render props. However, I cant get the dynamic data to show, only static.
ListItems.vue
<span>
<div v-for="item in items" :key="item.id">
<slot v-bind="item"></slot>
</div>
SearchAndList.vue
<div class="clients-list">
<div class="table-responsive">
<table class="table table-striped table-hover">
<list-items :items="items">
<slot name="row"></slot>
</list-items>
</table>
</div>
</div>
page.vue
<template>
<search-and-list :items="items">
<tr slot="row">
Hi
</tr>
</search-and-list>
</template>
<script>
import SearchAndList from '../components/base/SearchAndList'
export default {
components: {
SearchAndList
},
data() {
return {
items: [
{ id: 10, name: 'Marc' },
{ id: 11, name: 'Bob' },
{ id: 12, name: 'George' }
]
}
}
}
</script>
When using this, I get Hi listed out 3 times as exprected. However, when changing this to:
<search-and-list :items="items">
<tr slot="row" slot-scope="item">
{{ item.name}}
</tr>
</search-and-list>
I do get "Duplicate presence of slot "default" found in the same render tree - this will likely cause render errors." in the console, however, even giving the default slot a name=list, the error is the same, but default is not list.
Im sure there is something simple that I am missing. Any guidance would be great.
EDIT:
I have a child component () that exposes an { item } to its parent (). However, I would like to access { item } in the grand-parent (page.vue).
There is 2 missing bit in your configuration.
Expose you item in the List.vue:
<span>
<div v-for="item in items" :key="item.id">
<slot :item="item"></slot>
</div>
Then you need to bind the name in your SearchAndList.vue.
So try:
<div class="clients-list">
<div class="table-responsive">
<table class="table table-striped table-hover">
<list-items :items="items">
<slot scope-slot="{ item }" :name="item.name"></slot>
</list-items>
</table>
</div>