This question already has answers here:
Angular2 nested *ngFor
(2 answers)
Closed 1 year ago.
In my Ionic Angular app, I'm trying to display the below array that contains several workout exercises.
Each exercise consits of several sets:
exercises = [
{
name: 'Leg Extension Machine',
sets: [
{ setNo: 1, reps: 8, weight: 40 },
{ setNo: 2, reps: 10, weight: 36 },
{ setNo: 3, reps: 15, weight: 33 },
],
},
{
name: 'Leg Press Machine',
sets: [
{ setNo: 1, reps: 8, weight: 80 },
{ setNo: 2, reps: 10, weight: 72 },
{ setNo: 3, reps: 15, weight: 65 },
],
},
{
name: 'Rear Leg Elevated Split Squat (Bench)',
sets: [
{ setNo: 1, reps: 8, weight: 8 },
{ setNo: 2, reps: 10, weight: 8 },
{ setNo: 3, reps: 15, weight: 8 },
],
},
];
I'm able to use ngFor to display the exercise names like so:
<ion-card *ngFor="let exercise of exercises">
<ion-card-header>
<ion-card-title>
{{exercise.name}}
</ion-card-title>
</ion-card-header>
</ion-card>
It displays like so:
Now, I am trying to display each set under each Exercise Name.
I've managed to log data related to each set to the console using the below nested loops, but I don't know how to get this data to the front end:
ngOnInit() {
for (let i = 0; i < this.exercises.length; i++) {
this.exercises[i].sets;
console.log('---', this.exercises[i].name);
for (let x = 0; x < this.exercises[i].sets.length; x++) {
console.log('---', this.exercises[i].sets[x].weight);
}
}
}
This should work:
<ion-card *ngFor="let exercise of exercises">
<ion-card-header>
<ion-card-title>
{{exercise.name}}
</ion-card-title>
</ion-card-header>
<ion-card-content>
<div *ngFor="let set of exercise.sets">
<ul>
<li><strong>Nr.</strong> {{set.setNo}}</li>
<li><strong>Reps</strong> {{set.reps}}</li>
<li><strong>Weight</strong> {{set.weight}}</li>
</ul>
</div>
</ion-card-content>
</ion-card>
You already have the current object of your exercise assigned to exercise so you can then retreive the sets from that variable and iterate over these sets.
In the template, you need to use 3 loops and for displaying the individual set by keys and value you need to leverage the keyvalue pipe.
<div *ngFor="let exercise of exercises">
<h2>{{ exercise.name }}</h2>
<div *ngFor="let set of exercise.sets">
<span *ngFor="let prop of set | keyvalue">
{{ prop.key }}: {{ prop.value }}
</span>
</div>
</div>
Here's a stackblitz
Related
In Vue.js for calculating percentage I have the following code:
<tr>
<span v-for="(item, index) in data.values" :key="index" >
{{ item.name }} {{ getPercentage(item.nr) }}
</span>
</tr>
And in the percentage method:
methods: {
getPercentage (number) {
return number/ this.nrUsers * 100; (ignore the logic)
}
}
The percentage values are calculated alright but the problem is when displaying them. I would like to have sorted descending and I don't think I can do it from BE because the percentage is calculated here. Any ideas how to do that in Vue.js?
You can create computed property instead of method:
const app = Vue.createApp({
data() {
return {
items: [{id: 1, nr: 5, name: 'aaa'}, {id: 5, nr: 15, name: 'bbb'}, {id: 2, nr: 7, name: 'ccc'}, {id: 3, nr: 24, name: 'ddd'}, {id: 4, nr: 25, name: 'eee'}],
};
},
computed: {
sortedItems() {
return this.items.map(item => {
let pct = item.nr / 100 * this.items.length;
return { ...item, pct }
}).sort((a, b) => b.nr - a.nr)
}
},
})
app.mount('#demo')
<script src="https://unpkg.com/vue#3/dist/vue.global.prod.js"></script>
<div id="demo">
<li v-for="(item, index) in sortedItems" :key="index" >
{{ item.name }} {{ item.pct }}
</li>
</div>
You can't sort the items at the time of rendering. You first need to sort your data in descending order and then you can render.
According to your comment, you can surely sort the items first.
Fetch the data from API.
Loop over data items and calculate their percentage.
Sort the data in descending order according to the percentage value.
And last, render this sorted data in the template.
Hope this gives some direction.
/*
Write each function according to the instructions.
When a function's parameters reference `cart`, it references an object that looks like the one that follows.
{
"Gold Round Sunglasses": { quantity: 1, priceInCents: 1000 },
"Pink Bucket Hat": { quantity: 2, priceInCents: 1260 }
}
*/
function calculateCartTotal(cart) {
let total = 0;
for (const item in cart){
let quantity = Object.values(cart[item])[0];
let price = Object.values(cart[item])[1];
total += price * quantity;
}
return total;
}
function printCartInventory(cart) {
let inventory = "";
for (const item in cart){
inventory += `${Object.values(cart[item])[0]}x${item}\n`;
}
return inventory;
}
module.exports = {
calculateCartTotal,
printCartInventory,
};
The part that confuses me is the function calculateCartTotal. What I am confused about is how does this loop know to grab priceInCents? for example, if I was to add another value into the object called "weight: 24" assuming that it is 24 grams, how does the object value skip over quantity and weight and just grab priceInCents? Hopefully I am making sense on how I am confused and that someone has an explanation for me!
If you try to run below program then it will be easier for you to visualize everything.
What is happening is item is just the index of element and for an object we can either use the key name to access its value or its index.
You can read this doc to understand what Object.values() does.
function calculateCartTotal(cart) {
let total = 0;
for (const item in cart) {
console.log(item)
let quantity = Object.values(cart[item])[0];
let price = Object.values(cart[item])[1];
total += price * quantity;
}
return total;
}
var cart = [
{
quantity: 2,
price: 5,
weight: 24
},
{
quantity: 3,
price: 10,
weight: 90
},
{
quantity: 7,
price: 20,
weight: 45
},
{
quantity: 1,
price: 100,
weight: 67
}
]
console.log(calculateCartTotal(cart))
OUTPUT:
0
1
2
3
280
Program 2 to demonstrate what is happening
function calculateCartTotal(cart) {
console.log(Object.values(cart[2])[1])
console.log(cart[2]['price'])
console.log(cart[2].price)
}
var cart = [
{
quantity: 2,
price: 5,
weight: 24
},
{
quantity: 3,
price: 10,
weight: 90
},
{
quantity: 7,
price: 20,
weight: 45
},
{
quantity: 1,
price: 100,
weight: 67
}
]
calculateCartTotal(cart)
OUTPUT:
20
20
20
I am Deepak,🙂
I can explain (program 2 demonstration). See my friend,you will be able to see the first console.log i.e, console.log(Object.values(cart[2])[1])...
So, object means the whole cart and .values means the only numbers that a particular object contained. Now, see the result i.e, 20.
So, how this 20 will came?...
Now, see the console.log that I have written before. In the brackets of .value cart of [2](it means that 2 is the position of that cart, that why it is written as cart[2] i.e, inside a cart 2nd position's object and after cart[2] this one number is there [1], it means inside the 2nd position's object i.e,
OBJECT below:- position of an objects
var cart = [
quantity: 2, 0 position
price: 5,
weight: 24
},
{
quantity: 3, 1 position
price: 10,
weight: 90
},
{
quantity: 7, 2 position
price: 20,
weight: 45
} ,
{
quantity: 1, 3 position
price: 100,
weight: 67
}
]
console.log(calculateCartTotal(cart))
Now, match the console.log.
it says that console.log(OBJECT.values(cart[2])[1]);
In the cart, see the 2nd position's object i.e,
{
quantity: 7, 2 position
price: 20,
weight: 45
}
So, cart[2] means the whole object you will above.
[1] means inside the object count the position from 0 onwards. So, in the
values
0 position quantity, 7,
1 position price, 20,
2 position weight. 45.
In the [1] position price: 20.
So, cart[2] means
{
quantity: 7, 2 position
price: 20,
weight: 45
}
And,
[1] means price: 20.
So, your answer is 20.
Note: the numbers that is inside the square brackets will gives the position of an object or inside an object.
[
RowDataPacket {
House_Name: 'Merlin 5th Avenue',
no_of_rooms: 6,
Cost: 3400000,
PropertyId: 2
},
RowDataPacket {
House_Name: 'Presidential Building',
no_of_rooms: 5,
Cost: 3500000,
PropertyId: 106
},
RowDataPacket {
House_Name: 'GreenSlide Building',
no_of_rooms: 4,
Cost: 6300000,
PropertyId: 107
},
RowDataPacket {
House_Name: 'RabindraNath Building',
no_of_rooms: 4,
Cost: 5500000,
PropertyId: 108
},
RowDataPacket {
House_Name: 'CityView Apartment',
no_of_rooms: 3,
Cost: 4300000,
PropertyId: 109
}
]
I have this array of objects that I want to convert to string so that I can pass it in the get request to some other page. How can I convert the array of objects to string.
The number of objects in the array is dynamic and can change. The array shown above is just one example.
Is there any other way to pass array in a get request?
I am passing the array into a jade file, which then calls a get request?
doctype html
html(lang='en')
head
meta(charset='UTF-8')
title Property List
link(rel='stylesheet', href='/stylesheets/prop-list-style.css')
link(href='//netdna.bootstrapcdn.com/font-awesome/4.1.0/css/font-awesome.min.css', rel='stylesheet')
link(rel='stylesheet', href='/stylesheets/likeButton.css')
body
// partial:index.partial.html https://picsum.photos/500/300/?image=10
.main
h1(data-text='Property List') Property List
- var prop = props
ul.cards
for pip in prop
li.cards_item
.card
.card_image
img(src='https://picsum.photos/500/300/?image=10')
.card_content
h2.card_title #{pip.House_Name}
p.card_text #{pip.no_of_rooms} BHK
a(href='/signIn/interested?id=#{pip.PropertyId}&cid=#{customerid}&values=#{properties}')
i.like-button.fa.fa-2x.fa-heart-o(class='#{like}')
p.card_text Price: #{pip.Cost}
a(href='/signIn/readMore?id=#{pip.PropertyId}&cid=#{customerid}&values=#{properties}')
button.btn.card_btn Find Out More
else
h2 No such property exist
script(src='/javascripts/likebutton.js')
// partial
The #{properties} variable is the variable which holds the array and is a part of the get request.
You can try using this code for converting array of objects in to string
first assign the array of objects to a variable and then use json.stringify() method to convert array of objects in to string
var random = [{},{},{}.....];
JSON.stringify(random);
and to convert string back to array of objects use
JSON.parse(random);
And also you are making the array of objects wrongly it should be like this and then the above stringify and parse method will work completely fine.
[
{
House_Name: 'Merlin 5th Avenue',
no_of_rooms: 6,
Cost: 3400000,
PropertyId: 2
},
{
House_Name: 'Presidential Building',
no_of_rooms: 5,
Cost: 3500000,
PropertyId: 106
},
{
House_Name: 'GreenSlide Building',
no_of_rooms: 4,
Cost: 6300000,
PropertyId: 107
},
{
House_Name: 'RabindraNath Building',
no_of_rooms: 4,
Cost: 5500000,
PropertyId: 108
},
{
House_Name: 'CityView Apartment',
no_of_rooms: 3,
Cost: 4300000,
PropertyId: 109
}
]
I have a data set like following table.
+------+---------+----+----+----+----+-------+----------+
| Year | Subject | A | B | C | F | Total | PassRate |
+------+---------+----+----+----+----+-------+----------+
| 2015 | Maths | 12 | 20 | 10 | 5 | 47 | 80 |
| 2015 | Sinhala | 18 | 14 | 5 | 10 | 47 | 75 |
| 2016 | Maths | 25 | 15 | 4 | 8 | 52 | 25 |
| 2016 | Sinhala | 20 | 12 | 2 | 18 | 52 | 60 |
+------+---------+----+----+----+----+-------+----------+
I want to store those data in JavaScript array. So I have following code.
var firstArray = [];
firstArray.push(['Year', 'Subject', 'A', 'B', 'C', 'F', 'Total', 'PassRate']); // headers
firstArray.push([2015, 'Maths', 12, 20, 10, 5, 47, 80]); // 1st row
firstArray.push([2015, 'Sinhala', 18, 14, 5, 10, 47, 75]) // 2nd row
console.log(firstArray);
If I need to read how many "B",s in Maths for 2015, I need to run firstArray[1][3].
That is not readable. I mean it is hard to find what it means firstArray[1][3].
So is there way to build my array more readable way like firstArray[2015]['maths'] if I want to read how many "B",s in Maths for 2015
Sounds like you want an object indexed by year, containing objects indexed by subject:
const years = {
'2015': {
Maths: {
A: 12, B: 20, C: 10, F: 5, Total: 47, PassRate: 80
},
Sinhala: {
A: 18, B: 14, C: 5, F: 10, Total: 47, PassRate: 75
},
},
'2016': {
// ...
}
}
console.log(years['2015'].Maths);
Your purpose is correct, readability of code is very important.
It's not easy and there is no right path to follow, but I try to give you some hints on how to change your code.
First point: naming.
This is very hard, and often even experienced developer need to rename variables as they can't get the proper name at the first time.
Your variable is firstArray and this of course have low meaning and you can just say it is an array and it is the first...
Nothing about what the array is containing.
A better name could be studentsScoreByYear.
The improvement of this name is that it try to address the meaning of the content.
Then the index ad magic numbers.
Of course you need numbers to get a field from an array, and if you just use the proper integer in the code is very complicated to remember what this field is.
A way is to avoid array and use hash map, in javascript plain objects.
So you can give to each field a name.
If you can't abbandon the array for whatever reason, you can improve here too, just use constants to save the proper indexes:
var MATHS = 1;
var SCORE_OF_B = 3;
var studentsScoreByYear= [
['Year', 'Subject', 'A', 'B', 'C', 'F', 'Total', 'PassRate'],
[2015, 'Maths', 12, 20, 10, 5, 47, 80],
[2015, 'Sinhala', 18, 14, 5, 10, 47, 75]
];
console.log(studentsScoreByYear[MATHS][SCORE_OF_B]);
There are other ways of course, this is just a possibility.
A list of objects is what you want to represent your data in a readable format.
As for selecting data:
You can filter an array by using the filter method on the array.
You can change the contents or isolate a parameter with the map method on the array.
You can perform arithmetic operations with the reduce method on the array.
var data = [
{ year: 2015, subject: 'Maths', a: 12, b: 20, c: 10, f: 5, total: 47, passrate: 80 },
{ year: 2015, subject: 'Sinhala', a: 18, b: 14, c: 5, f: 10, total: 47, passrate: 75 },
{ year: 2016, subject: 'Maths', a: 25, b: 15, c: 4, f: 8, total: 52, passrate: 25 },
{ year: 2016, subject: 'Sinhala', a: 20, b: 12, c: 2, f: 18, total: 52, passrate: 60 },
];
console.log("Present All:");
console.log(data
.map(function (row) {
return [row.year, row.subject, row.a, row.b, row.c, row.f, row.total, row.passrate].join(", ");
})
.join("\n"));
console.log("Count `B` in `Maths` in 2015");
console.log(data
.filter(function (row) { return row.year === 2015 && row.subject === "Maths"; })
.map(function (row) { return row.b; })
.reduce(function (accumulator, currentValue) { return accumulator + currentValue; }, 0));
Sidenote
The eagle-eyed out there has probably already spotted that the map and filter calls are redundant in the "Present All" example as i could simply have put:
data.reduce(function (accumulator, row) {
return accumulator + (row.year === 2015 && row.subject === "Maths" ? row.b : 0);
}, 0);
While the above code is "smarter", i think the more verbose answer using map and filter will be of more use to anyone trying to learn JavaScript array handling and JavaScript objects.
I have 2 separate json objects coming from the server. Json A below is of a Car model object which is fetch when looking at a car. Json B is meta data which is used throughout the whole application when the web page first loads.
What I need to do is have a lookup on wheel_id while doing a ng-repeat on wheel_handlers so it returns the wheel object from json B and then I can use this within the view and print the results. I think I need to do something with the ng-repeat but I'm not sure to be honest.
A - Car model
[{
id: 14,
name: "Audi",
wheel_handlers: [
{
id: 9,
wheel_id: 62,
arguments: {
amount: 10
}
}
]
}]
B - wheel
{
id: 62,
name: "Change Wheel Size",
arguments: [
{
id: 25,
description: "amount"
}
]
}
I am assuming the following: The Json "A" may include several cars, but also several wheel_handlers (because there is an array at wheel_handler). So the JSON for the cars may also look like this:
[
{
id: 14,
name: "Audi",
wheel_handlers: [
{
id: 9,
wheel_id: 62,
arguments: {
amount: 10
}
},
{
id: 12,
wheel_id: 65,
arguments: {
amount: 12
}
},
{
id: 15,
wheel_id: 30,
arguments: {
amount: 8
}
}
]
},
{
id: 16,
name: "Mercedes",
wheel_handlers: [
{
id: 9,
wheel_id: 62,
arguments: {
amount: 10
}
},
{
id: 12,
wheel_id: 65,
arguments: {
amount: 12
}
}
]
}
]
For the JSON file B I assume that you also meant an Array, which could contain several wheel definitions. As an example:
[
{
id: 62,
name: "Change Wheel Size",
arguments: [
{
id: 25,
description: "amount"
}
]
},
{
id: 65,
name: "test wheel",
arguments: [
{
id: 25,
description: "amount"
}
]
},
{
id: 30,
name: "another wheel",
arguments: [
{
id: 25,
description: "amount"
}
]
}
]
If this is the case, you could iterate over the cars and while iterating call a helper function in the AngularJS controller. You call this helper function and give wheel_handlers of the current car as a parameter. This helper function then checks the wheel_id of each wheel_handler entry and searches these ids in the JSON b file - the wheel definitions. The helper function returns an array containing the wheels, so in the view you may iterate over the wheels. This will use a nested ng-repeat, because at first you iterate over the cars and while iterating over the cars you will iterate over the wheels.
Here is an example of the controller part. I used $scope.cars as the JSON A, and $scope.wheels as JSON B.
var testApp = angular.module('testApp', []);
testApp.controller('testContr', function ($scope) {
$scope.cars = [];
$scope.wheels = [];
$scope.getWheelsByIds = function (wheel_handlers) {
var wheelIds = [];
var returnArray = [];
for (var wheelKey in wheel_handlers) {
wheelIds.push(wheel_handlers[wheelKey].wheel_id);
}
for (var key in $scope.wheels) {
console.log(wheelIds.indexOf($scope.wheels[key].id));
if (wheelIds.indexOf($scope.wheels[key].id) > -1) {
returnArray.push($scope.wheels[key]);
}
}
return returnArray;
}
});
The necessary HTML part could look like this:
<div ng-app="testApp" ng-controller="testContr">
<div ng-repeat="car in cars" ng-init="wheels = getWheelsByIds(car.wheel_handlers)">
<span>Car name: {{car.name}}</span><br/>
<div ng-repeat="wheel in wheels">
<span>Wheel name: {{wheel.name}}</span><br/>
</div>
<br/>
<br/>
</div>
</div>
I create a fiddle demonstration with the test data, view it here: http://jsfiddle.net/4F3YD/10/
You can nest ng-repeats like that, although I'm not sure what you want to achieve
following code will repeat through cars, then wheels in cars and display wheels from object B(wheels) that match the car wheel id, hope that makes sense
<div ng-repeat="car in CarModels">
<div ng-repeat="wheel in car.wheel_handlers">
{{Wheels | filter:wheel.wheel_id}}
</div>
</div>
You can make use of angular filter over here. In the filter function you can check for the id in the second json.
More Documentation on Angular Filter
Code Example:
<div ng-repeat="element in wheel | filterIds:element.id">
And filter Function:
.filter('filterIds', function () {
return function(id) {
$scope.carModel.forEach(function(car){
if(id == car.id)
return id;
});
}
})