How to calculate partial value and total value in Vue.js? - javascript

I'm starting with Vue.js and I don't know how can I calculate the partial value and total value inside a v-for display.
I'm getting some info from a JSON with the next structure:
[saldos]
[bank]
[data]
[account]
- name
- balance
[account]
- name
- balance
[meta]
- info
[bank]
[data]
[account]
- name
- balance
[account]
- name
- balance
[meta]
- info
Each bank could be 0 accounts, 1 account or more accounts.
I need to get the partial value of each bank (it is the sum of all accounts 'balance' inside the same bank) and the total value (it is the sum of all partial values previously calculated for each bank)
My Vue.js file is:
var saldo = new Vue({
el: "#listasaldos",
data: {
saldos:[],
},
created: function(){
console.log("Cargando ...");
this.get_saldos();
},
methods:{
get_saldos: function(){
fetch("./api.php?action=saldosapi")
.then(response=>response.json())
.then(json=>{this.saldos=json.saldos})
}
}
});
And my HTML file is:
<div id="listasaldos">
<h1>Title</h1>
<h2>{{totalValue}}</h2>
<div v-for="bank in saldos">
<h3>{{partialValue}}</h3>
<table>
<thead>
<tr>
<th>Name</th>
<th>Balance</th>
</tr>
</thead>
<tbody v-for="account in bank.data">
<tr> {{account.name}}</tr>
<tr> {{account.balance}}</tr>
</tbody>
</table>
</div>
</div>
How can I do it?
Thanks!

For the saldos total, you could add a computed property and use reduce to calculate the total:
computed: {
totalValue() {
return this.saldos.map(({data}) => data).flat().reduce((a, b) => a + b.balance, 0);
}
}
I'm not familiar with how one would go about adding vue computed properties inside a loop. According to this answer you either have to create a component for each bank or add a function called getBankTotal inside methods:
getBankTotal: function(bank){
return bank.data.reduce((a, b) => a + b.balance, 0)
}
And use it in your HTML:
<h3>{{getBankTotal(bank)}}</h3>
Here's a working snippet:
(I have not used vue before. So, correct me if any syntax or pattern is wrong)
var saldo = new Vue({
el: "#listasaldos",
data: {
saldos: [],
},
created: function() {
this.get_saldos();
},
methods: {
getBankTotal: function(bank) {
return bank.data.reduce((a, b) => a + b.balance, 0)
},
get_saldos: function() {
this.saldos = [{
data: [{
name: "a/c 1",
balance: 100
}]
}, {
data: [{
name: "a/c 2",
balance: 300
}, {
name: "a/c 3",
balance: 400
}]
}]
}
},
computed: {
totalValue() {
return this.saldos.map(({data}) => data).flat().reduce((a, b) => a + b.balance, 0);
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="listasaldos">
<h1>Title</h1>
<h2>Total Value: {{totalValue}}</h2>
<div v-for="bank in saldos">
<h3>Partial Value: {{getBankTotal(bank)}}</h3>
<table>
<thead>
<tr>
<th>Name</th>
<th>Balance</th>
</tr>
</thead>
<tbody>
<tr v-for="account in bank.data">
<td>{{account.name}}</td>
<td>{{account.balance}}</td>
</tr>
</tbody>
</table>
<hr>
</div>
</div>

Related

Vue.js : How to iterate with v-for into a dynamic array

I want to display multiples html tables of tools (1 table = 1 tool's categorie / 1 tr = 1 tool).
data() {
return {
cats: '',
tools: [],
};
},
methods: {
getToolCats() {
var rez = getToolCats();
rez.then(data => this.receiveCats(data) )
},
receiveCats(_cats){
this.cats = _cats;
_cats.forEach(cat => {
getToolsByCat(cat.href).then(data => this.tools[cat.href] = data);
});
console.log(this.tools);
},
},
mounted() {
this.getToolCats();
},
cats (ie categories) is an array populated with an API call. Then for each cat, an API Call give me a tool list of that cat, that I place into the tools array (this.tools[cat.href] = data).
Here is the display code :
<div v-for="cat in cats" :key="cat.href" class="tbox col-xs-12 col-sm-6">
....
<table class="table table-hover">
<tr v-for="tool in tools[cat.href]" :key="tool.href">
<td>...</td>
</tr>
</table>
....
</div>
If i'm using a single var to store lthe tool list, all is OK. But while I don't know how many cats I'm going to have, I can't create a car for each category.
I think the problem could be there :
Using an array in v-for with a key not defined at mounted state :
v-for="tool in tools[cat.href]
I'll appreciate any help !
Vue can't detect dynamic property addition in this.tools[cat.href] = data, but it would detect the change with this.$set or Vue.set in this.$set(this.tools, cat.href, data):
new Vue({
el: '#app',
data() {
return {
cats: [],
tools: {}, // <-- make this an object (not an array)
};
},
mounted() {
this.getToolCats();
},
methods: {
getToolCats() {
// setTimeout to simulate delayed API calls...
setTimeout(() => {
this.cats = [
{ href: 'https://google.com' },
{ href: 'https://microsoft.com' },
{ href: 'https://apple.com' },
{ href: 'https://amazon.com' },
];
this.cats.forEach((cat, i) => {
setTimeout(() => {
const data = { href: cat.href };
this.$set(this.tools, cat.href, data); // <-- use this.$set for dynamic property addition
}, i * 1000);
})
}, 1000);
}
}
})
<script src="https://unpkg.com/vue#2.5.17"></script>
<div id="app">
<div v-if="!cats || cats.length == 0">Loading...</div>
<div v-for="cat in cats" :key="cat.href" v-else>
<table>
<tr v-for="tool in tools[cat.href]" :key="tool.href">
<td>cat.href: {{cat.href}}</td>
</tr>
</table>
</div>
<pre>tools: {{tools}}</pre>
</div>

vuejs2 get value from multi inputs with same model name

i have project working with vuejs2
this is my html code
<tr v-for="arrayresult , key in arrayresults">
<td>#{{ arrayresult.item_desc_ar}}</td>
<td><input class='form-control quantity' type='text' #input='changeQuantity()' v-model='quantity'/></td>
<td>#{{ arrayresult.item_smallest_unit_selling_price}}</td>
<td><a class='fa fa-trash' #click='deleteItem(arrayresult.id,key)'></a></td>
</tr>
and this is my vuejs2 code
data:{
message:'',
item_search_array:false,
arrayresults:[],
total:0,
quantity:1,
},
methods:{
changeQuantity:function()
{
},
deleteItem:function(key)
{
this.arrayresults.splice(key, 1);
}
}
now i have this method called changeQuantity i need when keyup the input with model name quantity send the value and the key index to the method changeQuantity my problem thats they are many input with same model name quantity thanks
You need to use object property as v-model for each input.
<input ... v-model="quantities[input_id_iterator]" />
Do not forget to define quantities object in data.
Think of each item in the arrayresults array as a model, then in your input, you update the particular model model='arrayresult.qty'.
You then can use computed properties to get the totals.
For example:
//
var vm = new Vue({
el: '#app',
computed: {
totalQty: function () {
var total = 0;
this.arrayresults.forEach(item => {
total += Number(item.qty);
})
return total
},
totalPrice: function () {
var total = 0;
this.arrayresults.forEach(item => {
total += Number(item.item_smallest_unit_selling_price * item.qty);
})
return total
}
},
data() {
return {
arrayresults:[
{id: 1, item_desc_ar: 'A', item_smallest_unit_selling_price: 5, qty:1},
{id: 2, item_desc_ar: 'B', item_smallest_unit_selling_price: 10, qty:1},
{id: 3, item_desc_ar: 'C', item_smallest_unit_selling_price: 15, qty:1},
{id: 4, item_desc_ar: 'D', item_smallest_unit_selling_price: 20, qty:1},
]
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.14/vue.min.js"></script>
<div id="app">
Total Qty: {{totalQty}}<br>
Total Price: {{totalPrice}}
<table>
<tr v-for="arrayresult , key in arrayresults">
<td>#{{ arrayresult.item_desc_ar}}</td>
<td><input class='form-control quantity' type='text' v-model='arrayresult.qty'/></td>
<td>#{{ arrayresult.item_smallest_unit_selling_price}}</td>
<td><a class='fa fa-trash' #click='deleteItem(arrayresult.id,key)'></a></td>
</tr>
</table>
</div>
p.s: also try avoid item_, if you think of each model in an items array as an item you dont need to include item in the property name.

How to use angular ng-repeat for following JSON object?

I want to match headfield values with datafield, and have to fetch corresponding value into table data.
HTML:
<table>
<thead>
<tr>
<th ng-repeat="ReportsHead in ReportsHeadList">
{{ReportsHead.headerfield = ReportsHead.headfield}}
</th>
</tr>
</thead>
<tbody>
<tr>
<td ng-repeat="ReportsData in ReportsDataList"
ng-if="ReportsHead.headerfield == ReportsData.datafield">
{{ReportsData.value}}
</td>
</tr>
</tbody>
</table>
JSON Data :
$scope.ReportsHeadList = [
{
'headfield':'Contact Name'
},
{
'headfield':'Phone'
},
{
'headfield':'Email'
}
];
$scope.ReportsDataList = {
[
{
'datafield':'Contact Name',
'value':'Gunaseelan'
},
{
'datafield':'Phone',
'value':'8122911043'
},
{
'datafield':'Email',
'value':'G#gmail.com'
}
],
[
{
'datafield':'Contact Name',
'value':'Gunaseelan'
},
{
'datafield':'Phone',
'value':'8122911043'
},
{
'datafield':'Email',
'value':'G#gmail.com'
}
]
};
Thanks in advance.
First you should parse your Json with JSON.parse(json), and assigment the result to variable. e.g:
$scope.json = JSON.parse(json).
After, you can check for match headfield and data fields with for loop or map, e.g:
for(i in headfield) {
if (i === something)
return i
}
Hope the answar will help, if not leave a comment,
Good luck!

Iterate through nested objects in Meteor.js and insert results into table

I'm trying to make a RuneScape hiscores checker in Meteor.js. I'm now successfully parsing data from their API and saving it as object (you can see app in action here: http://rs-hiscores.meteor.com/). Here's my server code:
var cheerio = Meteor.npmRequire('cheerio');
var BASE_SKILLS = [
'overall', 'attack', 'defence', 'strength', 'hitpoints', 'ranged',
'prayer', 'magic', 'cooking', 'woodcutting', 'fletching', 'fishing',
'firemaking', 'crafting', 'smithing', 'mining', 'herblore', 'agility',
'thieving', 'slayer', 'farming', 'runecrafting', 'hunter', 'construction'
];
var skills = {
'osrs': BASE_SKILLS,
'rs3': BASE_SKILLS.concat('summoning', 'dungeoneering', 'divination')
};
var urls = {
'osrs': 'http://services.runescape.com/m=hiscore_oldschool/index_lite.ws?player=',
'rs3': 'http://hiscore.runescape.com/index_lite.ws?player='
};
Meteor.methods({
lookup: function(player, game) {
if (!urls.hasOwnProperty(game)) {
game = 'rs3';
}
var url = urls[game].concat(encodeURIComponent(player));
result = Meteor.http.get(url);
$ = cheerio.load(result.content);
body = $.html();
parsed = Meteor.call('parseStats', body, skills[game]);
return parsed;
},
parseStats: function(raw, skillsList) {
var stats = raw.split('\n').slice(0, skillsList.length);
var statsObj = { };
stats.forEach(function(stat, index) {
var chunk = stat.split(',');
statsObj[skillsList[index]] = {
rank: +chunk[0],
level: +chunk[1],
xp: +chunk[2]
};
});
return statsObj;
}
});
In my client code, I'm testing lookup method manually by providing player name inside function as an argument:
Meteor.call('lookup', 'zezima', 'rs3', function(error, result) {
if (error) {
console.error(error);
}
console.log(result);
Session.set("data", result);
});
Template.rawdata.helpers({
getData: function() {
return Session.get("data");
}
});
Result in console:
{
agility: {
level: 99,
rank: 174,
xp: 100234567
},
attack: {
level: 99,
rank: 601,
xp: 127370193
},
construction: {
level: 99,
rank: 119,
xp: 141170001
},
and so on...
}
Obviously, when I call getData inside my template in html and render it, it shows: [object Object]. So I want to somehow iterate through objects and insert them into table, that will look like this one:
<table id="rs-hiscores">
<thead>
<tr>
<th>Skill name</th>
<th>Rank</th>
<th>Level</th>
<th>Xp</th>
</tr>
</thead>
<tbody id="rs-hiscore-data">
<tr>
<td>Agility</td>
<td>99</td>
<td>174</td>
<td>109317063</td>
</tr>
<tr>
<td>Attack</td>
<td>99</td>
<td>601</td>
<td>127370193</td>
</tr>
<tr>
<td>Construction</td>
<td>99</td>
<td>119</td>
<td>141170001</td>
</tr>
...and so on ...
</tbody>
</table>
How can I do that? And also - whould it be possible to sort results in the same order as skills are inside BASE_SKILLS? Thank for help in advance.
Okay so you're going to need the data in a decent array, in the order of BASE_SKILLS, using Underscore to map the skills from your data to a new array, your getData helper should look like this:
var data = Session.get('data');
var array = _.map( BASE_SKILLS, function(skillName){
var skill = data[skillName]
return {
name: skillName,
rank: skill.rank,
level: skill.level,
xp: skill.xp
};
});
return array;
This makes a neat array that looks like this:
[
{
name: "overall",
rank: 99,
level: 174,
xp: 109317063
},
{
name: "attack",
rank: 23,
level: 201,
xp: 256311563
},
...etc
]
Then you'll need to get those values into the html in the way you want.
<table id="rs-hiscores">
<thead>
<tr>
<th>Skill name</th>
<th>Rank</th>
<th>Level</th>
<th>Xp</th>
</tr>
</thead>
<tbody id="rs-hiscore-data">
{{#each getData}}
<tr>
<td>{{name}}</td>
<td>{{rank}}</td>
<td>{{level}}</td>
<td>{{xp}}</td>
</tr>
{{/each}}
</tbody>
</table>
You will have the lowercase keys in your table though, overall instead of "Overall" but to get nice names you can just make a dictionary helper function that swaps the one for the other like so: {{getNiceName name}}.

How do I return document property from document in another collection by ID using Meteor?

I have two collections: Products and Category. The insertion of both is working fine. My problem is returning the name of a category in products list. It's listing with _id from Category. I need its name.
Here is Products collections:
Products = new Meteor.Collection('products');
/*
* Add query methods like this:
* Products.findPublic = function () {
* return Products.find({is_public: true});
* }
*/
var Schema = {};
Schema.Products = new SimpleSchema({
name: {
type: String,
label: "Nome",
optional: false
},
category: {
type: Schema.Categories,
optional: false
},
"category.$.name": {
type: String
},
subcategory: {
type: Schema.Subcategories,
optional: true
},
"subcategory.$.name": {
type: String
},
description: {
type: String,
label: "Descrição",
optional: true,
max: 150
}
});
Products.attachSchema(Schema.Products);
Products helper to list them:
Template.Products.helpers({
// Return all products
list: function() {
return Products.find({}, {
sort: {
time: -1
},
fields: {
name: true,
category: true,
subcategory: true
}
}).fetch();
}
});
And Products template:
<template name="Products">
<fieldset>
<legend>
<p class="pull-left">
Produtos
</p>
Novo Cadastro
<div class="clearfix"></div>
</legend>
<table class="table">
<thead>
<tr>
<th>Nome</th>
<th>Categoria</th>
<th>Subcategoria</th>
<th></th>
</tr>
</thead>
<tbody>
{{#each list}}
<tr>
<td>{{name}}</td>
<td>{{category}}</td>
<td>{{subcategory}}</td>
<td>
Editar
</td>
<td>
<button class="destroy btn btn-danger btn-xs">Delete</button>
</td>
</tr>
{{/each}}
</tbody>
</table>
</fieldset>
</template>
How can I do that?
Here is an image of my problem:
David is right. A single product example would be helpful. It's hard to understand what you are trying to do. But my best guess is this:
In your template try
<td>{{categoryName}}</td>
<td>{{subcategoryName}}</td>
And in your js
Template.Products.categoryName = function() {
var _category = Categories.findOne({_id: this.category});
return _category ? _category.name : "";
}
Template.Products.subcategoryName = function() {
var _subcategory = Subcategories.findOne({_id: this.subcategory});
return _subcategory ? _subcategory.name : "";
}

Categories