Working with data from observable in a component in Angular 6 - json

I am not sure if I have phrased this question correctly, so I apologize for the clunky wording. I am relatively new to angular but am pretty comfortable with making HTTP requests and working with the data in other frameworks (like VueJS). I am beginning to understand the Observables that angular uses. I am trying to make a blog application, and have an express backend that has the JSON for the blog posts.
In my post.service.ts I have:
import { Injectable } from '#angular/core';
import { HttpClient, HttpHeaders } from '#angular/common/http';
import { Observable, of } from 'rxjs';
import { Post } from '../post';
#Injectable({
providedIn: 'root'
})
export class PostService {
private apiUrl = 'http://localhost:8081/posts';
getPosts(): Observable<Post[]> {
return this.http.get<Post[]>(this.apiUrl);
}
constructor( private http: HttpClient,
private postService: PostService ) { }
}
And then I want to list all the posts in my post-list.component.ts:
import { Component, OnInit } from '#angular/core';
import { PostService } from '../../services/post.service'
import { Post } from '../../post';
#Component({
selector: 'app-post-list',
templateUrl: './post-list.component.html',
styleUrls: ['./post-list.component.css']
})
export class PostListComponent implements OnInit {
public posts = [];
constructor(private postService: PostService) { }
ngOnInit() {
this.postService.getPosts()
.subscribe(data => this.posts = data);
}
}
But the posts array becomes an object and i'm not sure how to use it as an array. If I try to use the *ngFor method, I get an error. The page shows [object Object] if I put {{posts}} in the html. If i do {{posts | json}} it shows the actual JSON, but I still cannot iterate through it.
This is what the json looks like:
{
"posts": [
{
"_id": "5b04b269fde3ca29b35ffc3e",
"name": "Mike",
"title": "Stuff",
"post": "This is a post about stuff"
},
{
"_id": "5b04b24dfde3ca29b35ffc3d",
"name": "OtherUser",
"title": "New Post Test",
"post": "This is a test post"
},
{
"_id": "5b02ed783aa641758c08e601",
"name": "Emerjawn",
"title": "Post"
}
]
}
Before I try to setup CRUD for this application, I want to simply figure out how to display the data which I still cannot do and it is driving me insane. Thank you in advance for the help.

Your return JSON is an object which has field posts holding your needed array data so just take posts field from your server response and render such array of posts. Something like this:
ngOnInit() {
this.postService.getPosts()
.subscribe(data => this.posts = data.posts);
}
For better typing you can always specify your variable type i.e. public posts: Post[] then you will have type checking while coding.

Related

NgFor not being passed an array

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.

Reading content of json file in ionic app

I'm trying to build an app with ionic that reads data from a local `.json' file and uses this data to fill a page. But I'm already struggling with importing the file into the page. What I currently have is:
import { Component } from "#angular/core";
interface Entry {
name: string,
telephone: string
}
interface EntryList {
entryList: Array<Entry>;
}
#Component({
selector: 'page-list',
templateUrl: 'list.html'
})
export class ListPage {
entryList: EntryList;
constructor() {
this.load_entries();
};
load_entries () {
this.entryList = JSON.parse(
// ?
)
};
}
The .json file contains entries like:
[
{"name": "Person A","telephone": "1234"},
{"name": "Person B","telephone": "12345"}
]
I don't know how to proceed from here on. What's the right way to get my data into the app?
Please try this:
constructor(public http: HttpClient) {
this.load_entries();
};
load_entries(filePath: string) { //filePath: 'assets/test.json'
this.http
.get(filePath)
.subscribe((data) => {
console.log(data);
});
}
Of course, you have to import HttpClient first.
import { HttpClient } from '#angular/common/http';

Angular doesn't pass HTTP GET params properly

So I figuring out my way around Angular. Just started with a OpenWeather API based application using a simple GET method.
So here is my app.component.ts:
import { Component } from '#angular/core';
import { WeatherService } from './weather.service';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
providers: [WeatherService]
})
export class AppComponent {
title = 'Ng-Weather';
cityName: string;
constructor(private weather: WeatherService) { }
search() {
this.weather.getWeatherbyName(this.cityName);
}
}
As you can guess, the cityName variable is two way binded. The search() function is invoked onclick of a button and the data is passed to the weatherservice. The contents of weather service is:
import { Injectable } from '#angular/core';
import { Http, Response, URLSearchParams } from '#angular/http';
import { Observable } from 'rxjs';
import { Weather } from './weather';
#Injectable()
export class WeatherService {
APIurl = "http://api.openweathermap.org/data/2.5/weather";
Appid = "xxx";
constructor(private Http: Http) { }
getWeatherbyName(name: string): Observable<any> {
let myParams = new URLSearchParams();
myParams.append('q', name);
myParams.append('appid', this.Appid);
// actual http request should look like this: http://api.openweathermap.org/data/2.5/weather?appid=xxx&q=Chennai
return this.Http.get(this.APIurl, { search: myParams})
.map(this.extractData)
.catch(this.handleError);
}
private extractData(res: Response) {
console.log(res.json());
let body = res.json();
return body.data;
}
private handleError(error: Response | any) {
console.error(error.message || error);
return Observable.throw(error.message || error);
}
}
But I get no error in my console or during the compile process. What is being done wrong? Also, how can I map the JSON I get to my class and give back that instance to the app.component?
Following is my class:
export class Weather {
city: String;
max_temp: String;
min_temp: String;
description: String;
}
And this is a sample JSON I receive:
{
"coord":{
"lon":80.28,
"lat":13.09
},
"weather":[
{
"id":803,
"main":"Clouds",
"description":"broken clouds",
"icon":"04n"
}
],
"base":"stations",
"main":{
"temp":304.15,
"pressure":1008,
"humidity":79,
"temp_min":304.15,
"temp_max":304.15
},
"visibility":6000,
"wind":{
"speed":3.1,
"deg":160
},
"clouds":{
"all":75
},
"dt":1504629000,
"sys":{
"type":1,
"id":7834,
"message":0.0029,
"country":"IN",
"sunrise":1504571272,
"sunset":1504615599
},
"id":1264527,
"name":"Chennai",
"cod":200
}
As you can see all I need is some data from the JSON and not the whole thing.
Your main problem here is that you are not subscribing to the observable that is being produced by your getWeatherbyName function. Observables returned by Http are cold:
Cold observables start running upon subscription, i.e., the observable sequence only starts pushing values to the observers when Subscribe is called. (…) This is different from hot observables such as mouse move events or stock tickers which are already producing values even before a subscription is active.
In order to subscribe to this observable, you can simply update your search function to the following:
search() {
this.weather.getWeatherbyName(this.cityName)
.subscribe();
}
This is by no means the complete solution to your problem - You will want to do something in the subscription, such as assign the information received to properties of your component so that they can be rendered in the UI.
You appear to have other issues in your linked project, but I suggest you ask separate questions on Stack Overflow if needed, or even better, your favorite search engine should be able to help.
Try passing a RequestOptions object to the http get instead:
import { RequestOptions } from '#angular/http';
getWeatherbyName(name: string): Observable<any> {
let myParams = new URLSearchParams();
myParams.append('q', name);
myParams.append('appid', this.Appid);
let options = new RequestOptions({ search: myParams}); //<----- NEW
// actual http request should look like this: http://api.openweathermap.org/data/2.5/weather?appid=xxx&q=Chennai
return this.Http.get(this.APIurl, options) //<<----- NEW
.map(this.extractData)
.catch(this.handleError);
}

Output array values to template

I'm currently learning Angular 2 and have confused myself with how to output data returned from a service to my template.
My API Response :
{
"site": {
"success": true,
"title": "foo",
"description": "bar"
}
}
My Service :
import { Injectable } from '#angular/core';
import {HTTP_PROVIDERS, Http, Response, Headers, RequestOptions } from "#angular/http";
import { Observable } from 'rxjs/Rx';
#Injectable()
export class ContentService {
constructor(private _http:Http) {}
getContent() {
return this._http.get('http://localhost:8080/api/foobar-endpoint/')
.map((res:Response) => res.json())
}
}
My Component :
import { Component, OnInit } from '#angular/core';
import { ContentService } from "../../services/content/content.service";
const template = require('./home.jade');
const styles = require('./home.sass');
#Component({
selector: 'home',
templateUrl: template,
styleUrls: [styles]
})
export class HomeComponent implements OnInit {
public foo = {}
constructor(private _contentService: ContentService) {}
ngOnInit() {
this.getContent();
}
getContent() {
this._contentService.getContent()
.subscribe(
data => {this.foo = data},
err => { console.log(err) },
() => console.log()
);
}
}
My Template :
pre
p {{ foo.site.title }}
If I place {{ foo | json }} in my template I can see the returned values in a JSON format, but when I try and output a single value, such as title I get undefined errors.
How can I access the values being returned?
I think the only thing you are missing here is the ?. Basically the problem is that when the components instantiates your foo property has no site param so angular throws the error.
So what you can do is either this:
{{foo.site?.title}}
Or this:
<p *ngIf="foo.site">{{foo.site.title}}</p>
This way angular won't try to bind the title before there is a site.

How to display data from json retrieved from django-rest-framework?

I've cloned tour of heroes tutorial product from angular team where demo data is storing in in-memory-data-service.ts. Since my preferred backend is django-rest-framework, I need to link them together.
For example, my heroes are translating from localhost:8000/api/v1/heroes/.
[
{
"name": "Greg",
"id": 5,
},
{
"name": "Krek",
"id": 6,
}
]
What should I do except removing in-memory-data-service.ts to replace heroes list with provided by django backend via json? It would be great if you'll tell me do I need model declaration
export class Hero {
id: number;
name: string;
}
yet if rest-framework gives me full objects structure stored in JSON.
To consume any REST API you have to write a service like below,
import { Injectable } from 'angular2/core';
import { Http, Response } from 'angular2/http';
import { Observable } from 'rxjs/Rx';
export class Hero {
id: number;
name: string;
}
#Injectable()
export class HeroService {
constructor(private _http: Http) { }
getHeroes() {
return this._http.get('api/v1/heroes')
.map((response: Response) => <Hero []>response.json())
}
}
Hope this helps!!