How to merge properties JavaScript objects - javascript

I'm starting with Vue and I'm having a little difficulty.
In the image below I have a table with some items:
Every time an item is chosen and the amount increased I need that in my addOptional method (optional) my variable gets the amount of that item concatenated with the value. Example if I choose hammer would look like this `
let variavel = opcional.Qtd + 'x' + opcional.Code
If I give console.log the result would be 2x1
But if I choose another option, example Serrote I should join the first choice in that same variable and separate with Pipe ( | ) Example would look like this.
2x1 | 1x2
How should I do this? Should I use array?
What I already have:
new Vue({
el: '#app',
data() {
return {
Opcionais: [
{ Code: 1, Nome: 'Martelo', Valor: 50.00, Qtd: 0 },
{ Code: 2, Nome: 'Serrote', Valor: 50.00, Qtd: 0 },
{ Code: 3, Nome: 'Prego', Valor: 60.00, Qtd: 0 }
]
}
},
methods: {
addOpcional(opcional) {
// The variable below should receive the value of the quantity plus the code. If more than one option is chosen the variable must receive this new value and separate with pipe example Qty (2) x Code (2) | Qty (3) x Code (2)
opcional.Qtd += 1
let Code = [opcional.Qtd + 'x' + opcional.Code]
},
remove(opcional) {
}
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<template>
<div class="usuario-lista">
<table>
<thead>
<tr>
<th>#Code</th>
<th>Nome</th>
<th>Valor Unitário</th>
<th>Valor Total</th>
</tr>
</thead>
<tbody>
<tr v-for="opcional in Opcionais" :key="opcional.Code">
<td>
<button #click="opcional.Qtd ? opcional.Qtd-- : false">-</button>
<input type="text" :value="opcional.Qtd">
<button #click="addOpcional(opcional)">+</button>
</td>
<td>{{ opcional.Nome }}</td>
<td>{{ opcional.Valor }}</td>
<td>{{ opcional.Valor * opcional.Qtd }}</td>
</tr>
</tbody>
</table>
</div>
</template>
</div>

This seems like a perfect use case for a computed property:
computed: {
Code: function () {
return this.Opcionais
.filter( opcional => opcional.Qtd > 0 )
.map( opcional => opcional.Qtd + 'x' + opcional.Code )
.join( ' | ' );
}
}
Here's a full working example, showing the code below the table and updating it live:
new Vue({
el: '#app',
data() {
return {
Opcionais: [
{ Code: 1, Nome: 'Martelo', Valor: 50.00, Qtd: 0 },
{ Code: 2, Nome: 'Serrote', Valor: 50.00, Qtd: 0 },
{ Code: 3, Nome: 'Prego', Valor: 60.00, Qtd: 0 }
]
}
},
computed: {
Code: function () {
return this.Opcionais
.filter( opcional => opcional.Qtd > 0 )
.map( opcional => opcional.Qtd + 'x' + opcional.Code )
.join( ' | ' );
}
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<template>
<div class="usuario-lista">
<table>
<thead>
<tr>
<th>#Code</th>
<th>Nome</th>
<th>Valor Unitário</th>
<th>Valor Total</th>
</tr>
</thead>
<tbody>
<tr v-for="opcional in Opcionais" :key="opcional.Code">
<td>
<button #click="opcional.Qtd > 0 && opcional.Qtd--">-</button>
<input type="text" v-model.number="opcional.Qtd">
<button #click="opcional.Qtd++">+</button>
</td>
<td>{{ opcional.Nome }}</td>
<td>{{ opcional.Valor }}</td>
<td>{{ opcional.Valor * opcional.Qtd }}</td>
</tr>
</tbody>
</table>
</div>
<p>Code: {{Code}}</p>
</template>
</div>

Not really familiar with Vue but you can reduce Opcionais like so:
const Opcionais = [
{ Code: 1, Nome: 'Martelo', Valor: 50.00, Qtd: 0 },
{ Code: 2, Nome: 'Serrote', Valor: 50.00, Qtd: 0 },
{ Code: 3, Nome: 'Prego', Valor: 60.00, Qtd: 0 }
];
const result = Opcionais.reduce((arr, { Qtd, Code }) => {
return [...arr, `${Qtd}x${Code}`];
}, []).join(' | ');
console.log(result);

You can use the spread operator to preserve the current state and add new items.
To join with pipes, use the 'reducer' as answered below or just do it in the html, as you wish.
new Vue({
el: '#app',
data() {
return {
Opcionais: [
{ Code: 1, Nome: 'Martelo', Valor: 50.00, Qtd: 0 },
{ Code: 2, Nome: 'Serrote', Valor: 50.00, Qtd: 0 },
{ Code: 3, Nome: 'Prego', Valor: 60.00, Qtd: 0 }
],
Elements: []
}
},
methods: {
addOpcional(opcional) {
// The variable below should receive the value of the quantity plus the code. If more than one option is chosen the variable must receive this new value and separate with pipe example Qty (2) x Code (2) | Qty (3) x Code (2)
opcional.Qtd += 1
this.Elements = [...this.Elements, (opcional.Qtd + 1) + 'x' + opcional.Code]
},
remove(opcional) {
}
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<template>
<div class="usuario-lista">
<table>
<thead>
<tr>
<th>#Code</th>
<th>Nome</th>
<th>Valor Unitário</th>
<th>Valor Total</th>
</tr>
</thead>
<tbody>
<tr v-for="opcional in Opcionais" :key="opcional.Code">
<td>
<button #click="opcional.Qtd ? opcional.Qtd-- : false">-</button>
<input type="text" :value="opcional.Qtd">
<button #click="addOpcional(opcional)">+</button>
</td>
<td>{{ opcional.Nome }}</td>
<td>{{ opcional.Valor }}</td>
<td>{{ opcional.Valor * opcional.Qtd }}</td>
</tr>
</tbody>
</table>
<pre>{{ Elements.join("|") }}</pre>
</div>
</template>
</div>

Related

how can i achieve pagination in vue js

please i'm trying to paginate data but i'm getting this error in my console
Property or method "$index" is not defined on the instance but referenced during render.
this is my html template which displays the data in which i'll want to paginate
<div id="heroes">
<table class="table table-striped">
<thead>
<tr>
<td>Hero Name</td>
<td>Universe</td>
</tr>
</thead>
<tbody>
<tr
v-for="hero in heroes "
v-show="setPaginate($index)"
>
<td>{{ hero.name }}</td>
<td>{{ hero.universe }}</td>
</tr>
</tbody>
</table>
<div id="pagination">
<a
href="#"
class="btn btn-default"
v-for="page_index in paginate_total"
#click.prevent="updateCurrent(page_index + 1)"
>
{{ page_index + 1 }}
</a>
</div>
</div>
this is my script tag
<script>
export default {
data() {
return {
current: 1,
heroes: [
{ name: "Wolverine", universe: "Marvel" },
{ name: "Batman", universe: "DC" },
{ name: "Beast", universe: "Marvel" },
{ name: "Superman", universe: "DC" },
{ name: "Wonder Woman", universe: "DC" },
{ name: "Iceman", universe: "Marvel" },
{ name: "Black Panther", universe: "Marvel" },
{ name: "Beast Boy", universe: "DC" },
{ name: "Captain America", universe: "Marvel" },
{ name: "Hawkgirl", universe: "DC" },
{ name: "Cyclops", universe: "Marvel" },
{ name: "Green Lantern", universe: "DC" },
],
paginate: 5,
paginate_total: 0
};
},
created() {
this.paginate_total = this.heroes.length / this.paginate;
},
methods: {
setPaginate: function(i) {
console.log(i);
if (this.current == 1) {
return i < this.paginate;
} else {
return (
i >= this.paginate * (this.current - 1) &&
i < this.current * this.paginate
);
}
},
setStatus: function(status) {
this.status_filter = status;
this.$nextTick(function() {
this.updatePaginate();
});
},
updateCurrent: function(i) {
this.current = i;
},
updatePaginate: function() {
this.current = 1;
this.paginate_total = Math.ceil(
document.querySelectorAll("tbody tr").length / this.paginate
);
}
}
};
</script>
i got the example from codepen which works fine when using it on codepen but i'm getting an error in my code ,here's the link
https://codepen.io/lorena-tapia/details/VwZzpRZ
please how can i go about this
The $index is not defined anywhere, you could define it in the v-for loop like :
<tr v-for="(hero,index) in heroes " v-show="setPaginate(index)">
<td>{{ hero.name }}</td>
<td>{{ hero.universe }}</td>
</tr>

How to access multiple data elements of vue js in one tag of html file using v-for?

This is a part of my Vue project where I want to get some data elements in a table. I want to take data2 element like countryData in another column of the table. How can I do this? You can run the snippet for further reference.
In HTML code I have included the vue js library and simply made a table.
var app = new Vue({
el: "#app",
data(){
return {
countryData:[{name:"india", "1999":9537, "2000":10874},
{name:"china", "1999":7537, "2000":8874},
{name:"england", "1999":11537, "2000":12074}
],
data2: [
{ser1: 1, ser2: 7},
{ser1: 4, ser2: 1},
{ser1: 6, ser2: 8}
]}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div>
<h3>Data Table</h3>
</div>
<table>
<thead>
<tr>
<th>Country</th>
<th>Sales</th>
</tr>
</thead>
<tbody>
<tr v-for="x in countryData">
<td>{{x.name}}</td>
<! -- I want to take data2 element in another column of my table -->
</tr>
</tbody>
</table>
</div>
You can either combine the data as you wish, before displaying in template, for example:
countryData: [
{ name: "india", "1999": 9537, "2000": 10874 },
{ name: "china", "1999": 7537, "2000": 8874 },
{ name: "england", "1999": 11537, "2000": 12074 }
],
data2: [{ ser1: 1, ser2: 7 }, { ser1: 4, ser2: 1 }, { ser1: 6, ser2: 8 }],
combinedData: []
// ...
created() {
this.countryData.forEach((x, i) => {
this.combinedData.push({ ...x, ...this.data2[i] });
});
}
Then you can access in template with:
<tr v-for="(x, i) in combinedData" :key="i">
<td>{{x.name}}</td>
<td>{{x.ser1}}</td>
<td>{{x.ser2}}</td>
</tr>
or you can utilize the index in the template:
<tr v-for="(x, i) in countryData" :key="i">
<td>{{x.name}}</td>
<td>{{data2[i].ser1}}</td>
<td>{{data2[i].ser2}}</td>
</tr>

Dynamically Filter an Array of Objects in Vue.js

I have a Vue.js app. In this app, I'm trying to dynamically apply filter value to an Array of objects. Each object in the Array has fields. I'm trying to filter these objects by field values. Each field can be filtered by multiple values.
At this time, I have been unsuccessful in figuring out how to do this filtering. I've tried using JavaScript's baked-in filter function. However, that always returned an empty result set for me. I've put together this Fiddle, which includes this code:
new Vue({
el: '#app',
data: {
currentFilterProperty: '',
currentFilterValue: '',
cols: [
{ title: 'Name', prop:'name' },
{ title: 'Age', prop:'age' },
{ title: 'Birthday', prop:'birthday' },
],
dataFilters: [],
data: [
{ name:'Patricia Miller', age:69, birthday:'04-15-1948' },
{ name:'Bill Baggett', age:62, birthday:'05-07-1955' },
{ name:'Maxine Thies', age:21, birthday:'11-28-1995' },
{ name:'Alison Battle', age:65, birthday:'08-07-1952' },
{ name:'Dick Triplett', age:25, birthday:'08-27-1982' }
]
},
methods: {
addFilter: function() {
var f = this.dataFilters[this.currentFilterProperty];
if (!f) {
this.dataFilters = {};
this.dataFilters[this.currentFilterProperty] = [ this.currentFilterValue ];
} else {
this.dataFilters[this.currentFilterProperty].push(this.currentFilterValue);
}
// How to apply filter?
}
}
})
I'm not sure how to apply the filters to the data object.
Complete solution. Best test: add filter Age 62, then Birthday 04-15-1948, then 'tri' in Name Patricia.
new Vue({
el: '#app',
data: {
filteredProperty: 'name',
query: '',
activeFilters: [],
data: [
{name: 'Patricia Miller', age: 62, birthday: '04-15-1948'},
{name: 'Bill Baggett', age:62, birthday: '04-15-1948' },
{name:'Maxine Thies', age:62, birthday:'11-28-1948'},
{name:'Alison Battle', age:65, birthday:'08-07-1952'},
{name:'Dick Triplett', age:25, birthday:'08-27-1982'}
]
},
computed: {
filtered () {
var filtered = this.data
this.activeFilters.forEach(filter => {
filtered = filtered.filter(record => {
return filter.name === 'name'
? new RegExp(filter.value, 'i').test(record[filter.name])
: record[filter.name] == filter.value
})
})
return filtered
}
},
methods: {
addFilter () {
this.activeFilters.push({
name: this.filteredProperty,
value: this.query
})
this.query = ''
},
removeFilter (idx) {
this.activeFilters.splice(idx, 1)
}
}
})
<div id="app">
<div>
<select v-model="filteredProperty">
<option value="name">Name</option>
<option value="age">Age</option>
<option value="birthday">Birthdate</option>
</select>
<input placeholder="filter value" v-model="query">
<button #click="addFilter">add filter</button>
</div>
<hr>
<table v-if="activeFilters.length">
<tr style="width: 100px">
<th colspan="3">Filters in use:</th>
</tr>
<tr v-for="(filter, index) in activeFilters" :key="index">
<td>{{ _.capitalize(filter.name) }}:</td>
<td>{{ filter.value }}</td>
<td style="padding-left: 10px;">
<a href="#" #click.prevented=removeFilter(index)>
remove
</a>
</td>
</tr>
</table>
<hr v-if="activeFilters.length">
<table>
<tbody>
<tr v-for="(record, index) in filtered" :key="index">
<td style="padding-right:18px;">{{ record.name }}</td>
<td style="padding-right:18px;">{{ record.age }}</td>
<td>{{ record.birthday }}</td>
</tr>
</tbody>
</table>
</div>
<script src="https://unpkg.com/vue"></script>
<script src="https://unpkg.com/lodash"></script>
Take a look the below code. Change your method to a computed property and then the filter can happen automatically without pressing a button. The fiddle always filters by Name so you'll need to make a few adjustments to work for all filter criteria, but it should get you moving in the correct direction.
new Vue({
el: '#app',
data: {
currentFilterProperty: '',
currentFilterValue: '',
cols: [
{ title: 'Name', prop:'name' },
{ title: 'Age', prop:'age' },
{ title: 'Birthday', prop:'birthday' }
],
data: [
{ name:'Patricia Miller', age:69, birthday:'04-15-1948' },
{ name:'Bill Baggett', age:62, birthday:'05-07-1955' },
{ name:'Maxine Thies', age:21, birthday:'11-28-1995' },
{ name:'Alison Battle', age:65, birthday:'08-07-1952' },
{ name:'Dick Triplett', age:25, birthday:'08-27-1982' }
]
},
computed:{
filteredData(){
var self = this;
// Add condition for currentFilterProperty == 'Name'
if(this.currentFilterValue != undefined && this.currentFilterValue != ''){
return this.data.filter(function(d){
//alert(d.name + " " + this.currentFilterValue);
return d.name.indexOf(self.currentFilterValue) != -1;
});
}
// else if(currentFilterProperty == 'Date'){
// return this.data.filter(function(d){
//return d.birthday.indexOf(self.currentFilterValue) != -1;
// });
else{
return this.data;
}
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.4/vue.min.js"></script>
<div id="app">
<div>
<select v-model="currentFilterProperty">
<option v-for="c in cols" :value="c.prop">{{c.title}}</option>
</select>
<input placeholder="filter value" v-model="currentFilterValue" />
</div>
<hr />
<table>
<tbody>
<tr v-for="(record, index) in filteredData">
<td style="padding-right:18px;">{{ record.name }}</td>
<td style="padding-right:18px;">{{ record.age }}</td>
<td>{{ record.birthday }}</td>
</tr>
</tbody>
</table>
</div>
Here is working solution By checking captaining condition
new Vue({
el: '#app',
data: {
currentFilterProperty: 'name',
currentFilterValue: '',
filteredData:[],
cols: [
{ title: 'Name', prop:'name' },
{ title: 'Age', prop:'age' },
{ title: 'Birthday', prop:'birthday' },
],
dataFilters: [],
addFilters:[],
data: [
{ name:'Patricia Miller', age:69, birthday:'04-15-1948' },
{ name:'Bill Baggett', age:62, birthday:'05-07-1955' },
{ name:'Maxine Thies', age:21, birthday:'11-28-1995' },
{ name:'Alison Battle', age:65, birthday:'08-07-1952' },
{ name:'Dick Triplett', age:25, birthday:'08-27-1982' }
]
},
methods: {
addFilter: function() {
if(!this.currentFilterValue){
return false;
}
var obj = {};
this.addFilters.push({name:this.currentFilterProperty,value:this.currentFilterValue});
this.currentFilterValue = "";
var vm = this;
this.dataFilters = this.data
//var temp = [];
for(var i in vm.addFilters){
this.dataFilters = this.dataFilters.filter(function(a,b){
return ((a[vm.addFilters[i].name]).toString().toLowerCase()).indexOf((vm.addFilters[i].value).toString().toLowerCase()) !== -1;
});
}
// How to apply filter?
}
},
mounted(){
this.dataFilters = this.data;
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<div>
<select v-model="currentFilterProperty">
<option value="name">Name</option>
<option value="age">Age</option>
<option value="birthday">Birthdate</option>
</select>
<input placeholder="filter value" v-model="currentFilterValue" />
<button v-on:click="addFilter">
add filter
</button>
</div>
<div v-for="(filter,index) in addFilters">{{filter.name}} : {{filter.value}}</div>
<hr />
<table>
<tbody>
<tr v-for="(record, index) in dataFilters">
<td style="padding-right:18px;">{{ record.name }}</td>
<td style="padding-right:18px;">{{ record.age }}</td>
<td>{{ record.birthday }}</td>
</tr>
</tbody>
</table>
</div>

Vue JS 2: Bind computed property to data attribute

I have a data object in Vue.js 2 that looks like this:
data: {
items: [
{
value1: 10,
value2: 10,
valuesum: ""
},
{
value1: 10,
value2: 100,
valuesum: "",
}
]
I render that data object in a table and run calculations on it. I want the valuesum property to be computed and stored in each object somehow. In other words, I want the code to essentially perform this:
data: {
items: [
{
value1: 10,
value2: 10,
valuesum: {{ value1 + value2 }} //20
},
{
value1: 10,
value2: 100,
valuesum: {{ value1 + value2 }} //110
}
]
The computed attribute doesn't seem to be able to accomplish this. I tried to use the following function but this does not work:
function (index) {
for (let i = 0; i < this.items.length; i++ ){
return this.items[index].value1 + this.items[index].value2;
}
}
The closest I have managed to get to an answer is by doing the calculation inline, but I can't bind its result to the items.total object. My HTML looks like this:
<table id="table">
<thead>
<tr>
<td>Value 1</td>
<td>Value 2</td>
<td>Sum</td>
</tr>
</thead>
<tbody>
<tr v-for="item in items">
<td><input type="number" v-model="item.value1"></td>
<td><input type="number" v-model="item.value2"></td>
<td> {{ item.value1 + item.value2 }} </td>
</tr>
</tbody>
</table>
But I can't add v-model to it, since it's not an input. I'd like to avoid adding a readonly <input> to the column, since that doesn't seem like the best solution and isn't very elegant.
Here are a few approaches: Binding to a method, binding to a computed property, and binding to a data property that is saved during the computed property call:
<table id="table">
<thead>
<tr>
<td>Value 1</td>
<td>Value 2</td>
<td>Sum</td>
<td>Sum</td>
<td>Sum</td>
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in items">
<td><input type="number" v-model="item.value1"></td>
<td><input type="number" v-model="item.value2"></td>
<td> {{ sum(item) }} </td><!-- method -->
<td> {{ sums[index] }}</td><!-- computed -->
<td> {{ item.valuesum }}</td><!-- data property via computed -->
</tr>
</tbody>
</table>
The script:
var vm = new Vue({
el: "#table",
data: function () {
return {
items: [
{
value1: 10,
value2: 10,
valuesum: 0
},{
value1: 10,
value2: 100,
valuesum: 0,
}
]
}
},
computed: {
sums: function () {
var val = [];
for (var index in this.items) {
var sum = this.sum(this.items[index]);
val.push(sum);
// Update the local data if you want
this.items[index].valuesum = sum;
}
return val;
}
},
methods: {
sum: function (item) {
// Alternate, if you take valuesum out:
// for (var prop in item) {
// val += parseInt(item[prop]);
// }
return parseInt(item.value1) + parseInt(item.value2);
}
}
});
Is that the sort of thing you're looking for?

filter just 2 out of 3 columns angularjs

<table>
<tr>
<th>Primary Key</th>
<th>descriptionShort</th>
<th>descriptionLong</th>
</tr>
<tr ng-repeat="terReason in data | filter :({descriptionLong:searchTerm}||{descriptionShort:searchTerm} )">
<td>terReason.primaryKey</td>
<td>terReason.descriptionShort</td>
<td>terReason.descriptionLong</td>
</tr>
</table>
I have this table. How can i make that the filter works only on descriptionLong or descriptionShort and not in primaryKey.
At this time the filter works only on descriptionLong.
DATA sceernshot
I suppose this is what you want:
<input ng-model="searchTerm">
<table>
<tr>
<th>Primary Key</th>
<th>descriptionShort</th>
<th>descriptionLong</th>
</tr>
<tr ng-repeat="terReason in data | filter : myFilter">
<td>{{ terReason.primaryKey }}</td>
<td>{{ terReason.descriptionShort }}</td>
<td>{{ terReason.descriptionLong }}</td>
</tr>
</table>
JS:
$scope.data = [
{
primaryKey: 0,
descriptionShort: 'descriptionShort ZERO',
descriptionLong: 'descriptionLong descriptionLong descriptionLong ZERO'
},
{
primaryKey: 1,
descriptionShort: 'descriptionShort ONE',
descriptionLong: 'descriptionLong descriptionLong descriptionLong ONE'
},
{
primaryKey: 2,
descriptionShort: 'descriptionShort TWO',
descriptionLong: 'descriptionLong descriptionLong descriptionLong TWO'
},
{
primaryKey: 3,
descriptionShort: 'descriptionShort THREE',
descriptionLong: 'descriptionLong descriptionLong descriptionLong THREE'
}
];
$scope.myFilter = function myFilter (value) {
return !$scope.searchTerm || Object.keys(value).some(function (key) {
return key !== 'primaryKey' && value[key].search($scope.searchTerm) !== -1;
});
}
pluner: http://plnkr.co/edit/VMcs064WWbGraULyXQTU?p=preview
try to search by 0 or 1 - nothing displayed, it is not searching by primary key.
<tr ng-repeat="terReason in data | filter :filter1 | filter :filter2 ">
<td>terReason.primaryKey</td>
<td>terReason.descriptionShort</td>
<td>terReason.descriptionLong</td>
</tr>
Why not use multiple filters?

Categories