NgFor not being passed an array - json

Ive tried searching for a solution to this, but I cant find anything less than 3 or 4 years old and those dont map to my problem well. I know what the issue is from the error, but cant seem to track it down, although I general idea that I will note in my description below:
I need to generate a menu from an array of json elements in the following format:
{
"body": [{
"coursename": "introto1",
"course-lesson-name": "Welcome to One! "
}, {
"coursename": "introto2",
"course-lesson-name": "What is One?"
}, {
"coursename": "introto2",
"course-lesson-name": "What Can We do with One?"
}]
}
This response is coming from AWS API gateway and I have set up the following service to handle the call:
menus.service.ts
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
#Injectable({
providedIn: 'root'
})
export class MenusService {
constructor(private http: HttpClient) { }
getLinks(){
return this.http.get('api address');
}
}
Here is the component that uses the services:
navigation.component.ts
import { Component, OnInit } from '#angular/core';
import { MenusService } from './../menus.service';
#Component({
selector: 'app-navigation',
templateUrl: './navigation.component.html',
styleUrls: ['./navigation.component.css']
})
export class NavigationComponent implements OnInit {
links;
constructor(private menusService: MenusService,) { }
ngOnInit(){
this.links = this.menusService.getLinks();
}
}
and here is the component view:
navigation.component.html
<div>
<div class="col-sm-4" *ngFor="let links of links | async">
<a>{{links['course-lesson-name']}}</a>
</div>
</div>
I suspect my issue is in the service and the way Im establishing the get call:
return this.http.get('api address');
What am I missing here?
Here is the error for reference:
ERROR Error: Cannot find a differ supporting object '[object Object]' of type 'object'.
NgFor only supports binding to Iterables such as Arrays.

I bet this.links resolves into an object and not an array.
Do this in your ngOnInit:
ngOnInit(){
this.links = this.menusService.getLinks();
this.links.subscribe(data => console.log(data)); // ensure data here is an array and not an object with `{ body: [....] }`
}
If it is an object like mentioned previously, in your service, try:
getLinks(){
return this.http.get('api address').pipe(
map(res => res.body),
);
}
You can also do that in the component level too but just be sure to get a handle on the array and not on the object for the *ngFor.

Related

ANGULAR - Mapping nested JSON data from API

so I've been struggling for the past day or so with mapping the response from a mock API - I think I'm mapping it correctly but when I try to access the data it doesn't return anything in the HTML.
Please find my code below:
data.service.ts
import { HttpClient } from '#angular/common/http';
import { Injectable } from '#angular/core';
import { ConsentData, Prompt } from '#app/models/consent-data';
import { Observable } from 'rxjs';
#Injectable({
providedIn: 'root',
})
export class ConsentWebviewDataService {
constructor(private httpClient: HttpClient) {}
getConsentData(): Observable<ConsentData<Prompt>> {
return this.httpClient.get<ConsentData<Prompt>>(
'MY_API_URL',
);
}
}
data.ts (interface)
export interface ConsentData<Prompt> {
prompts: Prompt[];
}
export interface Prompt {
promptId: number;
headline: string;
body: string;
imageUrl: string;
consents: string[];
type: string;
}
app.component.ts
export class PromptComponent implements OnInit {
consentData: any;
constructor(private consentWebviewDataService: ConsentWebviewDataService) {}
ngOnInit(): void {
this.consentWebviewDataService.getConsentData().subscribe(data => {
this.consentData = data.prompts.map(consents => {
return {
promptId: consents.promptId,
headline: consents.headline,
body: consents.body,
imageUrl: consents.imageUrl,
consents: consents.consents,
type: consents.type,
};
});
});
}
}
Lastly here is the API JSON response:
{"prompts":[{"promptId":100,"headline":"Headline","body":"Body text.","imageUrl":"https://picsum.photos/200","consents":["Consent 1","Consent 2","Consent 3"],"type":"ConsentCampaign"}]}
From what I understand so far, after countless tutorials and resources, the getCosentData() function sends request to API, then in the component I subscribe to it, get the response, assign the response to the consentData variable, then map the contents of the response based on the interface / how the JSON response looks.
However, the problem is that I cannot access the mapped data in the HTML. I don't need it in a table, just need to get the mapped data.
I tried all variations such as {{ consentData.promptId }} which I mapped, and it returns ERROR TypeError: ctx.consentData is undefined. Tried {{ consents.promptId }} as well, etc. but nothing works.
What am I missing here? And apologies for the long question && thanks in advance for any help!
You mapped the response into a new array and trying to access it as an object
Try {{ consentData[0].promptId }} to get the id of first element

How to extract data from SafeSubscriber?

I have to make an Angular application in which i get data from the back-end and display it on the front-end, but with some added hard-coded data.
My communication is between 2 files:
client.service.ts
import { Injectable } from '#angular/core';
import {HttpClient, HttpHeaders} from "#angular/common/http";
import {environment} from "../environments/environment";
import {catchError, map, Observable, of} from "rxjs";
const clientUrl = environment.apiUrl+'client';
#Injectable({providedIn: 'root'})
export class ClientService {
public optional: any;
constructor(private http: HttpClient) {}
getText(): Observable<any> {
console.log("it works!");
return this.http.get(clientUrl+"/getText").pipe(map(res => {
console.log(res);
this.optional = res.toString();
}));
}
}
and the second one:
client.component.ts
import { Component, OnInit } from '#angular/core';
import {ClientService} from "../client.service";
#Component({
selector: 'app-client',
templateUrl: './client.component.html',
styleUrls: ['./client.component.css']
})
export class ClientComponent implements OnInit {
public textResponse: any;
constructor(public service: ClientService) {}
ngOnInit(): void {}
getText() {
let text: any;
this.textResponse = this.service.getText().subscribe();
console.log(this.textResponse);
text = this.textResponse + "This text is added from code.";
console.log(text);
}
}
When i call "this.http.get(clientUrl+"/getText")" I get a SafeSubscriber object, from which i managed to get the data displayed in the console using the method ".subscribe(...)" with a "console.log()" inside of it. However, i did not find any method to extract the data out of this subscribe.
As the code above shows, i have tried to use pipe and map, but the local variable is returned as [Object object], and when i print it in the console i get either undefined, either nothing.
This is what my code currently displays:
it works! [client.service.ts:33]
SafeSubscriber {initialTeardown: undefined, closed: false, _parentage: null, _finalizers: Array(1), isStopped: false, …} [client.component.ts]
[object Object]This text is added from code. [client.component.ts]
{text: 'This text is read from a file.'} [client.service.ts]
I have also tried all the suggestions found in questions below:
angular 2 how to return data from subscribe
Angular observable retrieve data using subscribe
Does anyone know a method in which i could get the data out of the Subscribe?
You are missing the return keyword when mapping the response, looking at the console.log, you need the text property
getText(): Observable<any> {
console.log("it works!");
return this.http.get(clientUrl+"/getText").pipe(map(res => {
console.log(res);
this.optional = res.toString();
return res.text;
}));
}

JSON service returning undefined property with Angular 7

This should be the simplest thing. I have a component that calls a service that imports a local JSON directly (see Import JSON directly with Angular 7)
It reads the JSON contents fine, but the pages property is undefined. I think I set everything up based on the StackBlitz in that link, there doesn't seem to be much to it. There isn't any HTML yet, this is all just via the console. It's in app.component.html.
Reading local json files json.service.ts:14
[{…}]0: {name: "Home"}length: 1__proto__: Array(0) json.service.ts:15
undefined home.component.ts:31
json.service.ts:
import { Injectable } from '#angular/core';
import SampleJson from '../assets/SampleJson.json';
export interface JsonInterface {
name: any;
}
#Injectable()
export class JsonService {
ngOnInit() {
console.log('Reading local json files');
console.log(SampleJson);
}
}
home.component.ts:
import { JsonService, JsonInterface } from '../json.service';
import { Component, OnInit, Input } from '#angular/core';
#Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.scss']
})
export class HomeComponent implements OnInit {
constructor(service: JsonService) {
service.ngOnInit();
};
#Input() pages: JsonInterface;
ngOnInit() {
console.log(this.pages);
}
}
Sample.json
{ "name":"Home" }
If I understand your log correctly, it works as expected:
constructor(service: JsonService) {
service.ngOnInit();
};
You request the service and you get an instance. Then you call ngOnInit:
ngOnInit() {
console.log('Reading local json files');
console.log(SampleJson);
}
Now it logs the "reading…" and the content of your json file.
ngOnInit() {
console.log(this.pages);
}
Then you log this.pages which is empty. You never filled it. You never did anything with your service or the data loaded in your service.
I think what you want is something like this
export class JsonService {
getPages() { return SampleJson; }
}
and in your component:
constructor(private service: JsonService) {}
ngOnInit() {
this.pages = this.service.getPages();
console.log(this.pages);
}
The sample code is not tested but I think you've got the point.
The problem is with pages. You have inly declared 'pages' as 'JsonInterface' which is only the type of 'pages' but never initialized with any value so it is undefined.. you need to write a function in Service as the above answer by #Christoph .
I hope you understand why this error occured and If you are not inputting a value to 'pages' from html you don't need to declare it as '#Input'.

Angular 4 get data from github api

I trying getting my last commit from github. I used for this github api.
But I get error:
Cannot find a differ supporting object '[object Object]' of type 'object'. NgFor only supports binding to Iterables such as Arrays."
I can getting data from simple json, and I used service and code like below, but now this not work for me.
Link to json:
JSON
If I getteing data from this JSON, then I don't getting error, and I display what I want.
My githubservice:
import { Injectable } from '#angular/core';
import { Http, Response, HttpModule } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import { IpostsGithub } from './ipostsGithub'
#Injectable()
export class GithubService {
private _postsURL = "https://api.github.com/repos/objectprogr/Dashboard/git/refs/heads/v1";
constructor(private http: Http) {
}
getPosts(): Observable<IpostsGithub[]> {
return this.http
.get(this._postsURL)
.map((response: Response) => {
return <IpostsGithub[]>response.json();
})
}
private handleError(error: Response) {
return Observable.throw(error.statusText);
}
}
github component:
import { Component, OnInit } from '#angular/core';
import { GithubService } from './github.service';
import { IpostsGithub } from './ipostsGithub';
#Component({
selector: 'app-github',
templateUrl: './github.component.html',
styleUrls: ['./github.component.css'],
providers: [ GithubService]
})
export class GithubComponent implements OnInit {
_postsArray: IpostsGithub[];
user: string;
constructor(private githubService: GithubService, ) {
}
getPost(): void {
this.githubService.getPosts()
.subscribe(
resultArray => this._postsArray= resultArray,
error => console.log("Error :: " + error)
)
}
ngOnInit(): void {
this.getPost();
}
}
And html:
<table class="table">
<thead>
<th>1</th>
</thead>
<tbody>
<tr *ngFor="let post of _postsArray">
<td>{{post.message}}</td>
</tr>
</tbody>
</table>
This is code from error which I getting and, which I found o browser console:
_postsArray: […]
0: {…}
object: {…}
sha: "f0814bea75841ef7488552d29c6e1b8ad849f558"
type: "commit"
url: "https://api.github.com/repos/objectprogr/Dashboard/git/commits/f0814bea75841ef7488552d29c6e1b8ad849f558"
__proto__: Object { … }
ref: "refs/heads/v1"
url: "https://api.github.com/repos/objectprogr/Dashboard/git/refs/heads/v1"
And I dont have idea, how to fixed it?
Sounds like the API returns an object of objects, ngFor only works with iterables such as an array.
Seems like you are getting an Object instead of an Array, and you want to loop over that? Correct me if I'm wrong.
If that's the case this is how you would go about it:
in your .ts file
export class SomeClass {
Object: Object;
constructor() {
...
}
}
In your .html
<div *ngFor="let item of Object.keys(yourObject); let i = index;">
{{item}}={{yourObject[item]}}
</div>

Angular - Unable to print json data in HTML page using *ngFor

I am new to angular. I have created a services class that returns a product details in json format.
api.service.ts
import { Injectable } from '#angular/core';
import { Http } from '#angular/http';
import 'rxjs/add/operator/map';
#Injectable()
export class ApiService {
constructor(private http: Http) { }
fetchData() {
return this.http.get('http://funiks.com/qbook/api/productmasterjson.php').map(
(response) => response.json()
).subscribe(
(data) => data
)
}
}
Now i called this service in component class
api.component.ts
import { Component, OnInit } from '#angular/core';
import { ApiService } from '../api.service';
#Component({
selector: 'app-api',
templateUrl: './api.component.html',
styleUrls: ['./api.component.css']
})
export class ApiComponent implements OnInit {
public details;
constructor(private api:ApiService) { }
ngOnInit() {
this.details = this.api.fetchData();
console.log(this.details);
}
}
Now i want to print all the data in HTML page. This is what i have tried to print the json data
<tr *ngFor="let d of details">
<td>{{d.CATEGORY}}</td>
<td>{{d.HSN}}</td>
<td>{{d.ID}}</td>
<td>{{d.NAME}}</td>
<td>{{d.POSTINGHEAD}}</td>
<td>{{d.PRODUCTSERVICE}}</td>
<td>{{d.RATE}}</td>
<td>{{d.SACCODE}}</td>
<td>{{d.TAX_CONNECTED}}</td>
<td>{{d.TYPE}}</td>
<td>{{d.UNIT}}</td>
</tr>
But unfortunately it throws as error and error is like
ERROR Error: Cannot find a differ supporting object '[object Object]' of type 'object'. NgFor only supports binding to Iterables such as Arrays.
Your component doesn't know the type of the fetchData, you should type it with
fetchData():Observable<Product[]> {
You shouldn't subscribe to your observable in fetchData(), just return the observable
fetchData():Observable<Product[]> {
return this.http.get('http://funiks.com/qbook/api/productmasterjson.php')
.map((response) => response.json()
)
}
In your component, subscribe to the observable and type details
details: Product[];
ngOnInit() {
this.api.fetchData().subscribe(data => this.details = data);
console.log(this.details);
}
You need to declare your public details as an array first of all
public details: any[];
Before your async request returns anything, your template doesn't know anything about the datatype of details unless you specify it.
I think that's why you are getting such error.
Cannot find a differ supporting object '[object Object]' of type
'object'. NgFor only supports binding to Iterables such as Arrays.
Also, put your subscribe part inside your component code
In your ngOnInit, you don't need to assign the return value to this.details as the when you are making get call the requests will have observable subscription. You will get a response in observable success so setting this.details value in success is needed as follows:
ngOnInit() {
this.api.fetchData().subscribe(response => this.details = response;);
console.log(this.details);
}