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.
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 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
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>
I am creating a project which uses a HTTP get from a web service and returns an array of projects, with ID, name, description etc.
Previously, before I created my filter, the get returned a list of 60 elements using an ngFor in the HTML file:
There is many projects within this web service but I am only concerned with 9 of them the rest are irrelevant.
My code was working when I had created my own observable object with manual data: in my project.service.http.ts class:
data: Project[] = [
{
id:...,
name:...
etc
},
and then in the fetchProjects Method:
fetchProjects(): Observable<Project[]> {
return Observable.of(this.data);
}
Because I want the observable object to be the data from my http get, this method is void. I tried to implement the observable being returned as the data from the web service, but I get the error below in my console when running.
Any help on this would be appreciated.
core.es5.js:1020 ERROR TypeError: response.filter is not a function
at SafeSubscriber._next (project.viewer.component.ts:36)
at SafeSubscriber.webpackJsonp.../../../../rxjs/Subscriber.js.SafeSubscriber.__tryOrUnsub (Subscriber.js:238)
at SafeSubscriber.webpackJsonp.../../../../rxjs/Subscriber.js.SafeSubscriber.next (Subscriber.js:185)
at Subscriber.webpackJsonp.../../../../rxjs/Subscriber.js.Subscriber._next (Subscriber.js:125)
at Subscriber.webpackJsonp.../../../../rxjs/Subscriber.js.Subscriber.next (Subscriber.js:89)
at CatchSubscriber.webpackJsonp.../../../../rxjs/Subscriber.js.Subscriber._next (Subscriber.js:125)
at CatchSubscriber.webpackJsonp.../../../../rxjs/Subscriber.js.Subscriber.next (Subscriber.js:89)
at MapSubscriber.webpackJsonp.../../../../rxjs/operator/map.js.MapSubscriber._next (map.js:83)
at MapSubscriber.webpackJsonp.../../../../rxjs/Subscriber.js.Subscriber.next (Subscriber.js:89)
at XMLHttpRequest.onLoad (http.es5.js:1226)
My Code:
project.service.http.ts:
#Injectable()
export class ProjectServiceHttp extends ProjectService {
//variables
baseUrl = "";
static projectIds: string[] = ["","","","","",""
,"", "",""];
//constructor
constructor(private http: Http) {
super();
}
//methods
fetchProjects(): Observable<Project[]>{
let headers = new Headers({'Content-Type': 'application/json'});
let options = new RequestOptions({headers: headers});
return this.http.get(this.baseUrl, options)
.map((response: Response) =>
{
let result = response.json();
return Observable.of(result);
})
.catch(this.handleError);
}
}
project.viewer.component.ts:
#Component({
selector: 'project-viewer',
templateUrl: './project-viewer.html',
styleUrls: ['./project-viewer.css']
})
export class ProjectViewerComponent {
name = 'ProjectViewerComponent';
projects: Project[] = [];
static projectIds: string[] = ["",""
,"","","",""
,"", "",""];
errorMessage = "";
stateValid = true;
constructor(private service: ProjectService) {
this.service.fetchProjects().subscribe(response => {
this.projects = response.filter(elements => {
return ProjectViewerComponent.projectIds.includes(elements.id);
});
})
}
private fetchProjects() {
this.service
.fetchProjects()
.subscribe(response =>{
this.projects = response['project']
.filter(project => { return ['...', '','','','','...'
,'','',''].indexOf(project.id) !== -1})
console.log(response);
console.log(this.projects);
},
errors=>{
console.log(errors);
});
}
}
project-viewer.html:
<h3>Projects </h3>
<div >
<ul class= "grid grid-pad">
<a *ngFor="let project of projects" class="col-1-4">
<li class ="module project" >
<h4 tabindex ="0">{{project.name}}</h4>
</li>
</a>
</ul>
</div>
They are multiple error in your project. First of all in your service you are not using correctly the map operator you should do:
//methods
fetchProjects(): Observable<Project[]>{
let headers = new Headers({'Content-Type': 'application/json'});
let options = new RequestOptions({headers: headers});
return this.http.get(this.baseUrl, options)
.map(response => response.json())
}
Then in the component you subscribe to the service then you try to do the filtering. You can do that before subscribe like this:
private fetchProjects() {
const filterProject = ['TotalMobileAnalyseInsights', 'TotalMobileMendel','TotalMobileSlam','TotalMobileServer','TotalMobileWedAdmin','TotalMobileForAndroid'
,'TotalMobileForWindows','TotalMobileForWindowsUniversal','TotalMobileForIOS'];
this.service.fetchProjects()
// convert each element of the array into a single observable
.flatMap(projects => ArrayObservable.create(projects))
// filter project
.filter(project => filterProject.indexOf(project.id) !== -1)
.toArray()
// subscribe and do something with the project
.subscribe(projects => console.log(projects));
}
Here is a quick running example https://plnkr.co/edit/3nzr3CFhV2y0iu3cQwAF?p=preview
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;
});