How could I use ngFor to design this (maybe nested) table - javascript

I have an Object Array that has a key and then has an array for it's value. Something like this:
[
{key: 'example#example.com', value: ['message1', 'message2']}
]
I previously had this working using an object like this
[
{key: 'example#example.com', value: 'message'}
]
but I would like to change the functionality some so I switched to the value being an array instead.
I want to create a table that looks something like this
| User Email | Result |
| example#example.com | message1 |
| | message2 |
| example1#example.com | message1 |
| | message2 |
It can be a nested table or it can just show the two messages in the table cell. I am not too picky.
I tried nesting the table, but that did not work. I am not sure how I can use a second ngFor to do something like this.
This is my html that works with the Object Array that does not have an array for its value
<div class="container" style="text-align:center">
<br />
<h1>Results</h1>
<head>
<style>
table,
th,
td {
border: 1px solid black;
align: center;
}
</style>
</head>
<body>
<table style="width:50%" align="center">
<tr>
<th *ngFor="let col of displayedColumns">
{{ col }}
</th>
</tr>
<tr *ngFor="let item of userResults | keyvalue">
<td>{{ item.key }}</td>
<td>{{ item.value }}</td>
</tr>
<tr></tr>
</table>
</body>
I tried changing the item.value line with another table and another ngFor, but nothing printed.
any ideas would be appreciated

You're very close; you should just need another *ngFor directive inside your second tag, like this...
<td>
<span *ngFor="let val of item.value" style="display:inline-block">
{{ item.value }}
<span>
</td>
So your final table looks like...
<div class="container" style="text-align:center">
<br />
<h1>Results</h1>
<head>
<style>
table,
th,
td {
border: 1px solid black;
align: center;
}
</style>
</head>
<body>
<table style="width:50%" align="center">
<tr>
<th *ngFor="let col of displayedColumns">
{{ col }}
</th>
</tr>
<tr *ngFor="let item of userResults | keyvalue">
<td>{{ item.key }}</td>
<td>
<span *ngFor="let val of item.value" style="display:inline-block">
{{ item.value }}
<span>
</td>
</tr>
<tr></tr>
</table>
</body>

You do not need to use the keyvalue pipe. The keyvalue pipe is used to transform an object into an array of key value pairs so using this will give you incorrect values in your table. On the other hand, you have a normal array of objects with one property that contains an array. This format is perfect for a nested ngFor.
Change your table to the code below
<table style="width:50%" align="center">
<tr>
<th *ngFor="let col of displayedColumns">
{{ col }}
</th>
</tr>
<tr *ngFor="let item of userResults">
<td>{{ item.key }}</td>
<td>
<div *ngFor="let value of item.value">
{{ value }}
</div>
</td>
</tr>
<tr>
</table>
In your css add a vertical-align to keep the data in the cell on top.
table,
th,
td {
border: 1px solid black;
align: center;
vertical-align: top;
}
Here is a working example on stackblitz
Edit: As requested in the comments, to add html content, use innerHTML on the div like below
<div *ngFor="let value of item.value" [innerHTML]="value">
</div>

Related

Having Tables inside NgFor with same height

I have an ngFor which will iterate number of tables for me,In my case 3(not static) tables will be generated with the ngFor i am using, and inside the table I have rows that are to be generated, using another ngFor, but now, the problem, if my first is having less amount of data, say 2 rows of data, and my third table is having 6 rows of data, then the height of tables isn't constant.
Expected Solution: All tables should be of equal height, if table 3 has 6 rows, then table 1 and table 2 should also have 6 rows, Lets keep the rows the static, i mean irrespective of data, each table should be able to show 6 rows. if table 2 has data for 2 rows then remaining 4 rows should be empty.
<div
class="shift-table"
[ngClass]="{ disablePreviousRecord: isPreviousWeekRecord }"
[attr.disabled]="isPreviousWeekRecord"
>
<div class="shift-name " *ngFor="let roster of rosterInfo">
<span>Shift - {{ roster.shiftName }}</span>
<table
class="table scrollbar"
id="customScrollDiv"
style="background-color: white; color:#EBF4FF"
>
<thead style="background-color: #9AA7C7;">
<tr class="header">
<th>
Copy
</th>
<th>
Person Name
</th>
<th>
Role
</th>
</tr>
</thead>
<tbody class="text-black-50">
<tr class="dataRow" *ngFor="let user of roster.userDetails">
<td>
<input type="checkbox" [disabled]="isPreviousWeekRecord" />
</td>
<td>
{{ user.userId }}
</td>
<td>
<span *ngFor="let role of user.roles; let i = index">
{{ role }} {{ i === user.roles.length - 1 ? '' : ',' }}
</span>
</td>
</tr>
</tbody>
</table>

object, Object displaying in nested *ngFor in angular with dynamic headers

I want to display a list of object which have dynamic number of keys in angular inside a table, but it always shows [object,Object] in the output, I have tried multiple options from stack overflow but none of them worked. My data looks like this.
ComparableCompaniesRatiosData = [{2008: 151260,2009: 121886,2010: 122542,2011: 198630,2012: 450840,2013: 537416,2014: 673084,ComparableParty: "CompanyName",FYE: "31-12"},{2008: 151260,2009: 121886,2010: 122542,2011: 198630,2012: 450840,2013: 537416,2014: 673084,ComparableParty: "companyName",FYE: "31-12"}]
//these table headers are dynamic
ComparablePartyHeadersTrimmed = ["ComparableParty", "FYE", "2008", "2009", "2010", "2011", "2012", "2013", "2014"]
I have tried the following to display the list:
1 : I tried with classic for loop.
<tbody>
<ng-container *ngIf="showRatiosData">
<tr *ngFor="let item of [].constructor(ComparableCompaniesRatiosData.length); let item = index" data-toggle="modal">
<td *ngFor="let value of [].constructor(ComparablePartyHeadersTrimmed.length); let i = index" >
<span>{{ComparableCompaniesRatiosData[item][i]}}</span>
</td>
</tr>
</ng-container>
</tbody>
2 : with KeyValue Pipe.
<ng-container *ngIf="showRatiosData">
<tr *ngFor="let row of ComparableCompaniesRatiosData" data-toggle="modal">
<td *ngFor="let item of row | keyvalue">
<span>{{item.value}}</span>
</td>
</tr>
</ng-container>
3 : And with simple angular *ngFor as well.
<ng-container *ngIf="showRatiosData">
<tr *ngFor="let item of ComparableCompaniesRatiosData" data-toggle="modal">
<td *ngFor="let row of ComparablePartyHeadersTrimmed" >
<span>{{item[row]}}</span>
</td>
</tr>
</ng-container>
Ok, I've added a new method getValue() which takes the item and property name and returns the value.
getValue(item, property): any {
return item[property];
}
Then in the HTML, first loop through the data, then through the headers, and call the getValue() with the row and the header name.
<table class="table" >
<thead>
<tr>
<th *ngFor="let header of ComparablePartyHeadersTrimmed" >{{header}}</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let item of ComparableCompaniesRatiosData">
<td *ngFor="let property of ComparablePartyHeadersTrimmed" >
{{getValue(item,property)}}
</td>
</tr>
</tbody>
</table>
Working example: https://stackblitz.com/edit/angular-dynamic-table-columns-example

Html/Angular toggle table rows to show hidden a table

I am completely new to angular, I need to create a table. The data array is as-follows:-
data = [{rollno: 1,name: 'abc',subject: 'maths'},
{rollno: 4,name: 'xyz',subject: 'history'},
{rollno: 2,name: 'pqr',subject: 'history'}
];
I want to create a table with some summary rows based on this data and then when I click the expand button the sub-rows should appear beneath that summary-row indicating the actual data.
For example:-
Expand/Collapse | No of Students | Subject
click here 1 Maths
click here 2 History
When I toggle the expand/collapse button on the second row for example I want actual rows to appear like this beneath it:-
Expand/Collapse | No of Students | Subject
click here 1 Maths
click here 2 History
RollNo | StudentName
4 xyz
2 pqr
How Can I achieve this?
1) Grouping the data by subject
First you need to group the data by subject and then count the items in each group.
You can use the angular.filter module's groupBy filter to do this.
1a) Add a dependency on that module as follows:
var app = angular.module("yourModuleName", ["angular.filter"]);
1b) You can then use the groupBy filter in an ng-repeat directive on a <tbody> tag like this:
<tbody ng-repeat="(key, value) in data | groupBy: 'subject'">
1c) You're now dealing with the data in the format below. This is an object of key/value pairs where "maths" and "history" are both keys, and the arrays are the values
{
"maths": [
{
"rollno": 1,
"name": "abc",
"subject": "maths",
}
],
"history": [
{
"rollno": 4,
"name": "xyz",
"subject": "history",
},
{
"rollno": 2,
"name": "pqr",
"subject": "history",
}
]
}
2) Displaying the grouped data and counting the items in each group
Use key and value to display the grouped data in a table as follows:
<table>
<thead>
<tr>
<th>Subject</th>
<th>Number of Students</th>
<th>Expand/Collapse</th>
</tr>
</thead>
<tbody ng-repeat="(key, value) in data | groupBy: 'subject'">
<tr>
<td>{{ key }}</td>
<td>{{ value.length }}</td>
<td>
<button>
Expand/Collapse
</button>
</td>
</tr>
<tr>
<td colspan="3">
<table>
<thead>
<tr>
<th>Roll Number</th>
<th>Name</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="student in value">
<td>{{ student.rollno }}</td>
<td>{{ student.name }}</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
Note the extra <tr> and nested table with another ng-repeat for displaying the student data. Currently all nested student data will display, the next step is to conditionally show/hide the nested tables based on which expand/collapse button was clicked.
3) Showing/Hiding the nested student data
3a) Add an ng-click directive on the button so that it passes in the key to an onExpandClicked function on your controller:
<button ng-click="onExpandClicked(key)">
Expand/Collapse
</button>
3b) Create the onExpandClicked function in your controller:
$scope.onExpandClicked = function(name){
$scope.expanded = ($scope.expanded !== name) ? name : "";
}
This sets a value on the $scope that can be used in the view to decide whether to show/hide a section of student data. The key is passed into the function as the name parameter and $scope.expanded will either be set to name or reset to "" depending on whether the passed in name is the same as the current $scope.expanded value or not.
3c) Finally, use the $scope.expanded variable in an ng-if directive on the second <tr> tag of <tbody> to show or hide the nested student data:
<table>
<thead>
<tr>
<!-- Omitted for brevity -->
</tr>
</thead>
<tbody ng-repeat="(key, value) in data | groupBy: 'subject'">
<tr>
<!-- Omitted for brevity -->
</tr>
<tr ng-if="expanded === key">
<!--
Omitted for brevity
-->
</tr>
</tbody>
</table>
Demo
CodePen: How to show/hide grouped data created by the angular.filter module's groupBy filter
Design a table with html and iterate through your data object with ng-repeat loop to display the data.
ngRepeat
W3Schools has some basic examples on how to display tables with AngularJS
Angular Tables
First you should replace the actual table by a div structure, because it is not possible to mix two kinds of table like you are planning (when I get you right).
You could toggle every row with a ng-click with the corresponding expanded content like this (pseudo code, I hope the idea gets clear):
<div class="row-header">
<span>RollNo</span>
<span>StudentName</span>
</div>
<div class="row-content" ng-if="!row_4_expanded" ng-click="row_4_expanded = !row_4_expanded">
<span>4</span>
<span>xyz</span>
</div>
<div ng-if="row_4_expanded">
<div class="row-expanded-header">
<span>No of Students</span>
<span>Subject</span>
</div>
<div class="row-expanded-content">
<span>1</span>
<span>Math</span>
</div>
<div class="row-expanded-content">
<span>2</span>
<span>History</span>
</div>
</div>
<div class="row-content" ng-if="!row_2_expanded" ng-if="row_2_expanded" ng-click="row_2_expanded = !row_2_expanded">
<span>2</span>
<span>pqr</span>
</div>
<div ng-if="row_2_expanded">
<div class="row-expanded-header">
<span>No of Students</span>
<span>Subject</span>
</div>
<div class="row-expanded-content">
<span>1</span>
<span>Math</span>
</div>
<div class="row-expanded-content">
<span>2</span>
<span>History</span>
</div>
</div>
When you now click on a row, it toggle presens with the corresponding expanded one.
Here is a working example which resolves your problem.
var indexCtrl = ['$scope', function($scope){
$scope.num = 0;
$scope.test = [{rollno: 1,name: 'abc',subject: 'maths'},
{rollno: 4,name: 'xyz',subject: 'history'},
{rollno: 2,name: 'pqr',subject: 'history'}
];
$scope.changeShow = function(index){
$scope.num = index;
};
}];
<!DOCTYPE html>
<html ng-app>
<head>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.min.js"></script>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body ng-controller='indexCtrl'>
<table>
<tr>
<td>Expand/Collapse</td>
<td>No of Students</td>
<td>Subject</td>
</tr>
<tr ng-repeat='i in test' ng-class="num == $index ? red : none">{{}}
<td ng-click='changeShow($index)'>click here</td>
<td>{{$index +1}}</td>
<td >{{i.subject}}</td>
</tr>
</table>
<table>
<tr>
<td>RollNo</td>
<td>StudentName</td>
</tr>
<tr ng-repeat='i in test'>
<td ng-show='num == $index'>{{i.rollno}}</td>
<td ng-show='num == $index'>{{i.name}}</td>
</tr>
</table>
<style>
table tr td{
border: 1px solid black
}
</style>
</body>
</html>
Hope it helps you.

How to create table as generic component in Angular 2?

I have this table below. This kind of table can be found anywhere in my project, with different number of columns, with-or-without header column. That's why I would like to create a generic component, more simple, and have his own CSS.
<table class="table">
<thead>
<tr>
<th style="width: 100px"></th>
<th> {{ 'nameColumn_result_name' | translate }} </th>
<th> {{ 'nameColumn_result_date' | translate }}
</tr>
</thead>
<tbody #lines>
<tr #lineSelected *ngFor="let result of results"
(click)="appendLine($event, lineSelected, result)"
(contextmenu)="multipleSelection($event)">
<td (click)="selectUnitaryLine($event, result)" class="arrowDown"></td>
<td>{{result.name}}</td>
<td>{{result.date}}</td>
</tr>
<tr *ngIf="result && result .length == 0" >
<td colspan="10" class="text-center">{{ 'no_result _found' | translate }}</td>
</tr>
</tbody>
</table>
I would like something like this :
<CustomTable [data]="results" (click)="appendLine($event, lineSelected, result)" (contextmenu)="multipleSelection($event)">
<CustomTableColumn style="width: 100px" (click)="selectUnitaryLine($event, result)"></CustomTableColumn>
<CustomTableColumn [data]="name"> {{ 'nameColumn_result_name' | translate }} </CustomTableColumn>
<CustomTableColumn [data]="date"> {{ 'nameColumn_result_date' | translate }}</CustomTableColumn>
</CustomTable>
The idea is that CustomTable generate table, thead, tbody and tr and CustomTableColumn generate td. The events (click) are not generic. It can be add by the page which use this table.
So I create my component:
customtable.component.ts
#Component({
selector: 'CustomTable',
templateUrl: './customtable.component.html',
styleUrls: ['./customtable.component.css']
})
(...)
I don't know how to do that?
Can you help me please?
Thanks

How Do I Render Table With Meteor Spacebars Template and Row Number For Each Row?

How can I render a table with a meteor spacebars {{#each}} in a template and for each row add row number in first column like below:
| | title header | content header |
| 1 | first title | first content |
| 2 | second title | second content |
| 3 | third title | third content |
This is my each code now:
<table class="table table-bordered table-hover">
<thead>
<tr>
<th></th>
<th>title</th>
<th>content</th>
</tr>
</thead>
<tbody>
{{#each posts}}
<tr>
<td>
*** need index for each row in this column ***
</td>
<td>
{{ title }}
</td>
<td>
{{ content }}
</td>
</tr>
{{/each}}
</tbody>
</table>
Is there a way to get the array index within the {{#each}} or define extra property named index myself and index++ in each iteration?
In your helper that returns the cursor of values, instead of just doing a collection.find() do a collection.find().fetch() This will give you you an array of objects instead of a cursor. Then simply .forEach() through that an add an index key! Or you could just use collection.map() and add the key directly to each element as you map the cursor into an array. Docs

Categories

Resources