Need to display a text area for displaying the details, once the details button is clicked.
It gives an output similar to this table structure
<table>
<th>ID</th>
<th>Title</th>
<tr *ngFor = "let row of indexes">
<td *ngFor = "let id of row>{{id}}</td>
<td><button (click)="showDetails()">DETAILS</td>
</tr>
</table>
<tr *ngIf="isClicked"><td>{{id.details}}</td></tr> ---> This needs to be displayed very next to the row where the button is clicked.
In angular 1.x we can achieve this using ng-repeat-start/ng-repeat-end. In angular 2 I have an clue of including </template ngFor let-row [ngForOf]="indexes">But not sure where to include this. Please suggest.
If i understand your problem correctly, you want to insert two table-rows for each of your indexes.
So if you have something like this in your component class:
rows: any[] = [
{ detailsVisible: false },
{ detailsVisible: false },
{ detailsVisible: false },
{ detailsVisible: false },
{ detailsVisible: false }
];
toggleDetails(row: any) {
row.detailsVisible = !row.detailsVisible;
}
You can do it by using ng-container:
<table>
<ng-container *ngFor="let row of rows">
<tr>
<td (click)="toggleDetails(row)">show details</td>
</tr>
<tr *ngIf="row.detailsVisible">
<td>only visible after click on details cell</td>
</tr>
</ng-container>
</table>
Hope this helps.
Related
Could some one tell me how to iterate below data structure. ii'm trying to load in normal bootstrap table. but failed. so far i tried to for..loop inside an for of loop. but it returns undefined on 'tableData'
i know its silly and duplicate question. but i don't find solution.
{
"parent":{
"tableHeading":{
"Header1":"ASD",
"Header2":"QQQ",
"Header3":"YYY",
"Header4":"OOO",
},
"tableData":[
{
"Sample_1":"2019-08-19T00:00:00",
"Sample_2":"Johney",
"Sample_3":"30",
"Sample_4":"PA,
},
{
"Sample_1":"2019-08-19T00:00:00",
"Sample_2":"Allen P",
"Sample_3":"29",
"Sample_4":"SA",
},
{
"Sample_1":"2019-08-19T00:00:00",
"Sample_2":"Chris",
"Sample_3":"28",
"Sample_4":"MM",
}
]
}
}
Try like this:
<table border="1">
<tr>
<td *ngFor="let head of data.parent.tableHeading |keyvalue">
{{head.value}}
</td>
</tr>
<tr *ngFor="let item of data.parent.tableData">
<td *ngFor="let element of item | keyvalue">
{{element.value}}
</td>
</tr>
</table>
Working Demo
I have a table in which each row has a delete button. on click of the button the data gets deleted. However to verify that record is deleted, I have to refresh the page. I want to hide the current row on click of the delete button. Here is my code.
<table>
<tr>
<th>Delete</th>
<th>Id</th>
<th>Name</th>
</tr>
<tr *ngFor="let person of people" *ngIf="!hideRow">
<td><button (click)="delete(person.id)" title="Delete">Delete</button></td>
<td>person.Id</td>
<td>person.Name</td>
</tr>
</table>
and in my component.ts On delete I change the value of hideRow
delete(id) {
this.hideTr = true;
this.personService.delete(id).subscribe(p=> console.log(p));
}
hideRow is a boolean variable with default value of false. The problem is that when I click on delete, all the rows become hidden(of course). How can I refer just to the current row?
When you want to delete the row then you should delete it in actual instead of hide row. No need of *ngIf="!hideRow". You no need to refresh the page, this is beauty of AngularJS. Below is code to delete particular row. Pass $index of the row:
HTML code:
<table>
<tr>
<th>Delete</th>
<th>Id</th>
<th>Name</th>
</tr>
<tr *ngFor="let person of people">
<td><button (click)="delete($index)" title="Delete">Delete</button></td>
<td>person.Id</td>
<td>person.Name</td>
</tr>
</table>
JavaScript Code:
// delete row
$scope.delete = function(index) {
$scope.people.splice(index, 1);
};
Simple yet more effective :
Template Side :
<tr *ngFor="let person of people" *ngIf="!person?.hideRow">
<td><button (click)="delete(person)" title="Delete">Delete</button></td>
<td>person.Id</td>
<td>person.Name</td>
</tr>
Component Side :
delete(person) {
person.hideRow = true;
this.personService.delete(person.id).subscribe(p=> console.log(p));
}
Without changing user's (property) interface
Template Side :
<tr *ngFor="let person of people;let i = index;">
<td><button (click)="delete(i , person.id)" title="Delete">Delete</button></td>
<td>person.Id</td>
<td>person.Name</td>
</tr>
Component Side :
delete(index , id) {
this.people.splice(index, 1);
this.personService.delete(id).subscribe(p=> console.log(p));
}
Based from the code you provided, I would remove this part *ngIf="!hideRow" and add this to your component
delete(id) {
this.personService.delete(id).subscribe(p=> {
console.log(p);
this.people.filter( person => person.id !== id)
// or you can use splice by using the index
});
}
Now your html is simpler and no need to use *ngIf
<table>
<tr>
<th>Delete</th>
<th>Id</th>
<th>Name</th>
</tr>
<tr *ngFor="let person of people">
<td><button (click)="delete(person.id)" title="Delete">Delete</button></td>
<td>person.Id</td>
<td>person.Name</td>
</tr>
</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.
I am receiving an object like this
[
{
"BET": 57630343,
"CUSTOMER": 181645,
"SPORT": "MLB",
"XX_FILL OPEN": "<button class=\"btn\" onclick=\"fillOpen(57630343)\">Fill Open</button>",
"XX_VIEW": null,
"XX_CANCEL": "<input type=\"checkbox\" name=\"sports\" value=\"soccer\" onchange=\"fillOpen(57630343)\"/>"
},...]
I am rendering that object in the DOM, but for now, looks like in the picture below
here is the HTML part for the dynamic table
<table>
<thead>
<tr>
<th ng-repeat="column in cols" ng-init="isXX = column.indexOf('XX') === 0">
<span ng-if="isXX">{{column.substring(3).replace('_', ' ')}}</span>
<span ng-if="!isXX">{{column}}</span>
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="row in rows">
<td ng-repeat="column in cols">
<span>{{row[column]}}</span>
</td>
</tr>
</tbody>
</table>
and here is the Angular part
ReportsFactory.pendingBets(reportParam).then(function(data) {
if (data.length) {
gridInfo = _.forEach(data, function(item) {return item;});
$scope.rows = gridInfo;
$scope.cols = Object.keys($scope.rows[0]);
}
}
The Database guys are sending me that data as you see it in the json I pasted above.
what I need to know, is, what should I do in order to render those elements in the DOM ?
You can use ngBindHtml for that.
<tbody>
<tr ng-repeat="row in rows">
<td ng-repeat="column in cols" ng-bind-html="row[column]">
</td>
</tr>
</tbody>
Since you're getting the HTML from a $http call, you have to use $sce.trustAsHtml to correctly display this HTML on every single one of your row columns that require this:
$scope.rows.forEach(function(row) {
for (var key in row) {
if (key.indexOf('XX') === 0) {
var value = row[key];
if (value) {
row[key] = $sce.trustAsHtml(value);
}
}
}
});
Here's a working plunkr:http://plnkr.co/edit/7iFUhjg6Q0YIwRi47fDm?p=preview
You can do it by using ngSanitize.
Check it here. https://docs.angularjs.org/api/ngSanitize
Then, you can use ng-bind-html directive, which render HTML code to DOM element.
And, Javascript in the element can be applied with trustAsHtml method of $sce service.
I made an example here.
http://plnkr.co/edit/sTEUIWHwGXSAUSQ2rC8y?p=preview
I need to do this:
<tbody>
<tr class=“object.sibling[0]”>
<tr class=“object.sibling[1]”>
<tr class=“object.sibling[2]”>
<tr class=“object.sibling[2].child”>
<tr class=“object.sibling[2].child”>
<tr class=“object.sibling[3]”>
<tr class=“object.sibling[4]”>
however I am not sure how to keep track of two loops that are siblings. I can easily do this:
<tbody>
<tr class=“object.sibling[0]”>
<tr class=“object.sibling[1]”>
<tr class=“object.sibling[2]”>
<tr class=“object.sibling[3]”>
<tr class=“object.sibling[4]”>
<tr class=“object.sibling[2].child”>
<tr class=“object.sibling[2].child”>
but then the rows are out of order.
I found a solution that appears to work using ng-repeat-start and ng-repeat-end that visually does exctally what I want but the extra empty rows needed to end the hg-repeat start loops messes up the table when users copy paste.
<tbody>
<tr ng-repeat-start=“x in object.sibling”>
<td class=“x”>
<tr ng-repeat-start=“y in x.child”>
<td class=“Y”>
<tr ng-repeat-end=“”>
<tr ng-repeat-end=“”>
The problem is that the tr’s though they may represent children of siblings must all be on the same level as if that are all sibings. I cannot figure out how to do this with angular 1.2.15. How do I run two loops that keep track of each other that are not nested?
Hm, interesting scenario you've got. This should work:
<tr ng-repeat-start="sibling in siblings"></tr>
<tr ng-repeat-end ng-repeat="child in sibling.children"></tr>
The idea is to repeat two rows for each sibling, but the second row of each sibling is actually repeated for all of the sibling's children. So, in practice, the second row will only show up (and be repeated) if the sibling actually has children.
Here's a full example:
var app = angular.module('app', []);
app.controller('MainCtrl', function($scope) {
$scope.siblings = [
{
children: [
{}, {}, {}
]
},
{},
{
children: [
{}, {}
]
}
];
});
<div ng-app="app">
<table ng-controller="MainCtrl">
<tr ng-repeat-start="sibling in siblings">
<td>Sibling {{$index}}</td>
</tr>
<tr ng-repeat-end ng-repeat="child in sibling.children">
<td>Sibling child {{$index}}</td>
</tr>
</table>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.2/angular.min.js"></script>
Notice how there are no extra <tr> elements.