Getting [Object Object] in angular2 application - html

I have developed angular2 application using ngrx/effects for making http calls. I have used GIT as reference application. Once the response come from http, i am not able to display it on screen. Its showing [object Object]. Here is my code.
HTML page linked to component.html
<div class="container">
<div class="left-container cf">
<mat-tab-group>
<mat-tab label="Configuration">{{jsons}}</mat-tab>
<mat-tab label="Captured Output">
</mat-tab>
</mat-tab-group>
</div>
</div>
Component.ts
export class ExperimentDetailsComponent implements OnInit {
jsons: Observable<any>;
isLoading: Observable<any>;
constructor(
private store: Store<fromStore.State>
) {
this.isLoading = store.select(fromStore.getIsLoading);
this.jsons = store.select(fromStore.getJson);
console.log(this.jsons)
}
ngOnInit() {
this.store.dispatch(new jsonAction.GetJson());
// this.jsons = this.store.select(fromStore.getJson);
}
}
Effects.ts
export class GetJsonEffects {
#Effect() json$ = this.actions$.ofType(Act.GET_JSON)
.map(toPayload)
.withLatestFrom(this.store$)
.mergeMap(([ payload, store ]) => {
return this.http$
.get(`http://localhost:4000/data/`)
.map(data => {
return new Act.GetJsonSuccess({ data: data })
})
.catch((error) => {
return Observable.of(
new Act.GetJsonFailed({ error: error })
);
})
});
constructor(
private actions$: Actions,
private http$: HttpClient,
private store$: Store<fromStore.State>
) {}
}

As you see, the result of store.select() is an observable. You cannot data bind to it directly.
You can either:
Use the async pipe to make the UI subscribe to the observable for you and extract the data, like:
<mat-tab label="Configuration">{{jsons | async}}</mat-tab>
Or subscribe yourself to the observable.
export class ExperimentDetailsComponent implements OnInit {
jsonSubscription = store.select(fromStore.getJson)
.subscribe(jsons => this.jsons = jsons);
ngOnDestroy() {
this.jsonSubscription.unsubscribe();
}
jsons: any;
// ...
}
That's one thing:
If you are using Http service (from #angular/http module):
The other thing is that you are returning the Response object not the JSON extracted from it. The map() in your effect needs to call data.json(). Like:
return this.http$
.get(`http://localhost:4000/data/`)
.map(data => {
return new Act.GetJsonSuccess({ data: data.json() })
})
Or, as I like, add another map() to make things clear:
return this.http$
.get(`http://localhost:4000/data/`)
// You could also create an interface and do:
// `response.json() as MyInterfaceName`
// to get intellisense, error checking, etc
.map(response => response.json())
.map(data => {
return new Act.GetJsonSuccess({ data: data })
})
If you are using HttpClient service (from #angular/common/http module):
(Available in Angular v4.3+)
In this case you don't need to call .json() yourself, it does it for you, so you don't need that first .map() I suggested.
You can also tell TypeScript about the type you expect the JSON to match by calling the get() like this:
return this.http$
.get<MyInterfaceName>(`http://localhost:4000/data/`)
.map(data => {
return new Act.GetJsonSuccess({ data: data.json() })
})
The get<MyInterfaceName>() bit will make Angular tell TypeScript that the JSON object matches the MyInterfaceName, so you'll get intellisense and error checking based on this (at compile time only, none of this affects runtime in anyway).
HttpClient Documentation

Related

How to fetch and use data from a JSON file in Angular7

I am trying to fetch data from a JSON file and display that data in the form
JSON FILE Link:
https://raw.githubusercontent.com/datameet/railways/master/trains.json
I am trying with the below code. But it returns following error in fetchdata.component.ts file:
Property 'json' does not exist on type 'Object'.
fetchdata.component.ts
import { Component, OnInit } from '#angular/core';
import { HttpClient } from '#angular/common/http';
#Component({
selector: 'app-fetchdata',
templateUrl: './fetchdata.component.html',
styleUrls: ['./fetchdata.component.css']
})
export class FetchdataComponent implements OnInit {
private _trainUrl = "https://raw.githubusercontent.com/datameet/railways/master/trains.json
";
items : any;
constructor(private http:HttpClient) {
this.http.get( this._trainUrl)
.subscribe(res => this.items = res.json());
console.log(this.items);
}
ngOnInit() {
}
}
fetchdata.component.html
<select>
<option *ngFor="let item of items" [value]="item.properties.from_station_name">{{item.properties.from_station_name}}</option>
</select>
Please help.
The response probably isn't what you think. I suggest you console.log() the response of your query to see what it actually looks like:
items : any;
constructor(private http:HttpClient) {
this.http.get( this._trainUrl)
.subscribe(res => {
this.items = res.features;
console.log("Response", res);
console.log(res.features)
});
}
You'll see that you actually get something like this in your console:
{type: "FeatureCollection", features: Array(5208)}
features: (5208) [{…}, …]
type: "FeatureCollection"
__proto__: Object
So you can assign your items to the features key as that's what you really need:
constructor(private http:HttpClient) {
this.http.get( this._trainUrl)
.subscribe(res => {
this.items = res["features"];
});
}
Then your select options should show up.
Just letting you know, this isn't the perfect way to do it but it works fine for a small example like this.
I suggest you look into creating a service for any request in the future (doing it in the constructor isn't the best way) and have a look at the RxJS library
There is a difference between Angular's Http and HttpClient module and they are also exported differently.
Http -> is the core module which requires the user to call res.json(). This was common prior to Angular version 4.0.
HttpClient -> is new module since version 4.0. It defaults the communication to json and hence you don't need to call res.json() explicitly.
In short, changing from res.json() to just res will fix the issue
for you.
i.e this.items = res; should be fine.
Also, as a good practice use the ngOnInit lifecycle method instead of the constructor to make any Http calls.
Why do you do this.items = res.json()? Why not just this.items = res? res should already hold the JSON object returned from the GET request. If it is indeed a string try this.items = JSON.parse(res).
Can you try :
private _trainUrl = "https://raw.githubusercontent.com/datameet/railways/master/trains.json";
items : any;
constructor(private http:HttpClient) {}
ngOnInit() {
this.http.get( this._trainUrl).subscribe(res => {
this.items = res;
console.log(this.items);
});
}
You don't need to call .json() function in case you doing plain this.http.get. Angular does that for you. Simply do this.items = res. That will do the trick.
UPD: your JSON object is not an array itself. You as well need to update your template in the following way:
<select>
<option *ngFor="let item of items.features" [value]="item.properties.from_station_name">{{item.properties.from_station_name}}</option>
</select>

How to save a list of json data that i get from an API to a class property using fetch

I'm trying to call a localhost API that i created in my react app class. This API will return a list of json data, i'm trying to save these results in a property
I don't know much about Reacjs. What i have tried so far is to create a method that will call the API and return the data, the i call this method in my class and save the results in a property.
The type of this method is Promise since the results that i'm expectibng are a list of data :
let items: any[];
function getIncidentsFromApiAsync(): Promise<any[]>{
return fetch('http://localhost:3978/calling')
.then((response) => response.json())
}
export class App extends React.Component<{}, IDetailsListCustomColumnsExampleState> {
constructor(props: {}) {
super(props);
getIncidentsFromApiAsync().then(json => items = json);
}
}
I haven't been able to see the results since items is always undefined after calling getIncidentsFromApiAsync() method.
You can handle this in React using State and lifecycle method componentDidMount that gets called when the component is ready:
function getIncidentsFromApiAsync(): Promise<any[]>{
return fetch('http://localhost:3978/calling').then(
(response) => response.json()
);
}
export class App extends React.Component<{}, IDetailsListCustomColumnsExampleState> {
constructor(props: {}) {
super(props);
this.state = {
items: []
};
}
componentDidMount() {
getIncidentsFromApiAsync().then(json => this.setState({ items: json });
}
render() {
if (this.state.items.length) {
const itemsList = this.state.items.map((item) => <li key={item}>{item}</li>);
return (
<div>
<ul>{itemsList}</ul>
</div>
);
}
return <div>List is not available</div>;
}
}

How to assign local variable to json response angular2 [duplicate]

This question already has answers here:
How do I return the response from an Observable/http/async call in angular?
(10 answers)
Closed 5 years ago.
I want to assign a json response which contains an array with the following:
0
:
{ID: 2, NAME: "asd", PWD_EXPIRY_IN_DAYS: 30}
1
:
{ID: 1, NAME: "Admin", PWD_EXPIRY_IN_DAYS: 30}
I have a local variable of type groups, which is like so
export class Group {
id: string;
name: string;
pwd_expiry_in_days: string;
}
Now I created an object of type Group in my component which I want to assign the json reply into, the following is not working and is showing undefined. Here is my code:
import { Injectable, Provider, ModuleWithProviders,Component, OnInit } from '#angular/core';
import { Http, Headers, Response, RequestOptions } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import {Group} from '../../_models/group'
import 'rxjs/add/operator/map';
interface Validator<T extends FormControl> {
(c: T): { [error: string]: any };
}
#Component({
selector: 'form1',
templateUrl: './form1.html',
moduleId: module.id,
})
export class Form1Component {
public roles: Group; // <--- variable to feed response into
private getGroups() : Observable<any> {
console.log("In Groups");
var responseAsObject : any;
let _url = groupsURL";
let headers = new Headers();
headers.append('X-User', sessionStorage.getItem('username'));
headers.append('X-Token', sessionStorage.getItem('token'));
headers.append('X-AccessTime', sessionStorage.getItem('AccessTime'));
headers.append('Content-Type', 'application/json');
let options = new RequestOptions({ headers: headers });
return this.http.get(_url, options)
.map(response => {
var responseAsObject = response.json();
console.log(responseAsObject); //<--- proper response
return responseAsObject;
})
}
constructor(private http: Http) {
this.getGroups()
.subscribe(data => {
this.roles = data;
console.log(this.roles); //<--- proper response
});
console.log(this.roles); //<----- undefined, I need the roles variable so I can use it on the front end
}
How can I fix this? I've been told its an Async issue, simply assigning this.roles = data (from the json response) is not working and shows up as undefined in my component (anywhere outside the scope of the subscription).
What is the proper method of assigning a response into my local variable in this case?
UPDATED with template to view the object, also being viewed as undefined:
<div class="form-group" [ngClass]="{'has-error':!complexForm.controls['group_id'].valid}">
<label>Group ID</label>
<div class="row">
<div class="col-md-4">
<select name="group_id" id="group_id" class="form-control" [formControl]="complexForm.controls['group_id']" data-width='200px'>
<option *ngFor="let role of roles" [value]="role.id">
{{role.name}}
</option>
</select>
</div>
</div>
</div>
thank you!
What is the proper method of assigning a response into my local variable in this case?
You're not doing it wrong. You just need to be better prepared for it for be undefined and/or empty at the initial stages of the component construction.
The easiest thing to do is simply do an *ngIf="someArray.length" on an html node before iteration. something like:
// html
...
<div class="row" *ngIf="roles.length"><!-- Prevents un-populated array iteration -->
<div class="col-md-4">
<select class="form-control">
<option *ngFor="let role of roles" [value]="role.id">
{{role.name}}
</option>
</select>
</div>
</div>
There are some improvements you can make to your typescript as well, such as not changing the pointer of the array- this may save you some trouble later on -so instead of this.roles = data, use this.roles.length = 0; this.roles.push(...data);. For more info read up on Angular Change Detection.
// ts
export class Form1Component implements OnInit{
public roles: Array<Group> = []; // <--- isn't this an Array?
...
private getGroups() : Observable<any> {
var responseAsObject : any;
...
return this.http.get(_url, options)
.map(response => {
var responseAsObject = response.json();
return responseAsObject;
});
}
constructor(private http: Http) {}
ngOnInit(){
this.getGroups()
.subscribe(data => {
let groups = data.map(item=>{
return new Group(item)
});//<--- If you want it to be of type `Group`
this.roles.length = 0;
this.roles.push(...groups);
});
}
...
}
You second console log will run before your api call because api calls are asynchronous. Please try to make the type of role any like publice role: any if its works then you have to modify your Group model.

Array undefined in Typescript but works in HTML

I have a component that populates an Object array in its ngInit() method from a service which I then use the contents of in my HTML template.
My problem is I can use this data fine in the HTML template but if I try to use this same Object array in my TypeScript file I will get an undefined error.
Below is a simplified code example of my problem:
#Component({
selector: 'booking',
template: `
<div *ngFor="let r of requestedBookings">
<label>Requested on {{r.created | date: 'd MMM H:mm'}}</label>
</div>
`
})
export default class BookingComponent {
requestedBookings: Object[];
constructor(private bookingService: BookingService) {
}
ngOnInit() {
this.getRequestLog();
// Cannot read property 'length' of undefined error
// console.log(this.requestedBookings.length);
}
private getRequestLog(): void {
this.bookingService.getRoomRequestBooking(1,1,1)
.subscribe(data => this.requestedBookings = (data as any))
.results, err => {
console.log(err);
}
}
Why is it in the above example I can use the requestedBookings array as expected in the HTML template but inside the TypeScript file I receive undefined errors?
IMHO the correct way should be something like:
ngOnInit() {
this.getRequestLog();
}
private getRequestLog(): void {
this.bookingService.getRoomRequestBooking(1,1,1)
.subscribe((data)=>{
this.requestedBookings = data;
console.log(this.requestedBookings.length);
})
.results, err => {
console.log(err);
}
}
As explained before, the call to getRoomRequestBooking is async, so you should not expect it will finish before calling the console.log. Instead, you should use the requestedBookings.length value in a place where you do know it will exist. Hope it helps!!
I fixed this issue by using this constructor from the subscribe method. the complete parameter event happens after successful completion.
subscribe(next?: (value: T) => void,
error?: (error: any) => void,
complete?: () => void): Subscription;
Code is as follows:
ngOnInit() {
this.getRequestLog();
}
private getRequestLog() {
this.bookingService.getRoomRequestBooking(this.date, this.level, this.room)
.subscribe(
data => this.requestedBookings = (data as any).results,
err => {
console.log(err);
},
() => console.log(this.requestedBookings.length));
}

http with Observable in Angular 2 cant use data

i am new to angular 2 and to observables but i wanted to give it a shot. So i have installed the angular-cli and made a simple test project.
All i wanted it to do is read a json file and work with the data inside of a component (the first intention was to make a service but i wanted to start on a low basis).
So i have created a json file in the assets/json folder (testjson.json):
{
"teststring": "test works"
}
then i have imported the http from angular and the rxjs map stuff inside of my content.component.ts file:
import { Component, OnInit } from '#angular/core';
import { Http } from '#angular/http';
import 'rxjs/add/operator/map';
#Component({
selector: 'app-content',
templateUrl: './content.component.html',
styleUrls: ['./content.component.css']
})
export class ContentComponent implements OnInit {
title: string = "Default";
data;
constructor(private http:Http) {
http.get('assets/json/testjson.json').map(res => res.json()).subscribe(data => {this.data = data; this.title = data.teststring; console.log(this.data);});
}
ngOnInit() {
}
}
So far so good, the app prints out the following:
app works!
test works [object Object]
But i want to use this data in the whole component, not only in the constructor. but if i try to console.log "this.data" outside of the constructor (inside the ngOnInit function), it prints undefined in the console.
I know, that it must have something to do with asynch loading but unfortunately i have no clue how to tell the app to wait until this.data is filled.
I hope you can help me with that. Of course in the future i want a service which does that kind of stuff and more than one component should grab data from it.
Thanks in advance!
You should move the initialization code to the initialization method.
Your data becomes available once the callback completes. In your template you can use *ngIf to execute code inside a block once there is data. As long as the *ngIf does not eval to true the inner code will not run.
The only way you can run console.log(data) is from inside the callback or called from the callback because you have to wait until the data is loaded.
content.component.html
<div *ngIf="data">
<span>{{data.teststring}}</span>
</div>
content.component.ts
export class ContentComponent implements OnInit {
title: string = "Default";
data: any = null;
constructor(private http:Http) {
}
ngOnInit() {
this.http.get('assets/json/testjson.json')
.map(res => res.json())
.subscribe(data => {
this.data = data;
this.title = data.teststring;
console.log(this.data);
});
}
}
Edit
In response to the comment below If you abstract out the http call to a service you can see the exact same logic still applies. You are still using the concept of a promise of data and that you can subscribe to that promise once it has completed. The only difference here is the http call is abstracted to a different class.
content.component.ts
export class ContentComponent implements OnInit {
title: string = "Default";
data: any = null;
// inject service
constructor(private contentService:ContentService) {
}
ngOnInit() {
this.contentService.getData()
.subscribe(data => {
this.data = data;
this.title = data.teststring;
console.log(this.data);
});
}
Service
export class ContentService {
constructor(private http:Http) {
}
getData(): IObservable<{teststring:string}> { // where string can be some defined type
return http.get('assets/json/testjson.json')
.map(res => res.json() as {teststring:string});
}