How to #click change the data in the array with Vuejs - vue.js

I have a table with two-button Inch and CM, and I want to change the numbers from Inch to CM on click, Please can someone teach me how to do it 🙏🙏🙏
data() {
return {
guild_size_inch: [
{
size: "S",
chest: "92",
length: "71"
},
{
size: "M",
chest: "40",
length: "29"
},
{
size: "L",
chest: "44",
length: "30"
}
],
}
},
methods: {
changeToCM: function() {
this.guild_size_inch * 2.54;
alert(this.guild_size_inch)
}
}

I created a below code snippet as per your requirement. Please find the explanation of the logic inline in the snippet.
const app = new Vue({
el: '#app',
data() {
return {
guild_size_inch: [
{
size: "S",
chest: "92",
length: "71"
},
{
size: "M",
chest: "40",
length: "29"
},
{
size: "L",
chest: "44",
length: "30"
}
],
isInch: true, // property to enable/disable the 'Inch' button.
isCM: false // property to enable/disable the 'CM' button.
}
},
methods: {
// Method used to convert the Inch values into CM.
changeToCM() {
this.isCM = true;
this.isInch = false;
this.guild_size_inch = this.guild_size_inch.map((obj) => {
obj.chest = parseInt(obj.chest) * 2.54;
obj.length = parseInt(obj.length) * 2.54;
return obj;
});
},
// Method used to convert the CM values into Inch.
changeToInch() {
this.isCM = false;
this.isInch = true;
this.guild_size_inch = this.guild_size_inch.map((obj) => {
obj.chest = Math.ceil(parseInt(obj.chest) / 2.54);
obj.length = Math.ceil(parseInt(obj.length) / 2.54);
return obj;
});
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<button #click="changeToInch" :disabled="isInch">Inch (Default)</button>
<button #click="changeToCM" :disabled="isCM">CM</button>
<table>
<th>Size</th>
<th>Chest</th>
<th>Length</th>
<tr v-for="(item, index) in guild_size_inch" :key="index">
<td>{{ item.size }}</td>
<td>{{ item.chest }}</td>
<td>{{ item.length }}</td>
</tr>
</table>
</div>

You are trying to multiply the object instead of the numbers contains in your object.
What you should try is loop through your array and multiply both chest and length.
You can perform this with a forEach loop : see documentation here
changeToCM: function() {
this.guild_size_inch.forEach(item => {
item.chest = parseInt(item.chest) * 2.54
item.length = parseInt(item.length) * 2.54
});
alert(this.guild_size_inch)
}

Define a property called currentUnit initially set to inch which will be changes by the click event, then use a computed property that returns the sizes based on the current unit which you selected :
data() {
return {
guild_size_inch:[....],
currentUnit:'cm'
}
},
computed:{
sizes(){
return this.guild_size_inch.map(size=>{
size.chest=this.currentUnit==='cm'?size.chest*2.54:size.chest*2.54
size.length=this.currentUnit==='cm'?size.length/2.54:size.length/2.54
return size;
})
}
},
methods:{
changeUnit() {
this.currentUnit==='cm'?this.currentUnit='inch':this.currentUnit='cm'
}
}
then render the sizes in the table.

Related

Vuejs : suggestions component in table row

I am trying to use autocomplete component "v-suggestions", from https://www.npmjs.com/package/v-suggestions
Want to use in tab row in a loop, since i want to create multiple select boxes, please see the code
<tr
v-for="ingredient in ingredients"
v-bind:key="ingredient.id"
>
<!-- https://www.npmjs.com/package/v-suggestions -->
<td>
<suggestions
v-model="query.suggestions"
:options="options"
:onInputChange="onSelectIngredient">
</suggestions>
</td>
<td>
<input
type="text"
class="form-control"
id=""
v-model="items.quantity"
/>
</td>
<td>
export default {
data() {
let ingredients_auto = ['Onion', 'Salt', 'Oil', 'Sugar']
return {
items: [
{ id: 0, item_name: "x", quantity: "" },
{ id: 1, item_name: "y", quantity: "" },
{ id: 2, item_name: "y", quantity: "" }
],
query: '',
ingredients_auto: ingredients_auto,
ingredients_selected: null,
options: {}
};
methods: {
onSelectIngredient (query) {
if (query.trim().length === 0) {
return null
}
// return the matching countries as an array
return this.ingredients_auto.filter((item) => {
return item.toLowerCase().includes(query.toLowerCase())
})
}
}
I am getting below error in console , any idea why I am see this issue , I think there are issue with v-model, not sure how to fix this
vue.runtime.esm.js?2b0e:1888 TypeError: Cannot use 'in' operator to search for 'suggestions' in Salt
at Proxy.set (vue.runtime.esm.js?2b0e:1076)
at callback (eval at ./node_modules/cache-loader/dist/cjs.js?{"cacheDirectory":"node_modules/.cache/vue-loader","cacheIdentifier":"d52508de-vue-loader-template"}!./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/views/AddDish.vue?vue&type=template&id=646fb311&scoped=true& (app.b02fcecff20360dbbc44.hot-update.js:11), <anonymous>:189:45)
at invokeWithErrorHandling (vue.runtime.esm.js?2b0e:1854)
at VueComponent.invoker (vue.runtime.esm.js?2b0e:2179)
at invokeWithErrorHandling (vue.runtime.esm.js?2b0e:1854)
at VueComponent.Vue.$emit (vue.runtime.esm.js?2b0e:3888)
at VueComponent.query (v-suggestions.js?c4f6:1)
at Watcher.run (vue.runtime.esm.js?2b0e:4568)
at flushSchedulerQueue (vue.runtime.esm.js?2b0e:4310)
at Array.eval (vue.runtime.esm.js?2b0e:1980)
You had couple of mistakes I have rectified go through this
<template>
<table>
<tr
v-for="ingredient in ingredients"
v-bind:key="ingredient.id"
>
<td>
<suggestions
v-model="queries[ingredient.id]"
:options="options"
:onInputChange="onSelectIngredient">
</suggestions>
<!-- note the queries array, each item in it binds to a unique item in ingredients array -->
</td>
<td>
<input
type="text"
class="form-control"
id=""
v-model="ingredient.quantity"
/>
</td>
</tr>
</table>
</template>
<script>
import suggestions from 'v-suggestions'
export default {
name: 'demo',
components: {
suggestions
},
// using component locally, but if want to use globally use Vue.use(suggestions)
data() {
let ingredients_auto = ['Onion', 'Salt', 'Oil', 'Sugar']
let q = [];
for(var i=0; i<ingredients_auto.length; i++) {
q.push('');
}
//since we want queries array to be same length as of ingredients/ingredients_auto
return {
dish_name: "",
ingredients: [
{ id: 0, item_name: "Salt", quantity: "" },
{ id: 1, item_name: "Pepper", quantity: "" },
{ id: 2, item_name: "Oil", quantity: "" }
],
error: "",
selected:[],
units: ['gms', 'pound', 'kgs', 'ml', 'nos', 'liter'],
query: '',
ingredients_auto: ingredients_auto,
ingredients_selected: null,
options: {},
queries: q
};
},
methods: {
onSelectIngredient (query) {
if (query.trim().length === 0) {
return null
}
// return the matching countries as an array
return this.ingredients_auto.filter((item) => {
return item.toLowerCase().includes(query.toLowerCase())
})
}
}
}
</script>
Note that I have made a dynamic array q which I copy to queries because you want each individual textbox to behave independently so typing in one textbox should not affect the text contained in other textboxes
#viney see the
I didn't added that portion, let me add
export default {
data() {
let ingredients_auto = ['Onion', 'Salt', 'Oil', 'Sugar']
return {
dish_name: "",
ingredients: [
{ id: 0, item_name: "Salt", quantity: "" },
{ id: 1, item_name: "Pepper", quantity: "" },
{ id: 2, item_name: "Oil", quantity: "" }
],
error: "",
selected:[],
units: ['gms', 'pound', 'kgs', 'ml', 'nos', 'liter'],
query: '',
ingredients_auto: ingredients_auto,
ingredients_selected: null,
options: {}
};

Can't read property 'replace' of null in angular data table

I wrote an angular datatable which fetches and populates the table with data from server. Code is given below:
HTML:
<table
class="table display table-striped table-hover table-bordered row-border hover responsive nowrap"
datatable
[dtOptions]="dtOptions"
datatable=""
#dataTable
id="issueTable"
>
<thead class="headColor">
<!-- <tr>
<th>Action</th>
</tr> -->
</thead>
<tbody>
<!-- <tr>
<td><button class="btn btn-primary btnColor" (click)="buttonInRowClick($event)">View</button></td>
</tr> -->
</tbody>
</table>
angular code:
import { Component, OnInit, Renderer, AfterViewInit, ViewChild } from '#angular/core';
import { Router, RouterLink } from '#angular/router';
import { routerTransition } from '../../router.animations';
import { DataTableDirective } from 'angular-datatables';
import 'datatables.net';
import 'datatables.net-bs4';
window['jQuery'] = window['$'] = $;
#Component({
selector: 'app-charts',
templateUrl: './issues.component.html',
styleUrls: ['./issues.component.scss'],
animations: [routerTransition()]
})
export class IssuesComponent implements OnInit {
#ViewChild(DataTableDirective)
datatableElement: DataTableDirective;
message = '';
title = 'angulardatatables';
#ViewChild('dataTable') table;
dataTable: any;
/**
* gets settings of data tables
*
* #type {DataTables.Settings}
* #memberof IssuesComponent
*/
constructor(private router: Router) { }
dtOptions: DataTables.Settings = {};
// someClickhandler(info: any): void {
// this.message = info.number + '-' + info.assignedto + '-' + info.company;
// console.log(this.message);
// }
ngOnInit(): void {
this.dtOptions = {
pagingType: 'full_numbers',
pageLength: 15,
processing: true,
responsive: true,
autoWidth: false,
dom: 'Bfrtip',
'ajax': {
url: 'http://localhost:8080/incident-updated',
type: 'GET',
dataSrc: 'result',
},
columns: [
{
title: 'Incident',
data: 'number'
},
{
title: 'Product',
data: 'u_product'
},
{
title: 'Status',
data: 'state'
},
{
title: 'Created By',
data: 'sys_created_by'
},
{
title: 'Group',
data: 'assignment_group'
},
{
title: 'Category',
data: 'u_category'
},
{
title: 'Updated on',
data: 'sys_updated_on'
},
{
title: null,
data: null,
render: function(data, type, full) {
return '<a class="btn btn-info btn-sm" href=#/>' + 'View' + '</a>';
}
}
]
// ,
// columnDefs: [
// {
// targets: -1,
// data: null,
// defaultContent: '<button>Click!</button>'
// }
// ]
};
this.dataTable = $(this.table.nativeElement);
console.log('table is ==>', this.dataTable);
$('#issueTable tbody').on('click', 'button', function () {
const data = this.dataTable.row($(this).parents('tr').data);
console.log('Data is ==>', data);
});
}
buttonInRowClick(event: any): void {
event.stopPropagation();
console.log('Button in the row clicked.');
this.router.navigate(['/event-viewer']);
}
wholeRowClick(): void {
console.log('Whole row clicked.');
}
// ngAfterViewInit() {
// this.renderer.listenGlobal('document', 'click', (event) => {
// if (event.target.hasAttribute("view-person-id")) {
// this.router.navigate(["/person/" + event.target.getAttribute("view-person-id")]);
// }
// });
// }
}
When I run that code, I get this error:
ERROR TypeError: Cannot read property 'replace' of null
at _fnSortAria (VM1053 scripts.js:16659)
at jQuery.fn.init.<anonymous> (VM1053 scripts.js:11820)
at VM1053 scripts.js:17237
at Function.map (VM1053 scripts.js:456)
at _fnCallbackFire (VM1053 scripts.js:17236)
at _fnDraw (VM1053 scripts.js:14107)
at _fnReDraw (VM1053 scripts.js:14150)
at _fnInitialise (VM1053 scripts.js:15333)
at loadedInit (VM1053 scripts.js:11893)
at HTMLTableElement.<anonymous> (VM1053 scripts.js:11905)
How do I fix this error?
I'm not sure about this but I think here is the problem:
{
title: null,
data: null,
render: function(data, type, full) {
return '<a class="btn btn-info btn-sm" href=#/>' + 'View' + '</a>';
}
}
Even if you don't use any field inside return, you should put some column name, for example data: 'u_product':
{
title: null,
data: 'u_product',
render: function(data, type, full) {
return '<a class="btn btn-info btn-sm" href=#/>' + 'View' + '</a>';
}
}
I recommend you to use the primary key column of the table, maybe in the future it will be useful in the button.

Change table values on change with Vue

I want to change table values when a user selects a value from the dropdown menu. By default, the values are in Metric. If a user selects Standard then I want to run some math on each value and to convert metric to standard. Also being able to switch back to Metric.
https://jsfiddle.net/tmun9cxa/13/
html
<div class="form-filter">
<select name="type" v-model="filter_unit" v-on:change="onSelectUnitChange">
<option value="metric">Metric</option>
<option value="standard">Standard</option>
</select>
</div><!-- /filter -->
<table style="text-align: center;">
<thead>
<tr>
<th v-for="column in columns">
<a
href="#"
v-on:click="sort(column.shortcode)">{{column.label}}
</a>
</th>
</tr>
</thead>
<tbody>
<tr v-for="(product) in showProducts">
<td>{{product.pn}}</td>
<td>{{product.od}}</td>
<td>{{product.id}}</td>
<td>{{product.thickness}}</td>
<td>{{product.lo}}</td>
<td>{{product.weight}}</td>
</tr>
</tbody>
</table>
</div><!-- /app -->
Javascript
var vm = new Vue({
el: '#app',
data: {
currentSort: 'pn',
currentSortDir: 'asc',
category: 'all',
filter_unit: 'metric',
search: '',
columns: [
{ label: 'P/N', shortcode: 'pn' },
{ label: 'OD (De,mm)', shortcode: 'od' },
{ label: 'ID (De,mm)', shortcode: 'id' },
{ label: 'Thickness (De,mm)', shortcode: 'thickness' },
{ label: 'LO', shortcode: 'lo' },
{ label: 'Weight (kg/1000)', shortcode: 'weight' },
], // columns
products: [
{
pn: 170158,
od: 13,
id: .44,
thickness: 1,
lo: .45,
weight: .7,
category: 'chrome',
},{
pn: 1803561,
od: 12,
id: .8,
thickness: .7,
lo: .11,
weight: .5,
category: 'chrome',
},{
pn: 170149,
od: 9,
id: .64,
thickness: .6,
lo: .75,
weight: .3,
category: 'stainless',
},{
pn: 150149,
od: 15,
id: .22,
thickness: .3,
lo: .55,
weight: .9,
category: 'chrome',
},
], // products
},
computed: {
showProducts(){
return this.products
.filter(a => {
return (a.pn + '').includes(this.search)
})
.sort((a, b) => {
if (this.currentSortDir === 'asc') {
//console.log( this.currentSort );
return a[this.currentSort] >= b[this.currentSort];
}
return a[this.currentSort] <= b[this.currentSort];
})
}
},
methods: {
sort:function(col) {
// if you click the same label twice
if(this.currentSort == col){
// sort by asc
this.currentSortDir = this.currentSortDir === 'asc' ? 'desc' : 'asc';
}else{
this.currentSort = col;
}
}, // sort
onSelectUnitChange:function(){
if(this.filter_unit == 'standard'){
// change values of OD using this equation. current OD value * 0.0393701
// change table header OD(De,mm) to OD(De,in)
// also will be doing a similar process to ID and Thickness
console.log('standard change');
}
},
}, // methods
}); // vue
You may add the logic on your computed property and check the v-model of the dropdown. I've updated your sample see https://jsfiddle.net/tmun9cxa/74
With my example I didn't change your existing computed but you can simply add your logic to that
filteredProducts() {
let filteredProducts = []
let _product
this.showProducts.forEach(product => {
_product = product
// do the logic here
if (this.filter_unit === 'metric') {
_product.displayWeight = _product.weight * 25
} else if (this.filter_unit === 'standard') {
_product.displayWeight = _product.weight + 10
}
filteredProducts.push(_product)
})
return filteredProducts
}
Update:
Another option is to use Vue filters. I've updated your example using filters http://jsfiddle.net/eywraw8t/274069
filters: {
convertOd(val, type) {
if (type === 'metric') {
return val * 0.0393701
} else if (type === 'imperial') {
return val
}
}
}
or
Vue.filter('convertOd', function (val, type) {
if (type === 'metric') {
return val * 0.0393701
} else if (type === 'imperial') {
return val
}
})
and to use it in html
<td>{{ product.od | convertOd(filter_unit) }}</td>
You could use computed properties but your code could work as it is.
I just applied conversions on values in the onSelectUnitChange function and it worked.
onSelectUnitChange:function(){
if(this.filter_unit == 'standard'){
this.products.forEach(p => {
p.od *= 0.0393701
p.id *= 0.0393701
p.thickness *= 0.0393701
})
this.columns[1].label = "OD(De,in)"
this.columns[2].label = "ID(De,in)"
this.columns[3].label = "Thickness(De,in)"
} else {
this.products.forEach(p => {
p.od /= 0.0393701
p.id /= 0.0393701
p.thickness /= 0.0393701
})
this.columns[1].label = "OD(De,mm)"
this.columns[2].label = "ID(De,mm)"
this.columns[3].label = "Thickness(De,mm)"
}
}

vue js 2 sorting a table

I have 2 questions with vue.js 2 and a fiddle here: https://jsfiddle.net/tmun9cxa/1/
When you click a column header, why does my sorting not work? What is the solution?
How do I go about making the search input field search only the pn column?
A lot of the examples ive found are using vue1 and out of date.
<input type="text" value="" v-model="search" placeholder="Search">
<table style="text-align: center;">
<thead>
<tr>
<th v-for="column in columns">
<a
href="#"
v-on:click="sort(column.shortcode)">{{column.label}}
</a>
</th>
</tr>
</thead>
<tbody>
<tr v-for="(product) in products">
<td>{{product.pn}}</td>
<td>{{product.od}}</td>
<td>{{product.id}}</td>
<td>{{product.thickness}}</td>
<td>{{product.lo}}</td>
<td>{{product.weight}}</td>
</tr>
</tbody>
</table>
javascript here
var vm = new Vue({
el: '#app',
data: {
currentSort: 'pn',
currentSortDir: 'asc',
search: '',
columns: [
{ label: 'P/N', shortcode: 'pn' },
{ label: 'OD (De,mm)', shortcode: 'od' },
{ label: 'ID (De,mm)', shortcode: 'id' },
{ label: 'Thickness (De,mm)', shortcode: 'thickness' },
{ label: 'LO', shortcode: 'lo' },
{ label: 'Weight (kg/1000)', shortcode: 'weight' },
], // columns
products: [
{
pn: 170158,
od: 13,
id: .44,
thickness: 1,
lo: .45,
weight: .7
},{
pn: 1803561,
od: 12,
id: .8,
thickness: .7,
lo: .11,
weight: .5
},{
pn: 170149,
od: 9,
id: .64,
thickness: .6,
lo: .75,
weight: .3
},{
pn: 150149,
od: 15,
id: .22,
thickness: .3,
lo: .55,
weight: .9
},
], // products
},
methods: {
sort:function(col) {
//console.log( 'current: '+this.currentSort );
//console.log( 'col: '+col );
//var colthing = col;
// if you click the same label twice
if(this.currentSort == col){
console.log( 'same col: '+col );
// sort by asc
this.products = this.products.sort((a, b) => {
return a.col >= b.col;
});
}else{
this.currentSort = col;
console.log( 'diff col: '+col );
// sort by desc
this.products = this.products.sort((a, b) => {
return a.col <= b.col;
});
} // end if
}, // sort
}, // methods
}); // vue
the column sorting , as pointed out, was not working because you need to use a[col] instead of a.col
Also, you should consider using a computed value instead of modifying the original data. This makes filtering easier too.
here is the updated script (note that <tr v-for="(product) in products"> needs to be <tr v-for="(product) in showProducts"> for this to work)
var vm = new Vue({
el: '#app',
data: {
currentSort: 'pn',
currentSortDir: 'asc',
search: '',
columns: [
{ label: 'P/N', shortcode: 'pn' },
/// more columns ...
], // columns
products: [
//.... objects
], // products
},
computed: {
showProducts() {
return this.products.filter(a => {
console.log(a.pn)
return (a.pn + '').includes(this.search)
})
.sort((a, b) => {
if (this.currentSortDir === 'asc') {
return a[this.currentSort] >= b[this.currentSort];
}
return a[this.currentSort] <= b[this.currentSort];
})
},
},
methods: {
sort:function(col) {
// if you click the same label twice
if(this.currentSort == col){
this.currentSortDir = this.currentSortDir === 'asc' ? 'desc' : 'asc';
}else{
this.currentSort = col;
console.log( 'diff col: '+col );
} // end if
}, // sort
}, // methods
}); // vue
finally, the fiddle: https://jsfiddle.net/tmun9cxa/2/

Vue.js How to calculate totals?

How can I calculate the total amount from an array?
I pass data to the child component as prop, and I am stuck here. When I console log prop, it returns a very complicated object . I tried this.values.reduce() function but it does not work.
<template>
<tr v-for="value in values" >
<th scope="row">{{$index+1}}</th>
<td>{{value.name}}</td>
<td>-</td>
<td>${{value.total}}</td>
</tr>
<tr>
<th></th>
<td><strong>Total:{{total}}</strong></td>
<td>-</td>
<td>-</td>
</tr>
</template>
<script>
export default {
props: ['values'],
ready: function() {
}
}
</script>
In case anyone else is in the same situation as me I thought I'd add this answer. I needed to get the values from nested objects then push them to an array before reducing them:
total: function(){
let total = [];
Object.entries(this.orders).forEach(([key, val]) => {
total.push(val.price) // the value of the current key.
});
return total.reduce(function(total, num){ return total + num }, 0);
}
This uses ES7 .entries to loop through the object which looked like this:
orders = {
1: {title: 'Google Pixel', price: 3000},
2: {title: 'Samsung Galaxy S8', price: 2500},
3: {title: 'iPhone 7', price: 5000}
}
You can then display the total in your template with:
<span> {{total}} </span>
var payments = new Vue({
el: "#payments",
data: {
payments: [
{ name: "houseRent", amount: 1000, is_paid: true },
{ name: "houseRent", amount: 1500, is_paid: true },
{ name: "houseRent", amount: 1200, is_paid: false },
{ name: "houseRent", amount: 1070, is_paid: true },
{ name: "houseRent", amount: 1040, is_paid: false }
]
},
computed: {
totalAmount: function () {
var sum = 0;
this.payments.forEach(e => {
sum += e.amount;
});
return sum
}
}
});`
As you proposed, you could use the Array#reduce function. Starting from this example on SO, you could adapt it to your needs and only add value.total to the sumtotal.
To compute the total of all values, you can use computed properties, which will display as {{ total }} in your template:
<script>
export default {
props: {
values: {
type: Array,
default: []
},
}
ready: function() {
},
computed: {
total: function() {
if (!this.values) {
return 0;
}
return this.values.reduce(function (total, value) {
return total + Number(value.total);
}, 0);
}
}
}
</script>
Note: This will of course only work, if value.total is a unitless number (e.g. 1, not '1 USD'). Otherwise you would need to strip the in the reduce function as well.