In Angular, why am I unable to display the results from an API call?
The JSON returned from the API call. I want to display the value attribute.
{
"data": [
{
"type": "fuzzycompletions",
"attributes": {
"value": "APALE"
},
"relationships": {
"lei-records": {
"data": {
"type": "lei-records",
"id": "9695006WHN4DWDKBF403"
},
"links": {
"related": "https://api.gleif.org/api/v1/lei-records/9695006WHN4DWDKBF403"
}
}
}
},
{
"type": "fuzzycompletions",
"attributes": {
"value": "APPLITEC"
}
},
{
"type": "fuzzycompletions",
"attributes": {
"value": "AppLogic"
},
"relationships": {
"lei-records": {
"data": {
"type": "lei-records",
"id": "724500S2JQ8M9Q67N911"
},
"links": {
"related": "https://api.gleif.org/api/v1/lei-records/724500S2JQ8M9Q67N911"
}
}
}
},
{
"type": "fuzzycompletions",
"attributes": {
"value": "APPRECIO"
},
"relationships": {
"lei-records": {
"data": {
"type": "lei-records",
"id": "969500ZFBM0TC2T1HV85"
},
"links": {
"related": "https://api.gleif.org/api/v1/lei-records/969500ZFBM0TC2T1HV85"
}
}
}
},
{
"type": "fuzzycompletions",
"attributes": {
"value": "AMPLEGEST"
},
"relationships": {
"lei-records": {
"data": {
"type": "lei-records",
"id": "969500DYMLRK8URCGP47"
},
"links": {
"related": "https://api.gleif.org/api/v1/lei-records/969500DYMLRK8URCGP47"
}
}
}
},
{
"type": "fuzzycompletions",
"attributes": {
"value": "ANPLEX OÜ"
},
"relationships": {
"lei-records": {
"data": {
"type": "lei-records",
"id": "254900RNJSFWJXOZE551"
},
"links": {
"related": "https://api.gleif.org/api/v1/lei-records/254900RNJSFWJXOZE551"
}
}
}
},
{
"type": "fuzzycompletions",
"attributes": {
"value": "Apleks OÜ"
},
"relationships": {
"lei-records": {
"data": {
"type": "lei-records",
"id": "254900VZB706EXH22533"
},
"links": {
"related": "https://api.gleif.org/api/v1/lei-records/254900VZB706EXH22533"
}
}
}
},
{
"type": "fuzzycompletions",
"attributes": {
"value": "APPLE-WAY"
},
"relationships": {
"lei-records": {
"data": {
"type": "lei-records",
"id": "969500QV2N8IIVULLH93"
},
"links": {
"related": "https://api.gleif.org/api/v1/lei-records/969500QV2N8IIVULLH93"
}
}
}
},
{
"type": "fuzzycompletions",
"attributes": {
"value": "APPLICOMM"
},
"relationships": {
"lei-records": {
"data": {
"type": "lei-records",
"id": "969500MCJ58VYVTX3715"
},
"links": {
"related": "https://api.gleif.org/api/v1/lei-records/969500MCJ58VYVTX3715"
}
}
}
},
{
"type": "fuzzycompletions",
"attributes": {
"value": "APPLIQ AS"
},
"relationships": {
"lei-records": {
"data": {
"type": "lei-records",
"id": "8945006S1SZMS8HKLR20"
},
"links": {
"related": "https://api.gleif.org/api/v1/lei-records/8945006S1SZMS8HKLR20"
}
}
}
}
]
}
I have a service.ts file for connecting to the API
import { HttpClient} from '#angular/common/http';
#Injectable({
providedIn: 'root'
})
export class GleifService {
private gleifUrl = 'https://api.gleif.org/api/v1/fuzzycompletions?field=entity.legalName&q='; // URL to web api
private data:any = []
constructor(private http: HttpClient) {}
getGleif(name: string){
const url = `${this.gleifUrl}${name}`;
this.http.get(url).subscribe((res)=>{
this.data = res
console.log(this.data)
})
}
}
A component.ts file
import { Component, OnInit } from '#angular/core';
import { GleifService } from '../gleif.service';
#Component({
selector: 'app-security',
templateUrl: './security.component.html',
styleUrls: ['./security.component.css']
})
export class SecurityComponent implements OnInit {
// array for storing the data
private data:any = []
constructor(private gleifService: GleifService) { }
getGleif(name: string): void {
this.data = this.gleifService.getGleif(name);
}
ngOnInit() {
this.getGleif(name);
}
}
And a .html file for displaying the results
<div>
<label>Company ID:
<input placeholder="Autofill"/>
</label>
</div>
<div>
<label>Company Name:
<input #Name placeholder="A Company Ltd"/>
<button (click)="getGleif(Name.value); Name.value=''">Search</button>
</label>
</div>
<div *ngFor="let entry of data?.data" style="text-align:center">
<h3>
header works: {{ entry.type }}
</h3>
</div>
I can see the results in the console response. How can I access and display results from the API call?
It is because you are subscribing your http call from your service instead of returning it's data. Once you have subscribed the call in your service, no data will be returned unless you return the observable value from your http and let the component subscribe it's response or data
Update your code instead to:
Service
import { Observable } from 'rxjs';
...
getGleif(name: string): Observable<any> {
const url = `${this.gleifUrl}${name}`;
return this.http.get(url);
}
Component
...
getGleif(name: string): void {
this.gleifService
.getGleif(name)
.subscribe(res => this.data = res);
}
or in your Component you could also do this:
...
data$: Observable<any>;
getGleif(name: string): void {
this.data$ = this.gleifService.getGleif(name); // we have assigned it with an Observable variable since the return of this service is of Observable type that needed to be subscribe into
}
Template
...
// Since the data$ is async, we use pipe async to fetch it's raw data
<div *ngFor="let entry of (data$ | async)?.data" style="text-align:center">
...
</div>
make sure to return the data from the service and subscribe inside the component file.
and you don't need to call the function inside ngOnInit because you are going to call it when search button is clicked.
componet.ts file
import { Component } from "#angular/core";
import { GleifService } from "./service";
#Component({
selector: "app-root",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"]
})
export class AppComponent {
// array for storing the data
private data: any = [];
constructor(private gleifService: GleifService) {}
getGleif(name: string): void {
this.gleifService.getGleif(name).subscribe((response: any) => {
this.data = response.data;
});
}
}
component.html file
<div>
<label
>Company ID:
<input placeholder="Autofill" />
</label>
</div>
<div>
<label
>Company Name:
<input #Name placeholder="A Company Ltd" />
<button (click)="getGleif(Name.value); Name.value=''">Search</button>
</label>
</div>
<div *ngFor="let entry of data" style="text-align: center;">
<h3>
header works: {{ entry.type }}
</h3>
</div>
service.ts file
import { HttpClient } from "#angular/common/http";
import { Injectable } from "#angular/core";
#Injectable({
providedIn: "root"
})
export class GleifService {
private gleifUrl =
"https://api.gleif.org/api/v1/fuzzycompletions?field=entity.legalName&q="; // URL to web api
constructor(private http: HttpClient) {}
getGleif(name: string) {
const url = `${this.gleifUrl}${name}`;
return this.http.get(url);
}
}
working codesandbox
Maybe you need to subscribe to the result in the component.ts file
getGleif(name: string): void {
this.gleifService.getGleif(name).subscribe((result) => {
this.data = result
});
}
Related
I am pretty new with Angular and I stuck with problem building up my portfolio project. The problem is with receiving list of (nested) objects like this:
"$id": "1",
"data": {
"$id": "2",
"$values": [
{
"$id": "3",
"id": 1,
"name": "producto",
"shortDescription": "prod11111",
"longDescription": "lorem ipsum bla bla",
"category": "1",
"price": 50.00,
"stockLevel": 10,
"orderDetails": {
"$id": "4",
"$values": []
}
},
{
"$id": "5",
"id": 2,
"name": "segundo",
"shortDescription": "prod222",
"longDescription": "lorem ipsum",
"category": "2",
"price": 30.00,
"stockLevel": 20,
"orderDetails": {
"$id": "6",
"$values": []
}
}
]
},
"error": null
}
This is my app.component.ts:
import { HttpClient } from '#angular/common/http';
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
title = 'Shop';
products: any[] = [];
constructor(private http: HttpClient) {}
ngOnInit(): void {
this.http.get('https://localhost:5001/api/products').subscribe((response: any) => {
this.products = response.data;
}, error => {
console.log(error);
});
}
}
This is app.component.html:
<app-nav-bar></app-nav-bar>
<div class="container" style="margin-top: 140px;">
<h1>Welcome to {{title}}!</h1>
<ul>
<li class="list-unstyled" *ngFor="let product of products">
{{product.name}}
</li>
</ul>
</div>
I looked thru StackOverflow for similar problems but cannot resolve this in my case.
I think that there is problem because of arrays of arrays or nested objects.
Any help is appreciated!
The response.data is not an array. Instead, I believe that response.data.$values is what you need for the products array.
this.http.get('https://localhost:5001/api/products').subscribe((response: any) => {
this.products = response.data.$values;
}, error => {
console.log(error);
});
The following service extracts category objects from a REST service which returns them in HAL format. Now I try to convert that response into JSON. For that I searched and tried different solutions, e.g. chariotsolutions or so. Some are based on Response from '#angular/http' which is deprecated and which I cannot make work.
How can I do the conversion?
import { Injectable } from '#angular/core';
import { HttpClient, HttpHeaders } from '#angular/common/http';
import { Observable } from 'rxjs/Rx';
import { of } from 'rxjs/observable/of';
import 'rxjs/Rx';
import 'rxjs/add/operator/map';
import { Category } from './category';
#Injectable()
export class CategoryService {
private categoriesUrl = 'http://localhost:8080/account/categories';
constructor(private http: HttpClient) { }
getCategories(): Observable<Category[]> {
return this.http.get<Category[]>(this.categoriesUrl);
}
}
The response as HAL
{
"_embedded": {
"categories": [
{
"id": 1,
"name": "hardware",
"description": "comprises all computer hardware",
"level": "FIRST",
"_links": {
"self": {
"href": "http://localhost:8080/account/categories/1"
},
"categoryEntity": {
"href": "http://localhost:8080/account/categories/1"
}
}
},
{
"id": 2,
"name": "hardware_notebook",
"description": "all notebooks",
"level": "SECOND",
"_links": {
"self": {
"href": "http://localhost:8080/account/categories/2"
},
"categoryEntity": {
"href": "http://localhost:8080/account/categories/2"
}
}
}
]
},
"_links": {
"self": {
"href": "http://localhost:8080/account/categories{?page,size,sort}",
"templated": true
},
"profile": {
"href": "http://localhost:8080/account/profile/categories"
}
},
"page": {
"size": 20,
"totalElements": 8,
"totalPages": 1,
"number": 0
}
}
getCategories(): Observable<Category[]> {
return this.http.get<Category[]>(this.categoriesUrl)
.map((result:any)=>{
console.log(result); //<--it's an object
//result={"_embedded": {"categories": [..]..}
return result._embedded.categories; //just return "categories"
});
}
With Rjxs 6.0 we must use pipe(map)
getCategories(): Observable<Category[]> {
return this.http.get<Category[]>(this.categoriesUrl).pipe(
map((result:any)=>{
console.log(result); //<--it's an object
//result={"_embedded": {"categories": [..]..}
return result._embedded.categories; //just return "categories"
}));
}
Try following:
getCategories(): Observable<Category[]> {
return this.http.get<Category[]>(this.categoriesUrl).map((response)=>{
return response;
})
}
I have issue with nested JSON file. I use primeNG templates dataTable and treeTable. When I try to access data via API call in console I can see data but in template I'm getting this error:
ERROR Error: Error trying to diff '[object Object]'. Only arrays and iterables are allowed
json file
{
"CategoryId": "257cf663-f6ba-40f7-69b8-08d527528e70",
"Name": "House Only",
"OverBudgetCalculator": true,
"Scopes": [
{
"ScopeId": 1,
"Name": "Budget",
"Question": {
"QuestionId": 1,
"Content": "What is your maximum budget ?",
"QuestionType": "range",
"Strict": true,
"NeutralScore": 10,
"Weight": 100,
"UpDownSelectionApplied": false,
"Active": true,
"RelatedProductFields": [
{
"RelatedProductFieldId": 3,
"FieldName": "Budget",
"Step": 1000,
"Min": 0,
"Max": 0
}
],
"Answers": []
}
},
{
"ScopeId": 2,
"Name": "Levels",
"Question": {
"QuestionId": 2,
"Content": "Do you want a single or double ?",
"QuestionType": "singleSelection",
"Strict": true,
"NeutralScore": 10,
"Weight": 95,
"UpDownSelectionApplied": false,
"Active": true,
"RelatedProductFields": [
{
"RelatedProductFieldId": 10,
"FieldName": "House_Only_Levels",
"Step": 0,
"Min": 0,
"Max": 0
}
],
"Answers": [
{
"AnswerId": 18,
"Title": "Double Story",
"Content": "Double Story",
"MatchRule": "Double",
"UpDownSpecifications": [],
"Selected": false
},
{
"AnswerId": 19,
"Title": "Single Story",
"Content": "Single Story",
"MatchRule": "Single",
"UpDownSpecifications": [],
"Selected": false
}
]
}
scope.service.ts
import { Injectable } from '#angular/core';
import { Http, Response, Headers, RequestOptions } from "#angular/http";
import { AppSessionService } from '#shared/common/session/app-session.service';
import { Observable } from 'rxjs/Observable';
import { Scope } from './scope';
import { TreeTableModule } from 'primeng/components/treetable/treetable';
#Injectable()
export class ScopeService {
constructor(
private http: Http,
private appSessionService: AppSessionService) { }
// GET api/categories/scopes/257CF663-F6BA-40F7-69B8-08D527528E70
getScopes(categoryId): Observable<any> {
let headers = new Headers({ 'Content-Type': 'application/json' })
let options = new RequestOptions({ headers: headers });
return this.http.get('/api/categories/' + categoryId)
.map((res: Response) => res.json())
.catch(this.handleErrorObservable);
}
private extractData(res: Response) {
let body = res.json();
return body.data || {};
}
private handleErrorObservable(error: Response | any) {
console.error(error.message || error);
return Observable.throw(error.message || error);
}
}
scope.component.ts
import { Component, Injector, OnInit, ViewChild } from '#angular/core';
import { AppComponentBase } from '#shared/common/app-component-base';
import { appModuleAnimation } from '#shared/animations/routerTransition';
import { ScopeService } from './scope.service';
import { DataTable } from 'primeng/components/datatable/datatable';
import { Paginator } from 'primeng/components/paginator/paginator';
import { TreeTableModule } from 'primeng/components/treetable/treetable';
#Component({
templateUrl: './scopes.component.html',
animations: [appModuleAnimation()]
})
export class ScopesComponent extends AppComponentBase implements OnInit {
errorMessage: String;
scopes: any = [];
categoryId: String = '257CF663-F6BA-40F7-69B8-08D527528E70';
constructor(
injector: Injector,
private scopeService: ScopeService
) {
super(injector);
}
ngOnInit(): void {
this.getScopes(this.categoryId);
}
getScopes(categoryId) {
this.scopeService.getScopes(categoryId).subscribe(values => {
this.scopes = values;
console.log(this.scopes);
})
}
}
This is component file with treeTable
scope.component.html (treeTable)
<p-treeTable [value]="scopes" [style]="{'margin-top':'50px'}">
<p-header>Editable Cells with Templating</p-header>
<p-column field="CategoryId" header="ScopeId">
<ng-template let-node="rowData" pTemplate="body">
<input type="text" [(ngModel)]="categories.Scopes.ScopeId" style="width:100%;border-width:0px 0px 1px 0px">
</ng-template>
</p-column>
<p-column field="Name" header="Name">
<ng-template let-node="rowData" pTemplate="body">
<input type="text" [(ngModel)]="categories.Scopes.Name" style="width:100%;border-width:0px 0px 1px 0px">
</ng-template>
</p-column>
</p-treeTable>
This is component file with dataDatable.
scope.components.html (dataTable)
<p-dataTable [value]="scopes"
selectionMode="single"
[rows]="10"
[responsive]="true">
<p-column field="Scopes" header="Scope name">
<ng-template let-account="rowData" pTemplate="body">
<ul>
<li *ngFor="let scope of categories.scopes">
{{scope.ScopeId}} - {{scope.Name}}
</li>
</ul>
</ng-template>
</p-column>
</p-dataTable>
When I use dataTable option I'm getting different error.
ERROR TypeError: val.slice is not a function
enter image description here
enter image description here
I'm following this tutorial https://www.barbarianmeetscoding.com/blog/2016/04/02/getting-started-with-angular-2-step-by-step-6-consuming-real-data-with-http/, with Angular2 and VS Code.
I created a db.json server to test an api with, with the test data looking like
{
"articles": [{
"id": 1,
"name": "Wand of Lightning",
"description": "A powerful wand of ligthning.",
"price": 50,
"imageUrl": "/assets/images/wand.png",
"specs": [{
"name": "weight",
"value": 10
}, {
"name": "height",
"value": 22
}, {
"name": "material",
"value": "wood"
}],
"reviews": [{
"author": "Jaime",
"title": "I loved it!",
"content": "I loved the wand of ligthning! I usually use it to charge my laptop batteries!",
"rating": 5
}, {
"author": "John Doe",
"title": "Underwhelming",
"content": "I didn't like it at all...",
"rating": 1
}]
}, {
"id": 2,
"name": "Staff of Fire",
"description": "A powerful staff of fire.",
"price": 150,
"imageUrl": "/assets/images/staff-of-fire.png",
"specs": [{
"name": "weight",
"value": 10
}, {
"name": "height",
"value": 22
}, {
"name": "material",
"value": "wood and alabaster"
}],
"reviews": [{
"author": "Jaime",
"title": "I loved it!",
"content": "I loved the wand of ligthning! I usually use it to charge my laptop batteries!",
"rating": 5
}, {
"author": "John Doe",
"title": "Underwhelming",
"content": "I didn't like it at all...",
"rating": 1
}]
}
Now if I try to adapt my code to the sample, I get
undefined is not a function
Here's items.component.ts
import { Component, OnInit } from '#angular/core';
import {ItemsService} from '../items.service';
import {Item} from '../item';
#Component({
selector: 'app-items',
templateUrl: './items.component.html',
styleUrls: ['./items.component.css']
})
export class ItemsComponent implements OnInit {
items: Item[] = [];
errorMessage: string = '';
isLoading: boolean = true;
constructor(private itemsService: ItemsService) { }
ngOnInit() {
this.itemsService
.getAll().
subscribe(
p => this.items =p,
e => this.errorMessage = e,
/* onCompleted */ () => this.isLoading = false
)
}
}
items.service.ts
import { Injectable } from '#angular/core';
import { Http, Response, RequestOptions, Headers } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';
import {Item} from './item';
#Injectable()
export class ItemsService {
private baseUrl: string ='http://localhost:3000';
constructor(private http: Http) {
}
getAll(): Observable<Item[]>{
let items = this.http
.get(`${this.baseUrl}/articles`, { headers: this.getHeaders()})
.map(this.mapItems)
.catch(this.handleError);
return items;
}
private getHeaders(){
let headers = new Headers();
headers.append('Accept', 'application/json');
return headers;
}
mapItems(response:Response): Item[]{
return response.json().map(this.toItem)
}
toItem(r:any): Item{
let result = <Item>({
id: r.id,
name: r.name,
description: r.description,
price: r.price,
imageUrl: r.imageUrl,
});
console.log('Parsed item:', result);
return result;
}
// this could also be a private method of the component class
handleError (error: any) {
// log error
// could be something more sofisticated
let errorMsg = error.message || `Yikes! There was a problem with our hyperdrive device and we couldn't retrieve your data!`
console.error(errorMsg);
// throw an application level error
return Observable.throw(errorMsg);
}
}
Note: making
return response.json().map(this.toItem)
Into
return response.json()
works. But I would like to get map working.
EDIT: Screenshot
this will solve your issue -
getAll(): Observable<Item[]> {
const items = this.http
.get(`${this.baseUrl}/articles`, {headers: this.getHeaders()})
.map((response: Response) => this.mapItems(response.json()))
.catch(this.handleError);
return items;
}
mapItems(data: Array<any>): Item[] {
return data.map(item => this.toItem(item));
}
I think what you want to map (Array.prototype.map) is the articles in your response object, and not the object itself. Do this:
mapItems(response:Response): Item[]{
return response.json().articles.map(this.toItem)
}
in my angular2 component
keyword_test={};
getData() {
this.service.getData()
.subscribe(
data => {
this.keyword_test = data
console.log(data);
console.log(this.keyword_test);
});
}
console.log(data) and console.log(this.keyword_test) print right data like this
{
"caption": "folder0",
"type": "folder",
"subnodes": [
{
"caption": "folder1",
"type": "folder",
"subnodes": [
{
"caption": "keyword1",
"type": "keyword",
"search_filter_expression": "sfe1"
},
{
"caption": "keyword2",
"type": "keyword",
"search_filter_expression": "sfe2"
}
]
},
{
"caption": "folder2",
"type": "folder",
"subnodes": [
{
"caption": "keyword3",
"type": "keyword",
"search_filter_expression": "sfe3"
},
{
"caption": "keyword4",
"type": "keyword",
"search_filter_expression": "sfe4"
}
]
}
]
}
but in my ngOnInit
ngOnInit() {
this.getData();
console.log(this.keyword_test);
}
despite the this.getdata(), this.keyword_test print "Object {}" i think none object.
Is the keyword_test initialized incorrectly?
when i print console.log(typeof data) in getData function, result is string...
I did change it to json in service, but I do not know why.
++and this is my service
#Injectable()
export class keywordService {
private API_URI: string = 'MYAPIURL';
constructor(private http: Http) {
}
getData() {
return this.http.get(this.API_URI, {body: ""})
.map(res => {res.json();
});
}
}
ngOnInit() {
this.getData(); // this execute but data arrives with call back
console.log(this.keyword_test); //this execute before data has arrived and thats why it is not printing the result from success call
}
Correct way
this.service.getData()
.subscribe(
data => {
this.keyword_test = data
console.log(data);
console.log(this.keyword_test);
});