Angular2: assign component field within #ngFor - html

I have such component:
#Component({
selector: "expenses-component",
templateUrl: "expenses.html"
})
export default class ExpensesComponent {
private expenses: [] = [{name: "Foo", amount: 100}, {name: "Bar", amount: 200}];
private totalAmount: number = 0;
}
and also such template:
<h2 class="sub-header">Total: {{total | currency:"USD":true}}</h2>
<div class="table-responsive">
<table class="table table-hover table-condensed">
<thead>
<tr>
<th>#</th>
<th>Name</th>
<th>Amount</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let expense of expenses | orderByDate; let id = index">
<td>{{id + 1}}</td>
<td>{{expense.name}}</td>
<td>{{expense.amount | currency:"USD":true}}</td>
</tr>
</tbody>
</table>
</div>
I need to count totalAmount, but i can't figure out how to do it without providing another one for loop in typescript code, so is there possibility to initialize total += expense.amount within the above #ngFor loop?

As far as I know, it is not possible. You can achieve it by creating custom pipe which will iterate through the array and return totalAmount. As the parameter you can pass the name of the key which you want to use as value.
For the example:
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({
name: 'totalAmount'
})
export class totalAmount implements PipeTransform {
transform(value:any, key:string):any {
if(value){
let total = 0;
for(let i in value){
total = total + value[i][key];
}
return total;
}
return value;
}}
And then you can call it from html:
<div>{{expenses | totalAmount:"amount"}}</div>

You could make totalAmount a get() method and return it there?
private get totalAmount() {
let total = 0;
for (let item in this.expenses) {
total += item.amount;
}
return total;
}

Related

Put json data into table

I actually want to create a table with the data who the api return to me.
The problem is that i can't print the data.
The IdLangage have his column in the table and i want to put the data of the traduction into the correct cell.
The JSON data format :
traductionData ={
"Data":
[
{
"Code": "BJR",
"TraductionsFormat":
[{
"Code": "BJR",
"Description": null,
"Id": 0,
"IdLangage": "FR",
"Traduction": "Bonjour"
},
{
"Code": "BJR",
"Description": null,
"Id": 0,
"IdLangage": "EN",
"Traduction": "Hello"
}]
},
] };
Here is my table where i want to print the data into :
<table>
<thead>
<tr>
<th width="25%">Code</th>
<th width="15%">FR</th>
<th width="15%">EN</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let traduction of traductionData">
<td>{{ traduction.TraductionsFormat.Code }}</td>
<td>{{ traduction.TraductionsFormat.Traduction}}</td>
</tr>
</tbody>
</table>
Here is my angular service :
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http'
import { map } from 'rxjs/operators';
import { environment } from 'src/environments/environment';
#Injectable({
providedIn: 'root'
})
export class ApiService {
localUrlAPI: string = environment.urlAPI;
constructor(private http : HttpClient) { }
getAllTraductions(){
return this.http.get<any>(this.localUrlAPI+"GetAllTraductionsGroupByCode")
.pipe(map((res:any)=>{
return res;
console.log(res);
}))
}
}
And here is my angular Component with my http request :
import { Component, OnInit } from '#angular/core';
import { ApiService } from 'src/app/services/api.service';
#Component({
selector: 'app-grid-edit-traductions',
templateUrl: './grid-edit-traductions.component.html',
styleUrls: ['./grid-edit-traductions.component.scss']
})
export class GridEditTraductionsComponent implements OnInit {
traductionData !: any[];
constructor(private api: ApiService) { }
ngOnInit(): void {
this.getLesTraductions();
}
getLesTraductions(){
this.api.getAllTraductions()
.subscribe(res=>{
this.traductionData = res.Data;
console.log(this.traductionData)
})
}
}
<table>
<thead>
<tr>
<th *ngFor="let column of tableHeaders">
{{column}}
</th>
</tr>
</thead>
<tbody>
<tr ng *ngFor="let row of tableRows">
<td *ngFor="let column of tableHeaders">
{{row[column]}}
<ng-container *ngFor="let trad of row.TraductionsFormat, let j = index">
<span *ngIf="row.TraductionsFormat[j].IdLangage === column">
{{row.TraductionsFormat[j].Traduction}}
</span>
</ng-container>
</td>
</tr>
</tbody>
</table>
Here's the ts:
tableRows: Array<any> = [];
tableHeaders: Array<any> = [];
ngOnInit(): void {
//---- TABLE HEADERS -----
this.tableHeaders.push("Code")
this.traductionData.Data.forEach(el => {
el.TraductionsFormat.map(c => c.IdLangage).forEach(lang => {
this.tableHeaders.push(lang);
})
});
this.tableHeaders = [...new Set(this.tableHeaders)];
//---- TABLE ROWS -----
this.traductionData.Data.forEach(el => {
this.tableRows.push(el)
});
}
Stackblitz example
The JSON data you've provided is wrong, there are missing commas and brackets. Although, I'm pretty sure that the reason the data isn't shown in table is that the "TraductionsFormat" is an array. If you want to get an item from array you have to provide an index.
<tr *ngFor="let traduction of traductionData">
<td>{{ traduction.TraductionsFormat[0].Code }}</td>
<td>{{ traduction.TraductionsFormat[0].Traduction}}</td>
</tr>
Above is just simple solution. You might want to use dynamic indexes.

How to bind Json response to table

I have products.components.ts class, where I am getting Json data in this.Getdata
ngOnInit() {
this._service.getProducts(this.baseUrl)
.subscribe(data => {
this.Getdata=data
this.products=data
alert(JSON.stringify(this.Getdata));
});
This Josn data I want to bind in products.components.html class Table
<p>
Product List
</p>
<table>
<th>Id</th> <th>Name</th> <th> Country</th> <th>Actions</th>
<tr *ngFor="let lst of products; let i = index" border="1">
<td>{{i+1}}</td><td>{{lst.id}}</td><td>{{lst.employee_name}}</td> <td>Edit</td>
</tr>
</table>
The above code is not working. Only alert displaying. How can I bind data to table?
This is my Json data
[{"id":"1","employee_name":"amit","employee_salary":"0","employee_age":"0","profile_image":""},{"id":"247793","employee_name":"Ana","employee_salary":"123","employee_age":"123","profile_image":""},{"id":"247856","employee_name":"Joseph Beltran","employee_salary":"1000","employee_age":"23","profile_image":""},{"id":"247982","employee_name":"testyeyyeye1","employee_salary":"123","employee_age":"23","profile_image":""},{"id":"248007","employee_name":"test100","employee_salary":"123","employee_age":"23","profile_image":""},{"id":"248038","employee_name":"Hendry","employee_salary":"61888","employee_age":"26","profile_image":""}]
Model class
export class Productlist {
id: string;
employee_name: string;
employee_salary: string;
employee_age: string;
profile_image: string;
}
Instead of subscribing to the Observable, consider storing it in a property and then unwrapping it in the template using the async pipe.
If you consider that, then you can significantly reduce your Component to this:
import { Component } from '#angular/core';
import { Observable } from 'rxjs';
import { EmployeeService } from './employee.service';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
employees$: Observable<Array<any>> = this.employeeService.getEmployees();
constructor(private employeeService: EmployeeService) {}
}
And in your Template:
<p>
Employee List
</p>
<table border="1">
<thead>
<th>Id</th>
<th>Name</th>
<th> Country</th>
<th>Actions</th>
</thead>
<tbody>
<tr *ngFor="let employee of employees$ | async; let i = index">
<td>{{i+1}}</td>
<td>{{employee.id}}</td>
<td>{{employee.employee_name}}</td>
<td>Edit</td>
</tr>
</tbody>
</table>
Here's a Working Sample Demo Code for your ref.
PS: Consider naming your properties and methods appropriately. If you're working with employees, then it doesn't really make sense to name properties and methods as products.
You can also use
const myobject= JSON.parse(yourjsonvalue);
You will get the object in myobject and now you can loop through with simple ngFor.
I got it this way
export class ProductsComponent implements OnInit {
public Getdata;
products:Productlist[];
constructor(private employeeService: ProductService) {}
ngOnInit() {
this.employeeService.getProducts(this.baseUrl)
.subscribe((data:any) => {
this.products=data;
});
}
}

How to get a value from an Object that comes from a ManyToOne relationship in Angular CLI?

I'm using Spring Boot, Angular CLI and mySQL.
I have an Employee than can have one Marital Status, and one M Status can be in N Employees.
In localHost:8080 I get the right array json:
[{"id":1,"firstName":"Name","lastName":"surname1","emailAddress":"test#test1.com","status":{"statusId":1,"nameStatus":"Single"}
In my angular table(localHost:4200), instead I get every data but in Status column I get "[object Object]".
In have a service for each one.
When I do a registration I have a dropDown w/ all status so I get them.
This is my HTML table:
<table class="table table-bordered">
<thead>
<tr>
<th *ngFor="let col of columns">{{col}}
</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let employee of employees | paginate: {itemsPerPage: pageSize,
currentPage: page,
totalItems: employees.length} | filterAll: searchString : field">
<td *ngFor="let col of columns">{{employee[col]}}</td>
<td>
<button [ngClass]="getClassCondition(act.actionType)" *ngFor="let act of actions"
(click)="actionFunc(act, employee)">{{act.label}}</button>
</td>
</tr>
</tbody>
</table>
Here I have an ngFor that gets All employees.
Now I share also my services and my componets.ts:
status.service.ts:
#Injectable({
providedIn: 'root'
})
export class StatusService {
private baseUrl = 'http://localhost:8080/api';
constructor(
private http: HttpClient) { }
getStatus(): Observable<Status[]> {
return this.http.get<Status[]>(`${this.baseUrl}` + '/status');
}
}
employee.service.ts
const httpOptions = {
headers: new HttpHeaders({'Content-Type': 'application/json'})
};
#Injectable({
providedIn: 'root'
})
export class EmployeeService {
columns = COLUMNS;
actions = ACTIONS;
private baseUrl = 'http://localhost:8080/api/employees';
constructor(private http: HttpClient) {
}
getColumns() {
return this.columns;
}
getActions() {
return this.actions;
}
getMetadata() {
return this.metadata;
}
/** GET Employees from the server */
getEmployees(): Observable<Employee[]> {
return this.http.get<Employee[]>(this.baseUrl);
}
getEmployee(id: number): Observable<Employee> {
const url = this.baseUrl + '/' + id;
return this.http.get<Employee>(url);
}
/** PUT: update the employee on the server */
updateEmployee(employee: Employee): Observable<any> {
return this.http.put(`${this.baseUrl}/${employee.id}`, employee, httpOptions);
}
deleteEmployee(id: number): Observable<any> {
return this.http.delete(`${this.baseUrl}/${id}`, {responseType: 'text'});
}
}
Here I have also a const w/ COLUMNS name.
employee.ts
export const COLUMNS = ['id', 'firstName', 'lastName', 'emailAddress', 'status'];
export class Employee {
id: number;
firstName: string;
lastName: string;
emailAddress: string;
status: string;
}
status.ts
export class Status {
statusId: number;
nameStatus: string;
}
What do I have to do to get my status.Name?
Is there something specific?
If you need more documentation ask me.
I suspect that col return status and employee[col] return {"statusId":1,"nameStatus":"Single"}.
So the error seems correct.
You can do a workaround here if you just want to display the nameStatus :
<td *ngFor="let col of columns">{{employee[col]?.nameStatus ? employee[col]?.nameStatus : employee[col]}}</td>
You have to be more specific, how you display the data or map the data to the structure you want
1) instead of <td *ngFor="let col of columns">{{employee[col]}}</td>
you can do
<td>{{employee.firstName}}</td>
<td>{{employee.lastName}}</td>
...
<td>{{employee.status.nameStatus}}</td>
2) before binding the data, map it to the structure you want (in your controller/*.ts file).
Take a look at Array map( for that.
Your code doesn't seem to work because the controller never assigns the employee method

Angular dynamic table using ngFor

I would like to know if it is possible to create a dynamic HTML table from JSON data. The amount of columns and headers should change according to the keys in the JSON. For example this JSON should create this table:
{
color: "green", code: "#JSH810"
}
,
{
color: "red", code: "#HF59LD"
}
...
And this JSON should create this table:
{
id: "1", type: "bus", make: "VW", color: "white"
}
,
{
id: "2", type: "taxi", make: "BMW", color: "blue"
}
...
This has to be 100% dynamic though, because I want to display hundreds of different JSON objects, so nothing should be hard coded in the HTML page.
If you want to get the key of your object as the head of your table, you should create a custom pipe.
import { PipeTransform, Pipe } from '#angular/core';
#Pipe({name: 'keys'})
export class KeysPipe implements PipeTransform {
transform(value, args:string[]) : any {
let keys = [];
for (let key in value) {
keys.push(key);
}
return keys;
}
}
Update: Or simply return keys using Object.keys().
(demo)
#Pipe({name: 'keys'})
export class KeysPipe implements PipeTransform {
transform(value, args:string[]) : any {
return Object.keys(value);
}
}
Now into your html template:
<table>
<thead>
<tr>
<th *ngFor="let head of items[0] | keys">{{head}}</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let item of items">
<td *ngFor="let list of item | keys">{{item[list]}}</td>
</tr>
</tbody>
</table>
Update: Here is the demo.
This can help:
export class AppComponent {
//Array of any value
jsonData:any = [
{id: "1", type: "bus", make: "VW", color: "white"},
{id: "2", type: "taxi", make: "BMW", color: "blue"}
];
_object = Object;
}
<div>
<table border="1">
<thead>
<tr>
<th *ngFor="let header of _object.keys(jsonData[0]); let i = index">{{header}}</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let row of jsonData; let i = index">
<th *ngFor="let objKey of _object.keys(row); let j = index">{{ row[objKey] }} </th>
</tr>
</tbody>
</table>
</div>

How to filter JSON object depends upon key name in angular 2?

In my angular 2 application Headers are dynamically setting up. I arranged my JSON data as shown in attached image. I want to filter entire data using the Table Heading as a search term (Ex: Name or Position or Extn).
I am separating my result data to keys and values like:
this.keys= Object.keys(this.ResultData_search[0])` // my table heading false here
in my template :
<table>
<tr>
<th *ngFor=" let key of keys,let i=index"></th>
</tr>
<tr *ngFor=" let res of ResultData_search ,let i = index" style="height: 35px;">
<td>{{i+1}}</td>
<td *ngFor=" let key of keys ">
{{res[key]}}
</td>
</tr>
</table
Why I am doing this is because my JSON data is not static. I have to set table headings and data dynamically.
You have to use a Pipe with an Array parameter containing the filters for each header.
In the Pipe you can dynamically filter your data for each header.
Let's assume you have the following component with a similar data structure as yours. It has a filters Array of type IFilter :
import { Component, OnInit, ChangeDetectionStrategy } from '#angular/core';
#Component({
selector: 'app-filter',
templateUrl: './filter.component.html',
styleUrls: ['./filter.component.css'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class FilterComponent implements OnInit {
data: Array<Map<string, string>>;
headers: Array<string>;
filters: Array<Ifilter>;
constructor() {
}
ngOnInit() {
this.data = new Array(
new Map([["header1", "value11"], ["header2", "value12"], ["header3", "value13"], ["header4", "value14"], ["header5", "value15"]]),
new Map([["header1", "value21"], ["header2", "value22"], ["header3", "value23"], ["header4", "value24"], ["header5", "value25"]]),
new Map([["header1", "value31"], ["header2", "value32"], ["header3", "value33"], ["header4", "value34"], ["header5", "value35"]])
);
this.headers = Array.from((this.data[0]).keys());
this.filters = new Array({header:"header1",filter:""},{header:"header2",filter:""},{header:"header3",filter:""},{header:"header4",filter:""},{header:"header5",filter:""})
}
}
export interface Ifilter{
header : string;
filter : string;
}
Then define the following Pipe and filter for each header.
import { Pipe, PipeTransform } from '#angular/core';
import { Ifilter } from "app/filter/filter.component";
#Pipe({
name: 'pipeFilter',
pure: false
})
export class FilterPipe implements PipeTransform {
transform(data: Array<Map<string, string>>, filters: Array<Ifilter>): Array<Map<string, string>> {
let filteredData = Array<Map<string, string>>();
for (let row of data) {
let exclude: boolean = false;
for (let filter of filters) {
if (filter.filter != '' && row.get(filter.header).indexOf(filter.filter) == -1) {
exclude = true;
break;
}
}
if (!exclude)
filteredData.push(row);
}
return filteredData;
}
}
Finally, display the table with this template.
Get the filters with a two-way binding [(NgModel)] and pass them as parameter to the Pipe on the *ngFor : *ngFor="let rows of data | pipeFilter:filters". :
<table *ngIf="data && data.length">
<th *ngFor="let header of headers">
{{header}}
</th>
<tr>
<td *ngFor="let filter of filters;let i=index">
<input type='text' [(ngModel)]='(filters[i]).filter' />
</td>
</tr>
<tr *ngFor="let rows of data | pipeFilter:filters">
<td *ngFor="let header of headers ">
{{rows.get(header)}}
</td>
</tr>
</table>
Both Template and Pipe are completely dynamic and can handle any header.
Regard,
Philippe