Error : You may have an infinite update loop in a component render function - vue.js

I am new to vuejs. I am trying to display checkbox on a table based on some condition. using bootstrapvue lib for checkbox. Below is my code,
template :
<tr v-for="(item, index) in data" :key="index">
<slot :row="item" >
<td v-for="(column, index) in columns" :key="index" v-if="checkForCol(item, column)">
<b-form-checkbox v-model="checked" name="check-button" disabled="disabled" switch></b-form-checkbox>
</td>
<td :key="index" v-else>
<span v-if="hasValue(item, column)">
{{itemValue(item, column)}}
</span>
</td>
</slot>
</tr>
Script :
data() {
return {
checked : false
}
},
methods: {
hasValue(item, column) {
return item[column] !== "undefined"
},
checkForCol(item, column) {
if(column === "statusInfo") {
this.checked = item[column] === "ONLINE" ? true : false
return true
}
},
itemValue(item, column) {
return item[column]
}
}
Column data:
[{
"label": "label 1",
"id": "5f123456",
"statusInfo": "OFFLINE",
},
{
"label": "label 2",
"id": "5f1236786",
"statusInfo": "ONLINE",
}]
for all "ONLINE" values not getting any error. but if any row is having "OFFLINE" then getting the infinite loop error. I have understood the problem that it is because of the mutation issue of this.checked. But don't have solution to resolve it. Any help would be helpful. Thanks in advance.

I have found alternative component vue-js-toggle-button which accepts the direct value instead of binding it to data variable. my requirement is achieved with vue-js-toggle-button.
With more studies about Vuejs, if any one has similar requirement then this can be achieved by creating new component for row element and maintaining statues of each row inside the each row component. If need more details let me know, I am happy to post some code sample.
Thanks

Related

Vuejs setting reactive style to component in v-for with vuex data

Not sure how to work this...
I'm displaying table rows, pulling my data with vuex. The style changes, but is not updated in the browser. Only when I reload or re-create the component, it shows the style change. Scratching my head on this and what would be the best way to set a reactive style that is ternary based off the data loaded in the v-for component ?
<tr #click="rowClick(item)" v-for="(item, index) in List" :style="[item.completed ? { 'background-color': 'red' } : { 'background-color': 'blue' }]" :key="index">
item.completed is a bool
I hope I was able to correctly reflect what you were trying to accomplish. I used a v-for on the template tag and after that line you could access the item.completed boolean value. I had to use a span element inside the tr to apply the styling.
Vue.createApp({
data() {
return {
List: [ // comes from your vuex
{
name: 'one',
completed: true,
},
{
name: 'two',
completed: false,
}
]
}
},
methods: {
rowClick(item) {
console.log(item)
}
}
}).mount('#demo')
<script src="https://unpkg.com/vue#next"></script>
<table id="demo">
<template v-for="(item, i) in List">
<tr>
<span #click="rowClick(item)" :style="item.completed ? { 'background-color': 'red' } : { 'background-color': 'blue' }">{{ i }}: {{ item.name }}</span>
</tr>
</template>
</table>
I think it could be solved a little bit cleaner. What do you think? Does it goes in the right direction?

vue2 list return mixed string or component

In loop like this, I mostly just iterate over item values as strings, but sometimes need to return rendered component, for example build link element, or dropdown menu, for that table cell - need to find a way to return other component output instead of raw html string
<tr class="listing-item listing-item-category">
<td v-for="td in headeritems">{{val(td.k)}}</td>
</tr>
Is that even possible? I've found no mention of this, how should the method code go to return other component output? I know I would have to use v-html, but how to get it?
Assume we have a list like this:
headerItems: [
{
type: 'text',
value: 'Some text'
},
{
type: 'img',
props: {
src: 'http://some-where....'
}
},
{
type: 'my-component',
value: 'v-model value',
props: {
prop1: 10,
prop2: 'Blah bla',
},
events: {
myEvt: () => console.log('myEvt has fired')
}
},
],
So, We can render it:
<tr>
<td
v-for="(item, i) in headerItems" :key="i"
>
<div v-if="item.type === 'text'"> {{ item.value }}</div>
<component
v-else
:is="item.type"
v-model="item.value"
v-bind="item.props"
v-on="item.events"
/>
</td>
</tr>

Creating a button dynamically in vuejs table

I am trying to create a table in vuejs for a personal project (I dont want to use a existing table) and I am facing probably a newbie problem.
I am trying to insert on my last column some buttons, but I dont know why, the grid is rendering my element tag instead of the element himself.
May someone explain to me why this does not work ? And, how can I create this feature ?
Fiddle: https://jsfiddle.net/y7830cvd/1/
<div id="app">
<div>
<vue-grid :rows="gridData" :title="nTitle"></vue-grid>
</div>
</div>
Vue.component('vue-grid', {
props: ['rows', 'title'],
template: `<div>
<h2>{{title}}</h2>
<div class="table-wrapper">
<table class="fl-table">
<thead>
<tr>
<th v-for="col in columns" :key="col.id" v-on:click="sortTable(col)">{{col}}</th>
</tr>
</thead>
<tbody v-if="rows.length > 0">
<tr v-for="row in rows" :key="row.id">
<td v-for="col in columns" :key="col.id">{{row[col]}}</td>
</tr>
</tbody>
</table>
</div>
</div>`,
computed: {
columns: function columns() {
if (this.rows.length == 0) {
return []
}
return Object.keys(this.rows[0])
}
},
sortTable(col) {
this.rows.sort(function(a, b) {
if (a[col] > b[col]) {
return 1
} else if (a[col] < b[col]) {
return -1
}
return 0
})
},
methods: {
formatter(row, column) {
return row.address
},
filterTag(value, row) {
return row.tag === value
},
filterHandler(value, row, column) {
const property = column['property']
return row[property] === value
}
}
});
var app = new Vue({
el: '#app',
data(){
return {
gridData: [
{"id" : 1, "name": "firstValue", "something": "wha the fox say?","options" : "<button>Add</button>" },
{"id" : 1, "name": "firstValue", "something": "uauu uauu uauu?"},
{"id" : 1, "name": "firstValue", "something": "The cow goes mu?"}
],
nTitle: "Hello There!"
}},
})
Try v-html:
<td v-for="col in columns" :key="col.id">
<span v-if="col == 'options'" v-html="row[col]"></span>
<span v-else>{{row[col]}}</span>
</td>
Something you should consider (source documentation - link above):
Updates the element’s innerHTML. Note that the contents are inserted
as plain HTML - they will not be compiled as Vue templates. If you
find yourself trying to compose templates using v-html, try to rethink
the solution by using components instead.

Passing an Argument Value to the Data Object as a Key in Vuejs

I'm creating a page that contains 4 different tables for now. All rows in each table will have update and delete buttons. I can make everything work when I code all CRUD functions for each table separately. But what I want to do is create a function for update action for example, and pass the table name as an argument.
<tr v-for="asset in assets" :key="asset.id">
<td>{{asset.id}}</td>
<td>{{asset.name}}</td>
<td>{{asset.category.name}}</td>
<td>
<a href #click.prevent="editDialog(asset, 'asset')">Edit</a>
<a href #click.prevent="deleteThis(asset.id)">Delete</a>
</td>
</tr>
This is the pop-up window for edit and create an item:
<v-dialog v-model="dialog">
<div v-if="editMode">
<div v-if="target_table == 'asset'">
<form>...</form>
</div>
</div>
<div v-else>...</div>
</v-dialog>
I have those things in script
export default {
data() {
return {
dialog: false,
editMode: false,
target_table: "",
assets: {},
asset: new Form({
id: "",
name: "",
category: "",
}),
}
},
methods: {
fetchAssets() {
axios.get("/api/assets").then(({ data }) => (this.assets = data));
},
editDialog(data, table) {
this.target_table = table;
this.editMode = true;
this.dialog = true;
this.table.fill(data);
},
},
}
And here is my problem. If you look at the last row of the code, I'm trying to fill a form named "asset" that comes with editDialog(asset, 'asset') as its second parameter. But with this syntax, Vuejs goes to data object and look for a "table" as a key instead of "asset" and I'm getting "table is not defined" error. How can I make it work? Thanks.
I suggest you to change this.table.fill(data); with this.$data[table].fill(data)
Let me know if it works

v-for custom component checkbox not working

I am using v-for with a custom component
Vue.component('lineitem', {
template: '#lineitem-template',
props: {
item: {
required: false,
default: null
},
},
computed: {
local_line_item() {
console.log("Re computing line item")
return _.clone(this.item)
},
},
methods: {
set_taxable(e) {
e.preventDefault();
if (this.local_line_item.taxable == false) {
this.local_line_item.taxable = true;
} else {
this.local_line_item.taxable = false;
}
console.log("Changing taxable to ", this.local_line_item);
},
}
});
with this template:
<template id="lineitem-template">
<tr>
<td>
<input type="text" class="form-control" v-model="local_line_item.cost">
</td>
<td>
<div class="option" #click="set_taxable">
<input type="checkbox" v-model="local_line_item.taxable" v-bind:true-value="true" v-bind:false-value="false">
<label for="check1"></label>
</div>
</td>
</tr>
</template>
see this JSFiddle: https://jsfiddle.net/shaunc869/1Ly7mL6n/7/
When I click the checkbox I am changing the value of the internal variable, but the checkbox doesn't change, what am I doing wrong? Thanks!
e.preventDefault() inside set_taxable is the cause of problem. Removing that part works just fine. You can see the updated fiddle here: https://jsfiddle.net/1Ly7mL6n/9/
Also, I don't understand your rationale behind using the click binding to trigger set_taxable. Since you have already used v-model binding to toggle local_line_item.taxable in accordance with the checked state of the checkbox, you do not need a click handler for it. So unless you plan to do some more operations in the click handler, you could remove that as well.