Angular nested *ngFor - javascript

I am having a table with two nested iterations, the first three columns where an array of object (item) iterates, and a fourth column is where an array of numbers should iterate ( total=[30,70,100] )
<table class="table">
<thead class="thead-dark">
<tr>
<th>Item</th>
<th>Unit Price</th>
<th>Quantity</th>
<th>Amount</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let i of item">
<td>{{i.product}}</td>
<td>{{i.price}}</td>
<td>{{i.quantity}}</td>
<ng-container *ngFor="let t of total">
<td>{{t}}</td>
</ng-container>
</tr>
</tbody>
</table>
The object array iterates fine, however, the problem is with the array of numbers ( total=[30,70,100] ), I tried to place the (ng-container *ngFor="let t of total") on different levels, but it always getting populated in a wrong way, I would appreciate advising me how to solve it.

Assuming your index level for the arrays are same, you can always use the index to track and repeat the element.
<tr *ngFor="let i of item; let in=index">
<td>{{i.product}}</td>
<td>{{i.price}}</td>
<td>{{i.quantity}}</td>
<ng-container *ngFor="let t of total[in]">
<td>{{t}}</td>
</ng-container>
</tr>
Edit: If the OP is multiplying quantity with price to come up with total then we don't need to maintain a separate array for that, we can just multiply it in the view and get it:
<tr *ngFor="let i of item; let in=index">
<td>{{i.product}}</td>
<td>{{i.price}}</td>
<td>{{i.quantity}}</td>
<td>{{i.price * i.quantity}}</td>
</tr>

We may need more information on how the data should be populated, what I can see is that the td tag is repeating in a wrong way when iterates with the total, having more tds than th tags. Also the total variable is a property inside the item or is separated?. For now I can give you examples for some scenarios:
If you want to have the total in a single td and the total is a property of i
<tr *ngFor="let i of item">
<td>{{i.product}}</td>
<td>{{i.price}}</td>
<td>{{i.quantity}}</td>
<td>{{i.total.join(,)}}</td>
</tr>
If you want to have the total in a single td and the total is just a variable outside the item array.
<tr *ngFor="let i of item let index = index">
<td>{{i.product}}</td>
<td>{{i.price}}</td>
<td>{{i.quantity}}</td>
<td>{{total[index]}}</td>
</tr>
If you want to have sum the total in a single td and the total is a property of i
<tr *ngFor="let i of item">
<td>{{i.product}}</td>
<td>{{i.price}}</td>
<td>{{i.quantity}}</td>
<td>{{i.total.reduce((a, b) => a + b, 0)}}</td>
</tr>
If you want to have sum the total in a single td and the total is just a variable outside the item array.
<tr *ngFor="let i of item let index = index">
<td>{{i.product}}</td>
<td>{{i.price}}</td>
<td>{{i.quantity}}</td>
<td>{{total[index].reduce((a, b) => a + b, 0)}}</td>
</tr>

Related

Create unique variable name inside *ngFor

I am trying to make a table that, when you click a button, it needs to display the row directly beneath it.
I have had a look at this post, but I could not find an answer from it.
When I have it like below, it works, but the problem is, it displays all other hidden rows since they all share the same collapse variable.
This is the working example, but not 100% correct:
<table>
<thead>
<th>Path out of this queue</th>
<th *ngFor="let role of roles">{{role.RoleName}}</th>>
</thead>
<tbody>
<ng-container *ngFor="let queue of workQueues; let i = index">
<tr>
<td><button (click)="collapse=!collapse">{{queue.WorkQueueName}}</button></td>
<td *ngFor="let role of roles">
<input type="checkbox" />
</td>
</tr>
<tr *ngIf="collapse">
Yay...
</tr>
</ng-container>
</tbody>
I thought I would be able to make the collapse variable unique, by appending the i, which is the index, to it, but then I get the following error:
Parser Error: Got interpolation ({{}}) where expression was expected
Here is my attempt:
<table>
<thead>
<th>Path out of this queue</th>
<th *ngFor="let role of roles">{{role.RoleName}}</th>>
</thead>
<tbody>
<ng-container *ngFor="let queue of workQueues; let i = index">
<tr>
<td><button (click)="{{collapse+i}}={{!collapse+i}}">{{queue.WorkQueueName}}</button></td>
<td *ngFor="let role of roles">
<input type="checkbox" />
</td>
</tr>
<tr *ngIf="{{collapse+i}}">
Yay...
</tr>
</ng-container>
</tbody>
Specifically, in my (click) event, how can I make a unique variable that could be used?
(click)="{{collapse+i}}={{!collapse+i}}"
should be
(click)="this[collapse+i] = !this[collapse+i]"
This allows you to use an indexer to obtain the field on the component. If it actually works depends on how you have collapse fields defined on your component.
Personally I would prefer extending the type contained in the workQueues array with an additional field.
(click)="queue.collapsed = !queue.collapsed"
...
<tr *ngIf="queue.collapsed">
An other alternative is to define a new field in the *ngFor.
<ng-container *ngFor="let queue of workQueues; let i = index; let isCollapsed = true">
<tr>
<td><button (click)="isCollapsed = !isCollapsed">{{queue.WorkQueueName}}</button></td>
<td *ngFor="let role of roles">
<input type="checkbox" />
</td>
</tr>
<tr *ngIf="!isCollapsed">
Yay...
</tr>
</ng-container>
stackblitz

Error in while looping in *ngFor in angular

I have the JSON of single object coming as:
I achieved this data by calling the API in component.ts file which is as follows
/* for auditgorup selecitonm */
this.http.get('http://localhost:8080/api/selections/getAllAuditGroupSelections')
.subscribe((datas: any[]) => {
this.auditSelection = datas;
console.log(this.auditSelection);
// You'll have to wait that changeDetection occurs and projects data into
// the HTML template, you can ask Angular to that for you ;-)
this.chRef1.detectChanges();
// Now you can use jQuery DataTables :
const tables: any = $('#nepal');
this.dataTable = tables.DataTable();
});
The view is as follows
<table id="nepal" class="table table-bodered">
<thead>
<tr>
<th>Selection No</th>
<th>SelectionDate</th>
<th>SelectedBy</th>
<th>PanEximNumber</th>
<th>Name</th>
<th>Address</th>
<th>PhoneNumber</th>
<th>SelectionType</th>
<th>Group Desc</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let nas of auditSelection">
<td>{{nas.selectionId}}</td>
<td>{{nas.selectionDate}}</td>
<td>{{nas.selectedBy}}</td>
<td>{{nas.panEximNumber}}</td>
<td>{{nas.name}}</td>
<td>{{nas.address}}</td>
<td>{{nas.phoneNumber}}</td>
<td>{{nas.selectionType}}</td>
<td>{{nas.assignmentAudit.auditorGroup.groupDesc}}</td>
</tr>
</tbody>
</table>
At this last line of <td>{{nas.assignmentAudit.auditorGroup.groupDesc}}</td> I cannot get the required value.
The output display should be "g1".
I am unable to comment, so adding it over here. assignmentAudit is an array, so try accessing using index
As per the analysis of the JSON structure the assignmentAudit is an array
so you can only access it with the index positions.
If you are sure the assignmentAudit is an array with single value then you can use
<td>
{{nas.assignmentAudit[0].auditorGroup.groupDesc}}
</td>
If assignmentAudit has multiple values and is not deterministic you can try
<td>
<span *ngFor="let assignmentAudit of nas.assignmentAudit">
{{assignmentAudit.auditorGroup.groupDesc}}
</span>
</td>
nas.assignmentAudit[0].auditorGroup.groupDesc
That would work. The section you are trying to access is an array. So picking the first index would then make it defined
groupDesc is insideauditorGroup loop so you cannot do like to get value inside loop. Try like that.
<!-- Angular -->
<ul>
<li *ngFor="let item of nas.assignmentAudit.auditorGroup;">
</li>
</ul>
or <td>{{nas.assignmentAudit.auditorGroup[0].groupDesc}}</td>

Exclude item on OrderBy in a table - AngularJS

I have a table sorted by properties "age" and "name" but I have a counter of rows (counterR) that shows the numbers of rows in the table.
I want to exclude this item on OrderBy because is ordered too, I need it would be static and always ordered but I canĀ“t.
There's the Plunker link of my problem: http://plnkr.co/edit/MJYayUANphxksbGkyEcj?p=preview
HTML:
<body ng-controller="MainCtrl">
<table border="0">
<thead>
<tr>
<th>#</th>
<th>ID</th>
<th ng-click="sortType = 'name';sortReverse=!sortReverse">NAME</th>
<th ng-click="sortType = 'age';sortReverse=!sortReverse">AGE</th>
</tr>
</thead>
<tbody>
<tr ng-init="counterR=incrementCounter()" ng-repeat="item in items | orderBy:sortType:sortReverse">
<td>{{counterR}}</td>
<td>{{item.id}}</td>
<td>{{item.name}}</td>
<td>{{item.age}}</td>
</tr>
</tbody>
</table>
JS:
$scope.items = [
{"name":"Jhon","id":"BB1","age":23},
{"name":"Austin","id":"BB2","age":44},
{"name":"Mark","id":"BB3","age":56},
{"name":"Jenn","id":"BB4","age":15}
];
var counterRow = 0;
$scope.incrementCounter = function(){
counterRow = counterRow + 1;
return counterRow;
}
Instead of storing the row number with the data - which doesn't work because the row numbers are not specific to a data row, but rather to a display position - you need to calculate the value "on the fly". In the scope of an ng-repeat you can do this with the special $index variable. That is, instead of
<td>{{counterR}}</td>
which reads a value from the data row, you would use
<td>{{$index+1}}</td>
(assuming sequential numbers starting from 1)

Display Object in HTML table ng-repeat - Angular

My Object looks like this
Object
207T : Object
metal : 1
steel : 2
205T : Object
metal : 1
steel : 3
208T : Object
metal : 1
steel : 3
209T : Object
metal : 0
steel : 9
Now this object i need to display in below format
207T, 205T, 208T, 209T should be in table heading which is fine
<tr>
<th></th>
<th></th>
<th ng-repeat="(key, value) in mainObj">{{key}}</th>
</tr>
And then the format should be
How to acheive this
We can not create table column wise so in this case best and cleanest way would be to filter out the row values and using them inside view. So
In controller
app.controller(['$scope', function($scope){
$scope.object= values;
$scope.valuesMetal= [];
$scope.valuessteel = [];
// initializing row values for use in the using in view
angular.forEach(values, function(value, key) {
$scope.valuesMetal.push(value.metal );
$scope.valuessteel.push(value.metal );
});
}]);
In the view we just display our values
<tr>
<th ng-repeat="(key, value) in mainObj">{{key}}</th>
</tr>
<tr>
<td >Metal</td>
<td ng-repeat="(key, value) in valuesMetal" >{{value}}</td>
</tr>
<tr>
<td >steel</td>
<td ng-repeat="(key, value) in valuessteel" >{{value}}</td>
</tr>
<div>
For the header you need to convert the object to array:
// inside controller
mainObjArray = Object.keys(mainObj); // ['207T', '205T', '208T', '209T']
// header html
<tr>
<th ng-repeat="key in mainObjArray">{{key}}</th>
</tr>
And for the rest of values you can do some like:
// inside controller
mainObjValues = mainObjArray.map(function(item){
return mainObj[item];
}); // output: [{metal: 1, steel: 2}, {...}]
Then the body-table:
<tbody>
<tr ng-repeat="item in mainObjValues">
<td>{{item.metal}}</td>
<td>{{item.steel}}</td>
....
</tr>
</tbody>
angular.module("app",[])
.controller("ctrl",function($scope){
var sampleObj = {
"207T":{
"metal":1,
"steel":2
},
"205T":{
"metal":1,
"steel":3
},
"208T":{
"metal":1,
"steel":3
},
"209T":{
"metal":0,
"steel":9
}
}
$scope.metal = [];
$scope.steel= []
$scope.keys = Object.keys(sampleObj);
angular.forEach(sampleObj, function(obj) {
$scope.metal.push(obj.metal );
$scope.steel.push(obj.steel );
});
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
<table>
<tr>
<th ng-repeat="item in keys">{{item}}</th>
</tr>
<tr>
<td >Metal</td>
<td ng-repeat="item in metal track by $index" >{{item}}</td>
</tr>
<tr>
<td >Steel</td>
<td ng-repeat="item in steel track by $index" >{{item}}</td>
</tr>
</table>
</div>
This is actually simple.
<table>
<tr>
<th></th>
<th ng-repeat="(key, value) in mainObj">{{key}}</th>
</tr>
Here you are ng repeating in th, will display 207t, 205T etc
<tr ng-repeat="item in items track by $index">
<td ng-repeat="item1 in item track by $index">{{item[$index]}}</td>
</tr>
Here you have to ng repeat tr, so that it will display metal and steel. and then each td u have to ng repeat and should display with index item[$index]

nested ng-repeat that one depend on the other

I have an issue with angular, I want to use two nested ng-repeat in a data table where the first get the data, and the second get the name of field to be retrieved from the data (retrieved in the first ng-repeat)
here is what I tried to do with code :
<table md-table flex-order-gt-sm >
<thead md-head>
<tr md-row >
//1st : get the name of fields, not a problem
<th md-column ng-repeat="field in fields">{{item.name}}</th>
</tr>
</thead>
<tbody md-body>
<tr md-row ng-repeat="item in items">
<td md-cell ng-repeat="field in fields" >
//here is the problem I want to get the item that it's field name is field
{{item.{{field}} }}</td>
</tr>
</tbody>
</table>
for example if fields contain :{'a','b','c'}
and items contains {'a':'1','b':'2','c':'3'};
I want for example for the 1st iteration of {{item.{{field}} }} to return 1
Use toString() to retrieve the scope data in an scope object.
<tr md-row ng-repeat="item in items">
<td md-cell ng-repeat="field in fields" >
{{item[field.toString()]}}
</td>
</tr>
Here is the plunker
As an alternative you could also use a filter:
<td md-cell ng-repeat="field in fields | filter: { name: 'field' }">
</td>
You need to get fields collection according to item of items collection after
getFieldsByItem(item)
Then you can get field from fields collection
{{field}}

Categories