i have been trying to get response from an api (http://www.cricapi.com/how-to-use.aspx) with angular2. i don't know what i am doing wrong? here's my code--
//matchlist.component.ts
#Component({
selector: 'match-list',
template: `
<ul>
<li *ngFor="let m of matches">
<p>{{m.title}}</p>
</li>
</ul>
`,
providers: [MatchListService]
})
export class MatchList implements OnInit{
matches: Match[] = [];
object: any;
constructor(private matchservice: MatchListService){
}
ngOnInit(){
this.matchservice.getMatchList()
.subscribe(
matches => this.matches = matches,
err => {}
);
}
}
here's my service class-
#Injectable()
export class MatchListService{
private matchListUrl = 'http://cricapi.com/api/cricket/';
constructor (private http: Http) {
}
getMatchList(): Observable<Match[]>{
return this.http.get(this.matchListUrl, [{}])
.map((res:any) => res.json()
.catch(this.handleError));
}
please tell me if i am doing it the wrong way? this is my first ex with api!
I'm not sure what language you are trying to code in but with regular angular you simply assign the JSON returned result to a variable and it will hold the complete JSON object like so:
$scope.data = [];
$http({url: url}).then(function (rs) {
console.log(rs);
$scope.data = rs.data;
});
Related
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>
I need to display the data on html that I get from web service. I am able to see the data in a format that I want, but I can't display properly on html. I think -any- in http.get is the problem. I can read data in console without -any- but it works fine with . When it works with it, it still does not print in html properly. Can anyone provide advice on this?
html
<div>{{this.res}}</div>
app.component.ts
import { Component, OnInit } from '#angular/core';
//import { IMovie } from './movie';
import { AppService } from './app.service';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
res: any[] ;
errorMessage: string;
constructor(private _appService: AppService) { }
ngOnInit(): void { this.getData(); }
getData(): void {
this._appService.getData()
.subscribe(
(res: any []) => this.res = res,
(error: any) => this.errorMessage = <any>error);
}
}
app.service.ts :
Injectable()
export class AppService {
private urlNorth = '';
constructor(private http: HttpClient) { }
getData(): Observable<any> {
const headers = new HttpHeaders();
headers.set('Content-Type', 'text/sml');
headers.set('Accept', 'text/xml');
headers.set('Content-Type', 'text/xml');
return this.http.get<any>(this.urlNorth,{responseType:'text', headers: headers})
.do(data => {
// console.log(data)
var dataParsed = data.replace('<string xmlns="service">', '').replace('</string>', '').replace(/</g, '<').replace(/>/g, '>');
// console.log(dataParsed);
parseString(dataParsed, (err, res) => {
if (err) {
return console.dir('invalid XML');
}
else {
console.log(res);
console.log(res.NewDataSet.Table[0].DataPointName[0]);
}
})
})
.catch(this.handleError);
}
**data in console w/o any **
{{this.res}} in html
I'm pretty sure you don't have to put any at this line in app.service.ts
return this.http.get<any>(this.urlNorth,{responseType:'text', headers: headers})
because get method expects 0 type arguments.
Type any is not the problem. It's just TypeScript annotation to organise your code. The problem is you are refering to the res in inline template as this.res, but you should just res. However it won't work as you think. Looking at your data structure You will have to iterate throught this data due to Table is an array. Additionaly I Highly suggest to always represnt your data as class
export class Apps {
public Table: Array<any>; //here should be another type instead of "any"
/* rest of data if any */
}
Back to your question you should have in your html file <div>{{res}}</div> but that's just print your object as string if I good remember. So to properly access your data you should iterate through table using *ngFor
<div *ngFor="let el of res.NewDataSet.Table">
<span>{{el.BackColor}}</span>
<!-- all other data -->
</div>
It looks as though the data is coming back. I'll answer your initial question first (since you added a few issues in comments):
My guess is when you get data back, it's not showing because it's HTML, and angular doesn't like injecting html.
Add this to your TS:
import { DomSanitizer, SafeHtml } from '#angular/platform-browser';
res[]: safeHTML;
And change your html to this:
<div [innerHTML]="res"></div>
As mentioned in a previous answer, this is a solution for a single return of res, not an array of different htmls. If it's an array, you'll have to handle it accordingly. for instance:
<ng-container *ngFor="let r of res">
<div [innerHTML]="r">
</ng-container>
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.
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});
}
I'm stuck here trying to loop the observable object on my users service.
The Chrome's console throws:
error_handler.js:47 EXCEPTION: undefined is not a function
Here's my code:
users.component.ts
import { Component, OnInit } from '#angular/core';
import { UserService } from '../user.service';
import { Observable } from 'rxjs/Rx';
import { User } from '../user';
#Component({
selector: 'app-users',
templateUrl: './users.component.html',
styleUrls: ['./users.component.css']
})
export class UsersComponent implements OnInit {
people: Observable<User[]>;
constructor( private _userService: UserService) { }
ngOnInit() {
this.people = this._userService.getAll();
console.log(this.people);
}
}
users.service.ts
import { Injectable } from '#angular/core';
import { Http, Response, Headers } from '#angular/http';
import { Observable } from 'rxjs/Rx';
import { ErrorObservable } from 'rxjs/observable/ErrorObservable';
import { User } from './user';
#Injectable()
export class UserService {
private baseurl: string= 'http://swapi.co/api';
constructor(private http: Http) {
console.log("User service initialized");
}
getAll(): Observable<User[]>{
let users$ = this.http
.get(`${this.baseurl}/people`,{headers: this.getHeaders()})
.map(this.mapUsers);
return users$;
}
private getHeaders(){
let headers = new Headers();
headers.append('Accept', 'application/json');
return headers;
}
mapUsers(response: Response): User[]{
return response.json().results.map(this.toUser);
}
toUser(r:any): User{
let user = <User>({
id: this.extractId(r),
name: r.name
});
console.log('Parsed user'+user.name);
return user;
}
extractId(personData:any){
let extractedId = personData.url.replace('http://swapi.co/api/people/','').replace('/','');
return parseInt(extractedId);
}
}
users.component.html
<ul class="people">
<li *ngFor="let person of people | async " >
<a href="#">
{{person.name}}
</a>
</li>
</ul>
user.ts
export interface User{
id: number;
name: string;
}
When I remove the HTML code from the template, everything works great (no errors on console) so, I guess there's something wrong with 'people' object, and obviously I can't iterative the response. Please guys, a hand would be appreciated here.
The most likely reason is the way you are handling the map callback
getAll(): Observable<User[]>{
let users$ = this.http
.get(`${this.baseurl}/people`,{headers: this.getHeaders()})
.map(this.mapUsers);
}
mapUsers(response: Response): User[]{
return response.json().results.map(this.toUser);
}
toUser() {}
You need to be careful when using this inside callback functions. The context sometimes messes you up. In this case this in .map(this.toUser) does not point to the class instance. You need to bind it, i.e.
let users$ = this.http
.get(`${this.baseurl}/people`,{headers: this.getHeaders()})
.map(this.mapUsers.bind(this));
When you use bind(this) you are saying that any uses of this inside the mapUsers function should be bound to the class instance.
When you use arrow functions, you don't need to worry about this distinction, as it keeps the lexical scope context
let users$ = this.http
.get(`${this.baseurl}/people`,{headers: this.getHeaders()})
.map(res => response.json().results.map(this.toUser));
Also, even passing the toUser function has the same problem, as you are using this.extractId(r). You also need to bind that
mapUsers(response: Response): User[]{
return response.json().results.map(this.toUser.bind(this));
}