Unable to print a list in html using ngFor - html

I am new to angular and front end. I am trying to call a rest Api to fetch a product list. And then I am trying to print them in the html page.
product.component.html
<div class="page-title">Product List</div>
<div class="bgblue">
<div class="product-preview-container">
<ul *ngIf="!emptyProductList">
<p>Available Products</p>
<li *ngFor="let product of products">
<p>Product Name : {{ product.productName }}</p>
<p>Product Code : {{ product.productCode }}</p>
<p>Product Price : {{ product.productPrice }}</p>
</li>
</ul>
<ul *ngIf="emptyProductList">
<p>No Product Found </p>
</ul>
</div>
</div>
product.component.ts
export class ProductComponent implements OnInit {
errorMessage = '';
emptyProductList = true;
products: Product[];
constructor(private productService: ProductService) {
this.products = [];
}
ngOnInit() {
console.log('Called ngOnInit method');
this.productService.fetchProductList().subscribe(
(productList: Product[]) => {
this.products = productList;
if(this.products.length>0){
this.emptyProductList = false;
console.log('not empty');
}
}, err => {
this.errorMessage = err.error;
})
}
}
UPDATE
product.service.ts
export class ProductService {
constructor(private http: HttpClient, private adapter: ProductAdapter) { }
fetchProductList(): Observable<Product[]> {
return this.http.get(PRODUCT_LIST_API, httpOptions).pipe(
map((data: any[]) => [data].map((item) => this.adapter.adapt(item)))
);
}
}
product.model.ts
export class Product {
constructor(
public id: number,
public productCode: string,
public productName: string,
public productPrice: number
) {}
}
#Injectable({
providedIn: "root",
})
export class ProductAdapter implements Adapter<Product> {
adapt(item: any): Product {
return new Product(item.id, item.productCode, item.productName, item.productPrice);
}
}
Updating the productList values :
[Product]
0: Product
id: undefined
productCode: undefined
productName: undefined
productPrice: undefined
__proto__: Object
length: 1
Response json in Postman:
[
{
"id": 1,
"productCode": "P01",
"productName": "Pencil",
"productPrice": 10.0
}
],
Output :
Product List
Available Products
Product Name :
Product Code :
Product Price :
What am I doing wrong here?

The problem is in [data].map((item) => this.adapter.adapt(item))). Note that data is already array, so there is no need to [].
Try this one:
data.map((item) => this.adapter.adapt(item)))

Related

Observable data on Angular not displaying

I'm trying to pass the data of an Observable retrieved by API request to a component variable in order to display it but I just can't make it work. Please help, here is my code. Regards,
TypeScript Service : Request to API to get Observable
export class ServeurService {
serverCharacter = 'https://cegep.fdtt.space/v1/characters';
serverSecret = 'https://cegep.fdtt.space/v1/secret';
personnages: any[] = [];
persoName = '';
constructor(private http_client: HttpClient) { }
getPersonnage(): Observable<ICharactere[]> {
return this.http_client.get<ICharactere[]>(this.serverCharacter).pipe(retry(4));
}
getAllInfosById(id: string): Observable<ICharactere> {
const myUrl = 'https://cegep.fdtt.space/v1/character/' + id;
return this.http_client.get<ICharactere>(myUrl)?.pipe();
}
setPersonnageName(name: string) {
this.persoName = name;
}
getPersonnageName():string {
return this.persoName;
}
getPersonnages() {
this.http_client.get<any>('https://cegep.fdtt.space/v1/characters').subscribe({
next: (val) => {
val.data.forEach((element: { data: any; }) => {
this.personnages.push(element);
});
}
});
return this.personnages;
}
getPersonnageById(id: string) {
const persoSelectionne = this.getPersonnages().find((x: { id: string; }) => x.id === id);
return persoSelectionne;
}
getPersonnageIdByName(name: string) {
const persoSelectionne = this.getPersonnages().find((n: {name: string; }) => n.name === name);
console.log("perso name service", persoSelectionne)
return persoSelectionne.id;
}
TypeScript Component : Passing the Observable to a variable
export class StatsComponent implements OnInit {
myCharactere!: any;
statLookup = [
{ key: 'str', prefix: $localize`str`, suffix: $localize`enght`, couleur: 'bg-danger' },
{ key: 'dex', prefix: $localize`dex`, suffix: $localize`terity`, couleur: 'bg-primary' },
{ key: 'con', prefix: $localize`con`, suffix: $localize`stitution`, couleur: 'bg-warning' },
{ key: 'int', prefix: $localize`int`, suffix: $localize`elligence`, couleur: 'bg-success' },
{ key: 'sag', prefix: $localize`wis`, suffix: $localize`dom`, couleur: 'bg-info' },
{ key: 'cha', prefix: $localize`cha`, suffix: $localize`risma`, couleur: 'bg-dark' }
];
constructor(public myService: ServeurService) { }
ngOnInit(): void {
this.myService.getAllInfosById(this.myService.getPersonnageIdByName(this.myService.getPersonnageName())).subscribe(result => {
this.myCharactere = result
});
console.log("stats component perso", this.myCharactere)
}
getModifier(stat: number): string {
const mod = Math.floor((stat-10)/2)
return (mod<0)?'-':'+'+ mod.toString();
}
}
HTML : Displaying the variable
<div class="row text-center text-light bg-secondary mt-2 bg-transparent">
<div class="row mt-5">
<div class="col-2" *ngFor="let stat of statLookup">
<div class="{{stat.couleur}} mx-xxl-4 mx-2 mx-md-1 rounded-4">
<div class="fw-bold">{{stat.prefix}}<span class="d-none d-lg-inline">{{stat.suffix}}</span>
</div>
<div class="h2">
{{myCharactere && myCharactere.statistics && myCharactere.statistics[stat.key] ? getModifier(myCharactere.statistics[stat.key]) : ''}}
</div>
<div class="">
{{myCharactere?.data.statistics[stat.key]}}
</div>
</div>
</div>
</div>
If it helps, here is the model too :
export interface ICharactere {
error: string;
data: {
id: string;
name: string;
statistics: { [ key : string ]: number }
race: string;
player: string;
classe : string;
sousclasses: string;
level: number;
background: string;
synopsis: string;
image: string;
health: number;
currentHealth: number;
traits: {
trait: string;
description: string;
}[];
// Should be computed
armorClass: number;
initiative: number;
speed: number;
};
}
You have some fundamental concepts mixed up here. First, you're calling getPersonnages() synchronously and it is making an HTTP call which is an asynchronous operation. I understand what you're trying to do, but if you are going to use observables for your search result, then I suggest you make all of your function calls consistent that way. Here's an example:
getPersonnages(): Observable<any> {
return this.http.get<any>('https://cegep.fdtt.space/v1/characters');
}
getPersonnageIdByName(name: string) {
return new Observable(observer => {
this.getPersonnages().subscribe(
(results => {
observer.next(results.data.find(x => x.name === name))
observer.complete();
})
)
})
}
Now you can search for the ID value you want like this:
this.getPersonnageIdByName("Michel Michaud").subscribe(
(searchResult => {
// here I can get the id
const id = searchResult.id;
})
);

Send Angular API GET request value to a variable

I am trying to display the data of an object on Angular like so
{{myCharactere && myCharactere.statistics && myCharactere.statistics[stat.key] || ''}}.
The object is issue from an API GET request but I'm not able to send it's value to my local variable myCharactere. Thank you for helping me out! Edit: Added code for clarification
Here is what I tried
TypeScript component
export class StatsComponent implements OnInit {
myCharactere: any;
statLookup = [
// Pourquoi est-ce un mauvais choix???
{ key: 'str', prefix: $localize`str`, suffix: $localize`enght`, couleur: 'bg-danger' },
{ key: 'dex', prefix: $localize`dex`, suffix: $localize`terity`, couleur: 'bg-primary' },
{ key: 'con', prefix: $localize`con`, suffix: $localize`stitution`, couleur: 'bg-warning' },
{ key: 'int', prefix: $localize`int`, suffix: $localize`elligence`, couleur: 'bg-success' },
{ key: 'sag', prefix: $localize`wis`, suffix: $localize`dom`, couleur: 'bg-info' },
{ key: 'cha', prefix: $localize`cha`, suffix: $localize`risma`, couleur: 'bg-dark' }
];
constructor(public myService: ServeurService) { }
ngOnInit(): void {
this.myService.getAllInfosById(this.myService.getPersonnageIdByName("Xefoul Snaromers")).subscribe(result => {
this.myCharactere = result
console.log(this.myCharactere);
});
getModifier(stat: number): string {
const mod = Math.floor((stat-10)/2)
return (mod<0)?'-':'+'+ mod.toString();
}
}
TypeScript Service
export class ServeurService {
personnages: any[] = [];
persoName = '';
constructor(private http_client: HttpClient) { }
getPersonnage(): Observable<ICharactere[]> {
return this.http_client.get<ICharactere[]>(this.serverCharacter).pipe(retry(4));
}
getAllInfosById(id: string) {
const myUrl = 'https://cegep.fdtt.space/v1/character/' + id;
return this.http_client.get<ICharactere>(myUrl).pipe();
}
setPersonnageName(name: string) {
this.persoName = name;
}
getPersonnageName():string {
return this.persoName;
}
getPersonnages() {
this.http_client.get<any>('https://cegep.fdtt.space/v1/characters').subscribe({
next: (val) => {
val.data.forEach((element: { data: any; }) => {
this.personnages.push(element);
});
}
});
return this.personnages;
}
getPersonnageById(id: string) {
const persoSelectionne = this.getPersonnages().find((x: { id: string; }) => x.id === id);
return persoSelectionne;
}
getPersonnageIdByName(name: string) {
const persoSelectionne = this.getPersonnages().find((n: {name: string; }) => n.name === name);
console.log("perso name service", persoSelectionne)
return persoSelectionne.id;
}
}
HTML to display
<div class="row text-center text-light bg-secondary mt-2 bg-transparent">
<div class="row mt-5">
<div class="col-2" *ngFor="let stat of statLookup">
<div class="{{stat.couleur}} mx-xxl-4 mx-2 mx-md-1 rounded-4">
<div class="fw-bold">{{stat.prefix}}<span class="d-none d-lg-inline">{{stat.suffix}}</span>
</div>
<div class="h2">
{{myCharactere && myCharactere.statistics && myCharactere.statistics[stat.key] ? getModifier(myCharactere.statistics[stat.key]) : ''}}
</div>
<div class="">
{{myCharactere && myCharactere.statistics && myCharactere.statistics[stat.key] || ''}}
</div>
</div>
</div>
</div>
Model if it helps
export interface ICharactere {
error: string;
id: string;
name: string;
statistics: { [ key : string ]: number }
race: string;
player: string;
classe : string;
sousclasses: string;
level: number;
background: string;
synopsis: string;
image: string;
health: number;
currentHealth: number;
traits: {
trait: string;
description: string;
}[];
armorClass: number;
initiative: number;
speed: number;
}
Summarized from comments:
In your component you can use
serviceName.getAllInfosById("demochar").subscribe(console.log)
to manually make the request to the API and log the result. Please be aware that this.http_client.get<ICharactere>(myUrl) returns a cold observable. This means that nothing will be done until you actually call .subscribe to it.
Best practice:
Usually when you want to display data from an observable in your HTML template you define an observable and subscribe to it using async pipe.
The way to do this is to first define the observable in your component, like: info$ = this.serviceName.getAllInfosById("demochar").
Now in your HTML template you can use {{ (info$ | async).name }} to first subscribe to the observable (async pipe does this for you) and display the name property of the emitted value.
If you are actually using an observable like this.http_client.get<ICharactere>(myUrl), another way is to await the return value and store it in a this.myCharactere:
async getInfos(): void {
this.myCharactere = await firstValueFrom(this.myService.getAllInfosById(this.myService.getIdPersonnageByName("Xefoul Snaromers")));
}

Angular refresh data in child component from filtered value

I have a reusable child table component that loads dynamically based on data from the parent component.
For the first time everything loads well, however when I need to click on one of the sort columns in the table I need to send that sort property again to the parent component to return the result from api and refresh the data in the child component with a new set of data from api.
In the code it looks like this, I missed something in that refresh:
table.component.ts
export class TableComponent implements OnChanges {
#Input() items;
#Input() headers;
#Input('sortColumn') sortColumn;
#Output() sortColumnChange = new EventEmitter<string>();
ngOnChanges(changes: SimpleChanges) {
this.items;
console.log('OnChanges', changes);
}
onSortClick(event, selectedColumn) {
const target = event.currentTarget,
classList = target.classList;
let column = '';
if (classList.contains('sort-icon-asc')) {
classList.remove('sort-icon-asc');
classList.add('sort-icon-desc');
column = `${selectedColumn} DESC`;
this.sortColumn = column;
this.sortColumnChange.emit(column);
} else {
classList.add('sort-icon-asc');
classList.remove('sort-icon-desc');
column = `${selectedColumn} ASC`;
this.sortColumn = column;
this.sortColumnChange.emit(column);
}
}
}
table.component.html
<table>
<thead>
<td (click)="onSortClick($event, header.value)" *ngFor="let header of headers" class="sort-icon-asc">{{ header.name }}</td>
</thead>
<tbody>
<tr *ngFor="let item of items">
<td *ngFor="let value of item | objectValues">
{{ value }}
</td>
</tr>
</tbody>
</table>
users.component.ts
export class UsersComponent implements OnInit {
observablesDispose$: Subject<void> = new Subject();
sortColumn = 'userId ASC';
items: [];
usersTableHeaders = [
{
value: 'userId',
name: this.translateService.instant('ADMIN.USERS_TABLE.USER_ID')
},
{
value: 'name',
name: this.translateService.instant('ADMIN.USERS_TABLE.NAME')
},
{
value: 'role',
name: this.translateService.instant('ADMIN.USERS_TABLE.ROLE')
},
{
value: 'email',
name: this.translateService.instant('ADMIN.USERS_TABLE.EMAIL')
},
{
value: 'status',
name: this.translateService.instant('ADMIN.USERS_TABLE.STATUS')
}
];
constructor(
private readonly usersService: UsersService,
private readonly translateService: TranslateService
) {}
ngOnInit(): void {
this.getUsers();
}
getUsers(): void {
this.usersService
.getUsers(this.sortColumn)
.pipe(takeUntil(this.observablesDispose$))
.subscribe((users) => {
this.items = users.resultList.map((tableColumn) => ({
userId: tableColumn.userId,
name: tableColumn.displayName,
role: tableColumn.role,
email: tableColumn.email,
status: tableColumn.status
}));
});
}
ngOnDestroy(): void {
this.observablesDispose$.next();
this.observablesDispose$.complete();
}
}
users.component.html
<div class="row">
<div class="table-section">
<app-table
[headers]="usersTableHeaders"
[items]="items"
[(sortColumn)]="sortColumn">
</app-table>
</div>
</div>
EDIT
users.component.ts
export class UsersComponent implements OnInit {
observablesDispose$: Subject<void> = new Subject();
sortColumn = 'userId ASC';
items: [];
usersTableHeaders = [
{
value: 'userId',
name: this.translateService.instant('ADMIN.USERS_TABLE.USER_ID')
},
{
value: 'name',
name: this.translateService.instant('ADMIN.USERS_TABLE.NAME')
},
{
value: 'role',
name: this.translateService.instant('ADMIN.USERS_TABLE.ROLE')
},
{
value: 'email',
name: this.translateService.instant('ADMIN.USERS_TABLE.EMAIL')
},
{
value: 'status',
name: this.translateService.instant('ADMIN.USERS_TABLE.STATUS')
}
];
constructor(
private readonly usersService: UsersService,
private readonly translateService: TranslateService
) {}
ngOnInit(): void {
this.getUsers();
}
getUsers(): void {
this.usersService
.getUsers(this.sortColumn)
.pipe(takeUntil(this.observablesDispose$))
.subscribe((users) => {
this.items = users.resultList.map((tableColumn) => ({
userId: tableColumn.userId,
name: tableColumn.displayName,
role: tableColumn.role,
email: tableColumn.email,
status: tableColumn.status
}));
});
}
updateSort(newColumn: string): void {
this.sortColumn = newColumn;
this.getUsers();
}
ngOnDestroy(): void {
this.observablesDispose$.next();
this.observablesDispose$.complete();
}
}
users.component.html
<div class="row">
<div class="table-section">
<app-table
[headers]="usersTableHeaders"
[items]="items"
[(sortColumn)]="sortColumn"
(sortColumnChange)="updateSort($event)"
>
</app-table>
</div>
</div>
In your way of handle sortColumn change you cannot control change of it to update users list. In your component you should do like:
users.component.html
<div class="row">
<div class="table-section">
<app-table
[headers]="usersTableHeaders"
[items]="items"
[sortColumn]="sortColumn"
(sortColumnChange)="updateSort($event)">
</app-table>
</div>
</div>
users.component.ts
...
updateSort(newColumn: string): void {
this.sortColumn = newColumn;
getUsers();
}

Print Json Data in HTML with Ionic/Angular

I'm building an app with IONIC Angular, and I'm trying to print the result in HTML.
From console.log it works correctly, but from the view I can't get the data printed
json api
{
"matches1": {
"homeTeam": "Barcellona",
"awayTeam": "Real Madrid",
},
"matches2": {
"homeTeam": "PSG",
"awayTeam": "Lione",
}
}
home.page.ts
export class HomePage {
matches1: any;
homeTeam1: any;
awayTeam1: any;
result1: any;
private apiurl = 'https://myapi.xxx';
constructor(private httpService: HttpClient) {
this.getdata();
}
getdata() {
this.httpService.get(this.apiurl).subscribe(res => {
this.item = res['matches1']['homeTeam'];
console.log(this.item); <-- this work in console log
}, (err: HttpErrorResponse) => {
console.log (err.message);
}
);
}
}
home.page html
<ion-item *ngFor="let item of items">
{{item.homeTeam}}
</ion-item>
thanks!
This should do the work :
export class HomePage {
matches1: any;
homeTeam1: any;
awayTeam1: any;
result1: any;
items: any;
private apiurl = 'https://myapi.xxx';
constructor(private httpService: HttpClient) {
this.getdata();
}
getdata() {
this.httpService.get(this.apiurl).subscribe(res => {
this.items = Object.keys(res).map(function(key, index) {
return res[key];
});
}, (err: HttpErrorResponse) => {
console.log (err.message);
}
);
}
}
I like to use the json pipe (https://angular.io/api/common/JsonPipe), try this:
<pre>{{ items | json }}</pre>
Edit:
In your case, it might be item | json. Also, Hala Madrid !
You need to declare item in your Controller, and you can't use ngFor if your item is not a list. Right now your json is NOT returning an array, so if you can, it would be best to make it into a list of matches.
Your Json would look better if it was something like this:
{
"matches": [
{
"homeTeam": "Barcellona",
"awayTeam": "Real Madrid",
},
{
"homeTeam": "PSG",
"awayTeam": "Lione",
}
]
}
This way you can easily iterate through your list of matches in your controller
export class HomePage {
matches1: any;
homeTeam1: any;
awayTeam1: any;
result1: any;
item: string[]:
private apiurl = 'https://myapi.xxx';
constructor(private httpService: HttpClient) {
this.getdata();
}
getdata() {
this.httpService.get(this.apiurl).subscribe(res => {
res["matches"].forEach(match => this.item.push(match["homeTeam"]));
}, (err: HttpErrorResponse) => {
console.log (err.message);
}
);
}
}

Angular2 Getting very deep nested json value using pipe! *ngFor

Hi I am having trouble getting json value which is really deeply nested using pipe.
What am I doing wrong?
Pipe I'm using
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({
name: 'keyValues'
})
export class KeysPipe implements PipeTransform {
transform(value, args: string[]): any {
let keys = [];
for (let key in value) {
keys.push({
key: key,
value: value[key]
});
}
return keys;
}
}
Json I'm getting from server.
data:
0: {
Profile: { ...
}
BasicInfo: { ...
}
introduceInfo: {
curriculum: { ...
}
experience: {
0: {
category: "Mentor"
year: "2011"
duration: "24"
}
1: {
category: "Student"
year: "2011"
duration: "14"
}
}
}
}
It's actually a huge json object but I've simplified to only show what I need to get.
I want to get the value of category (which is "Mentor"and "Student".
And to do so, I've tried in my html
<div *ngFor="let detail of teaInfo | keyValues">
<div *ngFor="let experience of detail.value['introduceInfo'] | keyValues">
<div *ngFor="let exp of experience.value['experience'] | keyValues">
<p class="fontstyle2">{{exp.value['category']}} {{exp.value['year']}}년 | {{ex.value['duration']}}개월</p>
</div>
</div>
</div>
And I'm getting my json object in my component like this.
teaInfo: any[];
getTeacherDetail(): void {
let params = new URLSearchParams();
params.set('gradeType', `${this.getVal2()}`)
params.set('subjectType', `${this.getVal3()}`)
params.set('district', `${this.getVal1()}`)
this.teaDetail.getTeachersDetail(params)
.subscribe(
teaInfo => this.teaInfo = teaInfo,
error => this.errorMessage = error
)
}
And the result is I am getting nothing
What am I doing wrong?
Trying to interpret how your JSON looks like, something like this:
{
"data":{
"0": {
"Profile":{
"prof":"prof"
},
"BasicInfo":{
"basic":"basic"
},
"introduceInfo":{
"curriculum": {
"curr":"curr"
},
"experience":{
"0":{
"category":"Mentor",
"year":"2011",
"duration":"24"
},
"1":{
"category":"Student",
"year":"2011",
"duration":"14"
}
}
}
}
}
}
In below example, I have extracted the values from data, so:
.map(res => res.json().data)
To reach values Mentor and Student, first change your pipe to this:
export class KeysPipe implements PipeTransform {
transform(value: any, args: any[] = null): any {
return Object.keys(value).map(key => value[key]);
}
}
and change your HTML to this:
<div *ngFor="let detail of teaInfo | keyValues">
<div *ngFor="let experience of detail['introduceInfo']['experience'] | keyValues">
{{experience.category}}
</div>
</div>
This should work nicely:
Demo