vue.js - How to split array of objects and display in two different columns - vue.js

I have two columns with name and age.The jason returns the following object.I would to return the value under name and age column.
Students: {
details: [
{ John: 21 },
{ Brian: 22 },
{ Ryan: 21 },
<tbody>
<tr v-for= "(item,index) in Students.details" :key="index" >
<td ">
{{item --(should display name)}}
</td>
<td">
{{item --(should display age)}}
</td >
</tr>
</tbody>

This could work out:
<tbody>
<tr v-for= "(item, index) in Students.details" :key="index" >
<td>
{{Object.keys(item)[0]}}
</td>
<td>
{{item[Object.keys(item)[0]]}}
</td >
</tr>
</tbody>

There are many ways to solve your problem, but I suggest to arrange the data first to a simpler form then render them. For example, use computed to change the form first.
computed: {
dataArray() {
return Object.keys(this.Students).map(name => {
return {
name: name,
age: this.Students[name]
}
})
}
}
// you can get an array [{name: "John", age: 21}, {name: "Brian", age: 22}, ...]
Then in the template just show the data:
<tr v-for= "item in dataArray" :key="item.name">
<td>
{{ item.name }}
</td>
<td>
{{ item.age }}
</td >
</tr>

Related

Vue 3 how to remove item from array with filter method?

I want to delete specific item from array with filter method. This is how I add new items to array first:
addNewInvoiceItem() {
this.invoiceItemList.push({
id: Date.now(),
itemName: "",
qty: "",
price: 0,
total: 0,
});
},
Template:
<tr
class="table-items flex"
v-for="item in invoiceItemList"
:key="item.id"
>
<td class="item-name">
<input type="text" v-model="item.itemName" />
</td>
<td class="qty"><input type="text" v-model="item.qty" /></td>
<td class="price">
<input type="text" v-model="item.price" />
</td>
<td class="total flex">
{{ (item.total = item.qty * item.price) }}
</td>
<img
src="#/assets/trash-bin.png"
alt="Delete icon"
#click="deleteInvoiceItem(item.id)"
/>
</tr>
And delete method:
deleteInvoiceItem(id) {
this.invoiceItemList = this.invoiceItemList.filter(
(item) => item.id == !id
);
},
There is second delete method, which is working correctly:
deleteInvoiceItem(item) {
this.invoiceItemList.splice(this.invoiceItemList.indexOf(item), 1);
},
But I want to know, why method with filter is not working?
I found solution. There was a mistake in this method:
deleteInvoiceItem(id) {
this.invoiceItemList = this.invoiceItemList.filter(
(item) => item.id == !id
);
},
it should be:
(item) => item.id !== id

display a foreign data in a table vuejs

I am creating a page, in which a section called product, I show data and in this I have a data called id_categoria, where instead of showing the id I want to show the name of that category in question, this data is in the table of categories and in the product table I have the id. I already managed to show the data, but not this, besides that I managed to save and edit it in question, it is only necessary in show.
When working with vuejs I saw that you have to use a v-if but I do not know how to do it, or at least the attempts I have made have been wrong.
this is the code of the table where I show the data
<div class="card-body table-responsive">
<table id="example1" class="table table-bordered table-striped">
<thead>
<tr>
<th>id_producto</th>
<th>imagen</th>
<th>código</th>
<th>producto</th>
<th>categoria</th>
<th>stock</th>
<th>precio_compra</th>
<th>precio_venta</th>
<th>fecha</th>
<th colspan="2" class="text-center">Acciones</th>
</tr>
</thead>
<tbody>
<tr v-for="pro in productos" :key="pro.id_productos">
<th>{{pro.id_productos}}</th>
<td>
<img :src="pro.imagen" class="img-circle elevation-2" alt="Product Image" width="60" />
</td>
<td>{{pro.codigo}}</td>
<td>{{pro.producto}}</td>
<td>{{pro.id_categoria}}</td>
<td>{{pro.stock}}</td>
<td>{{pro.precio_compra}}</td>
<td>{{pro.precio_venta}}</td>
<td>{{pro.fecha}}</td>
<td class="text-center">
<button #click="modificar=true; abrirModal(pro)" type="button" class="editar btn btn-primary"><i class="fa fa-pencil-alt"></i></button>
</td>
<td class="text-center">
<button #click="eliminar(pro.id_productos,pro.producto)" type="button" class="eliminar btn btn-danger" data-toggle="modal" data-target="#modalEliminar"><i class="fas fa-dumpster-fire"></i></button>
</td>
</tr>
</tbody>
<tfoot>
<tr>
<th>id_producto</th>
<th>imagen</th>
<th>código</th>
<th>producto</th>
<th>categoria</th>
<th>stock</th>
<th>precio_compra</th>
<th>precio_venta</th>
<th>fecha</th>
<th colspan="2" class="text-center">Acciones</th>
</tr>
</tfoot>
</table>
</div>
and this is the script to bring the info to me
<script>
import axios from "axios";
export default {
watch: {
$route: {
immediate: true,
handler(to, from) {
document.title = to.meta.title || 'Productos';
}
},
},
data() {
return{
productosdatos:{
id_producto: "",
codigo: "",
producto: "",
stock: "",
precio_compra: "",
precio_venta : "",
id_categoria: "",
},
id: 0,
modificar: true,
modal: 0,
tituloModal: '',
productos:[],
categorias:[],
}
},
methods: {
async listarcategorias(){
const res = await axios.get('http://localhost:8000/categoria/');
this.categorias = res.data;
console.log(this.categorias)
},
async listar(){
const res = await axios.get('http://localhost:8000/productos/');
this.productos = res.data;
console.log(this.productos)
},
cerrarModal(){
this.modal = 0;
}
},
created() {
this.listar();
}
}
</script>
as you can see I have a variable called products, where is the id_category corresponding to the product, and categories where I bring all the category info.
the table looks something like this:
how can I make it not show the id of the category but the name of the category in question ?
pd: the category data is received in json as follows:
{
"id_categoria": 8,
"categoria": "Electrodomesticos",
"fecha": "2021-10-24 13:55:00"
}
thank you if you can help me to show the name of the category thank you
You can implement a function like this:
const findCategory = (id)=>{this.categorias.find(category=>category.id_categoria===id)?.categoria}
And in the template:
<td>{{findCategory(pro.id_categoria)}}</td>
Only one small change. In your code you have to edit. pro.id_categoria to pro.categoria. see comment inline.
<tr v-for="pro in productos" :key="pro.id_productos">
<th>{{pro.id_productos}}</th>
<td>
<img :src="pro.imagen" class="img-circle elevation-2" alt="Product Image" width="60" />
</td>
<td>{{pro.codigo}}</td>
<td>{{pro.producto}}</td>
// this line
<td>{{pro.id_categoria}}</td>
// edit to
<td>{{ pro.categoria }} </td>

Bootstrap Select and dynamic rows issue in vue.js

I have the following dynamic table/rows in vue.js and I use bootstrap-select to have a nicer dropdown select. The form has a add/remove line to be dynamic. I cannot load the options of a select using bootstrap select. The select appears on each row but no dropdown list appears.
What am I doing wrong?
here goes my jsfiddle
HTML:
<div id="app">
<table class="table">
<thead>
<tr>
<td><strong>Date</strong></td>
<td><strong>Account</strong></td>
<td><strong>Debit</strong></td>
<td><strong>Credit</strong></td>
<td></td>
</tr>
</thead>
<tbody>
<tr v-for="row in rows">
<td>
<input type="date" v-date="row.myDate">
</td>
<td>
<select class="selectpicker" ref="select" v-model="row.select">
<option value="Acc1">Account1</option>
<option value="Acc2">Account2</option>
<option value="Acc3">Account3</option>
<option value="Acc4" selected>Account4</option>
</select>
</td>
<td>
<input type="text" v-model="row.debit" v-on:keypress="isNumber(event)">
</td>
<td>
<input type="text" v-model="row.credit" v-on:keypress="isNumber(event)">
</td>
<td><a #click="removeRow(row)">Remove</a></td>
</tr>
</tbody>
<tfooter>
<td class="al-g"> <button class="button btn-primary" #click="addRow">Add Line</button></td>
<td></td>
<td class="al-r">tot D.: {{ totaldebit | roundme }}</td>
<td class="al-r">tot Cr.:{{ totalcredit | roundme}}</td>
<td class="al-r">Dif: {{ totaldebit-totalcredit | roundme}}</td>
</tfooter>
</table>
</div>
JS:
Vue.filter('roundme', function (value) {
return value.toFixed(3);
})
var app = new Vue({
el: "#app",
data: {
rows: [{debit:0, credit:0},
]
},
computed: {
totaldebit() {
return this.rows.reduce((total, row) => {
return total + Number(row.debit);
}, 0);
},
totalcredit() {
return this.rows.reduce((total, row) => {
return total + Number(row.credit);
}, 0);
}
},
methods: {
addRow: function() {
this.rows.push({myDate:"",
account:"",
debit: "",
credit: ""
});
},
removeRow: function(row) {
//console.log(row);
this.rows.$remove(row);
},
isNumber: function(evt) {
evt = (evt) ? evt : window.event;
var charCode = (evt.which) ? evt.which : evt.keyCode;
if ((charCode > 31 && (charCode < 48 || charCode > 57)) && charCode !== 46) {
evt.preventDefault();;
} else {
return true;
}
}
}
});
You are going to need a wrapper component like this.
The way bootstrap-select normally activates is to scan the HTML at startup and apply itself to .selectpicker elements. That won't work if the DOM is dynamic, as it is with Vue. You have to activate the elements using the $(element).selectpicker() method as they are created or updated.
See also Make VueJS and jQuery play nice.

Removing a row from a table with VueJS

In Vue how do you remove a row from a table when the item is deleted?
Below is how I am rendering the table
<tbody>
<tr v-for="item in items">
<td v-text="item.name"></td>
<td v-text="item.phone_number"></td>
<td v-text="item.email"></td>
<td><button #click="fireDelete(item.id)">Delete</button></td>
</tr>
</tbody>
Below is an excerpt from my Vue component.
data() {
return {
items: []
}
},
methods: {
fireDelete(id) {
axios.delete('/item/'+id).then();
}
},
mounted() {
axios.get('/item').then(response => this.items = response.data);
}
The axios.get work and so does the axios.delete, but the fronend doesn't react so the item is only removed from the table after a page refresh. How do I get it to remove the relevant <tr>?
I've managed to work out a nice way. I pass the index to the fireDelete method and use the splice function. Works exactly how I wanted.
<tbody>
<tr v-for="(item, index) in items" v-bind:index="index">
<td v-text="item.name"></td>
<td v-text="item.phone_number"></td>
<td v-text="item.email"></td>
<td><button #click="fireDelete(item.id, index)">Delete</button></td>
</tr>
</tbody>
fireDelete(id, index) {
axios.delete('/item/'+id).then(response => this.organisations.splice(index, 1));
}
I had the same trouble as this question. So maybe someone will find this usefull.
For the button use:
v-if="items.length > 1" v-on:click="fireDelete(index)"
And the fireDelete function:
fireDelete: function (index) {
this.photos.splice(index, 1);
}
You can try to modify your #click="fireDelete(item.id)" part to a custom method #click='deleteData(items, item.id)'
and do something like:
methods: {
deleteData (items, id) {
this.items = null // These parts may not
this.fireDelete(id) // match your exact code, but I hope
} // you got the idea.
}
and your template can do just:
<tbody>
<tr v-for="item in items" v-if='items'>
<td v-text="item.name"></td>
<td v-text="item.phone_number"></td>
<td v-text="item.email"></td>
<td><button #click="deleteData(item, item.id)">Delete</button></td>
</tr>
</tbody>

Can datatables sort a column with an input field?

I am trying to make datatables sort my columns. The first column works okay as it's a simple number. However the next column is an input field. When I try to make that sort then nothing happens.
<table width="100%" cellspacing="0" class="table sortable no-margin">
<thead>
<tr>
<th scope="col" class="sorting" style="width: 57px;">
<span class="column-sort">
</span>
ID
</th>
<th scope="col" class="sorting_desc" style="width: 94px;">
<span class="column-sort">
</span>
Order
</th>
</tr>
</thead>
<tbody>
<tr id="row_20" class="odd">
<td id="refKey_20" style="text-align:center;" class="">
1Y
</td>
<td class=" sorting_1">
<input type="text" value="160" size="3" name="item.Order"
maxlength="3" id="Order_20" >
</td>
</tr>
<tr id="row_19" class="even">
<td id="refKey_19" style="text-align:center;" class="">
1X
</td>
<td class=" sorting_1">
<input type="text" value="150" size="3" name="item.Order"
maxlength="3" id="Order_19" >
</td>
</tr>
</tbody>
</table>
Is there some way that I can get datatables to sort input fields?
The easiest way is to add a hidden span inside columns <span style="visibility:hidden">value of the input</span>
You should look at this example that explains how to do sorting on input fields. Basically you declare a sorting function
/* Create an array with the values of all the input boxes in a column */
$.fn.dataTableExt.afnSortData['dom-text'] = function ( oSettings, iColumn )
{
var aData = [];
$( 'td:eq('+iColumn+') input', oSettings.oApi._fnGetTrNodes(oSettings) ).each( function () {
aData.push( this.value );
} );
return aData;
}
And then tell to your table to use that
$('#example').dataTable( {
"aoColumns": [
null,
{ "sSortDataType": "dom-text" }
]
} );
or wit aoColumnDefs
$('#example').dataTable( {
"aoColumnDefs": [{ "sSortDataType": "dom-text" , aTarget: "yourclass"}]
} );
For versions of Datatables 1.10+ the names of some option variables have been changed and a new API introduced. Documentation here: http://datatables.net/examples/plug-ins/dom_sort.html.
Here is a working version of the above accepted answer in 1.10+:
$(document).ready(function () {
var table = $('#example').DataTable({
"columnDefs": [
{
"orderDataType": "dom-input",
"targets": 0, // Just the first column
},
],
});
});
The custom sort function:
$.fn.dataTable.ext.order['dom-input'] = function (settings, col) {
return this.api().column(col, { order: 'index' }).nodes().map(function (td, i) {
return $('input', td).val();
});
}
jQuery.fn.dataTableExt.oSort['textbox-asc'] = function (a, b) {
var vala = $('#' + $(a).filter('input').attr('id')).val().toLowerCase();
var valb = $('#' + $(b).filter('input').attr('id')).val().toLowerCase();
if (vala === '')
return 1;
if (valb === '')
return -1;
return vala < valb ? -1 : vala > valb ? 1 : 0;
};
jQuery.fn.dataTableExt.oSort['textbox-desc'] = function (a, b) {
var vala = $('#' + $(a).filter('input').attr('id')).val().toLowerCase();
var valb = $('#' + $(b).filter('input').attr('id')).val().toLowerCase();
if (vala === '')
return 1;
if (valb === '')
return -1;
return vala < valb ? 1 : vala > valb ? -1 : 0;
};
then use it like this
$(datatable).dataTable({
"iDisplayLength": 50,
"bLengthChange": false,
"bPaginate": false,
"columns": [
null, { "sType": "textbox" }
],
});
If you decide to use the columns option where you are rending information from a JSON file you can easily add a hidden span on your render property. It appears as though DataTables looks for text to order and if it cannot find any, it will break. The example at https://datatables.net/examples/plug-ins/dom_sort.html uses a table that has already been rendered. Here is an example using an API:
...columns([{
"data": "receivedDate",
"render": function (data, type, row, meta)
{
if (data == "null")
{
return "<input type='text' id='datepicker_" + meta.row + "' class='datepicker form-control' /><span class='hidden'><span>";
}
else
{
return "<input type='text' id='datepicker_" + meta.row + "' class='datepicker form-control' value='" + moment(data).format("MM/DD/YYYY") + "'/><span class='hidden'>" + moment(data).format('MM/ DD / YYYY') + "</span>";
}
}
}]);
Set an invisible div with the value before the input field.
<tbody>
<tr id="row_20" class="odd">
<td id="refKey_20" style="text-align:center;" class="">
1Y
</td>
<td class=" sorting_1">
<div style="display:none;">160</div>
<input type="text" value="160" size="3" name="item.Order"
maxlength="3" id="Order_20" >
</td>
</tr>
<tr id="row_19" class="even">
<td id="refKey_19" style="text-align:center;" class="">
1X
</td>
<td class=" sorting_1">
<div style="display:none;">150</div>
<input type="text" value="150" size="3" name="item.Order"
maxlength="3" id="Order_19" >
</td>
</tr>
</tbody>