I've followed the Heroes tutorial and I am now attempting to retrieve my hero data from an MVC web api rest service. I have modified the GetHeroes() method in my hero.service:
import 'rxjs/add/observable/of';
import 'rxjs/add/operator/map';
import { Injectable } from '#angular/core';
import { Observable } from 'rxjs/Rx';
import { HttpClient } from '#angular/common/http';
export class Hero {
constructor(public Id: number, public HeroName: string, public Location: string) { }
}
#Injectable()
export class HeroService {
constructor(private http: HttpClient) { }
results: Observable<string[]>;
private location: string;
getHeroes(): Observable<Hero[]> {
return this.http.get('http://localhost:50125/api/heroes')
.map((response: Response): Hero[] => JSON.parse(response['_body']))
.catch(error => Observable.throw(error));
}
getHero(id: number | string) {
return this.getHeroes()
// (+) before `id` turns the string into a number
.map(heroes => heroes.find(hero => hero.Id === +id));
}
}
I am calling the service method from my component:
import 'rxjs/add/operator/switchMap';
import { Observable } from 'rxjs/Observable';
import { Component, OnInit } from '#angular/core';
import { ActivatedRoute, ParamMap } from '#angular/router';
import { Hero, HeroService } from './hero.service';
#Component({
template: `
<h2>HEROES</h2>
<ul class="items">
<li *ngFor="let hero of heroes$ | async"
[class.selected]="hero.Id === selectedId">
<a [routerLink]="['/hero', hero.Id]">
<span class="badge">{{ hero.Id }}</span>{{ hero.HeroName }}
</a>
</li>
</ul>
`
})
export class HeroListComponent implements OnInit {
heroes$: Observable<Hero[]>;
private selectedId: number;
constructor(
private service: HeroService,
private route: ActivatedRoute
) {}
ngOnInit() {
this.heroes$ = this.route.paramMap
.switchMap((params: ParamMap) => {
// (+) before `params.get()` turns the string into a number
this.selectedId = +params.get('id');
return this.service.getHeroes();
});
}
}
My Heroes api controller looks like this:
using HeroesService.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using Newtonsoft.Json;
namespace HeroesService.Controllers
{
public class HeroesController : ApiController
{
// GET: api/Heroes
public List<Hero> Get()
{
List<Hero> heroes = new List<Hero>();
Hero superman = new Hero();
superman.Id = 10;
superman.HeroName = "Superman";
superman.Location = "Los Angeles, California";
heroes.Add(superman);
Hero batman = new Hero();
batman.Id = 11;
batman.HeroName = "Batman";
batman.Location = "Chicago, Illinois";
heroes.Add(batman);
return heroes;
}
}
}
I can see data in the Chrome's Network tab that looks like this:
[{"Id":10,"HeroName":"Superman","Location":"Los Angeles, California"},{"Id":11,"HeroName":"Batman","Location":"Chicago, Illinois"}]
Unfortunately, I get an error that looks like this (probably means the response data is undefined):
ERROR SyntaxError: Unexpected token u in JSON at position 0
at JSON.parse ()
at MapSubscriber.eval [as project] (hero.service.ts:34)
at MapSubscriber._next (map.ts:75)
at MapSubscriber.Subscriber.next (Subscriber.ts:95)
at MapSubscriber._next (map.ts:80)
at MapSubscriber.Subscriber.next (Subscriber.ts:95)
at FilterSubscriber._next (filter.ts:95)
at FilterSubscriber.Subscriber.next (Subscriber.ts:95)
at MergeMapSubscriber.notifyNext (mergeMap.ts:151)
at InnerSubscriber._next (InnerSubscriber.ts:17)
You can take advantage of the fact that HttpClient.get is able to work with JSON data for you. Use the following code to tell HttpClient that the response type is Hero[] and drop your calls to both map and catch:
return this.http.get<Hero[]>('http://localhost:50125/api/heroes');
I expect you are getting an undefined due to the fact that response will not have a property _body.
You're using the new HttpClient but using it the way you would with old http.
getHeroes(): Observable<Hero[]> {
return this.http.get('http://localhost:50125/api/heroes')
.map((response: Response): Hero[] => JSON.parse(response['_body']))
.catch(error => Observable.throw(error));
}
Should be
getHeroes(): Observable<Hero[]> {
return this.http.get<Hero[]>('http://localhost:50125/api/heroes');
}
You can just subscribe to that and you don't need to JSON.parse it.
Related
hi want to show the data from my api to my frontend (Angular 6), but this error comes up: I am using HttpClient method from angular 6 I am new to angular
Angular6 error: the data which I am getting from api is in the string format, I need to convert it to object, below is the response image
this is model.ts
export class Incident {
public Title: string;
public status: string;
constructor(Title: string, status: string) {
this.status = status;
this.Title= Title;
}
}
this is component
import { Component, OnInit } from '#angular/core';
import { Router } from '#angular/router';
import { Incident } from '../../shared/incidents.model';
import { DataStorageService } from '../../shared/data-storage.service';
#Component({
selector: 'app-active-incident',
templateUrl: './active-incident.component.html',
styleUrls: ['./active-incident.component.css']
})
export class ActiveIncidentComponent implements OnInit {
incidents: Incident[];
constructor(private router: Router, private dataStorageService: DataStorageService) { }
ngOnInit() {
this.dataStorageService.getIncidents()
.subscribe(
(data: Incident[]) => this.incidents = data,
(err: any) => console.log(err),
() => console.log('All done getting incidents')
);
}
this is service
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { Observable } from 'rxjs/Observable';
import { Incident } from './incidents.model';
#Injectable()
export class DataStorageService {
constructor(private http: HttpClient) {}
getIncidents(): Observable<Incident[]> {
console.log('Getting all incidents from server');
return this.http.get<Incident[]>
('api/url');
}
}
my json
{
"Events": ["{'title': 'some title', 'Incident_Status': 'status'}",
"{'title': 'some title', 'Incident_Status': 'status'}"]
}
html view
<div class="card" *ngFor="let incident of incidents">
<div class="card-header">
<span class="badge badge-danger"></span>{{incident.Title}}
<span class="badge badge-danger"></span>{{incident.Incident_Status}}
</div>
</div>
You are trying to iterate an object instead of an array. This happens because the list of events are inside the Events key, but you aren't accessing it to extract the list of events. Instead you are using the root of the response object.
Corrected code:
ngOnInit() {
this.dataStorageService.getIncidents()
.subscribe(
(data: Incident[]) => this.incidents = data.Events, // <--
(err: any) => console.log(err),
() => console.log('All done getting incidents')
);
}
I am a beginner in angular and start to build my first app.My goal is to build a generic service that will be inherited from others service. I am following the structure of this link to my approach Generic HTTP Service .In read method i`m using Serializer class to convert the response json object to my typescript an it work. I got a map error. How can I solve it?
Service code:
import { Injectable } from '#angular/core';
import { Http, Headers } from '#angular/http';
import 'rxjs/add/operator/map';
import { HttpClient, HttpHeaders } from '#angular/common/http';
import { Resource } from '../models/resource.model';
import { Observable } from 'rxjs/Observable';
import { Serializer } from '../serializer/serializer';
import { AuthenticationService } from './authentication.service';
#Injectable()
export class SharedService<T extends Resource> {
constructor(
private httpClient: HttpClient,
private url: string,
private endpoint: string,
private authentication: AuthenticationService,
private serializer: Serializer
) { }
create(resource: T) {
let headers = new HttpHeaders();
headers = headers.set('Content-Type', 'application/json; charset=utf-8');
return this.httpClient.post(`${this.url}/${this.endpoint}`, JSON.stringify(resource), { headers: headers });
}
//PUT
update(item: T): Observable<T> {
return this.httpClient.put<T>(`${this.url}/${this.endpoint}`, JSON.stringify(item), { headers: this.addHeaders() })
.map(data => this.serializer.fromJson(data) as T);
}
//GET
read(id: number): Observable<T> {
return this.httpClient.get(`${this.url}/${this.endpoint}/${id}`, { headers: this.addHeaders() })
.map((data: any) => this.serializer.fromJson(data) as T);
}
//GET ALL
list(): Observable<T[]> {
return this.httpClient.get<T>(`${this.url}/${this.endpoint}` , {headers : this.addHeaders()})
.map((data: any) =>
this.convertData(data.items));
}
protected convertData(data: any): T[] {
return data.map(item => {this.serializer.fromJson(item)});
}
protected addHeaders() {
let token = ('Bearer ' + this.authentication.getToken()).valueOf();
let headers = new HttpHeaders();
headers = headers.set('Content-Type', 'application/json; charset=utf-8').set('Authorization', token);
return headers;
}
}
UserService:
import { Injectable } from '#angular/core';
import { SharedService } from './shared.service';
import { HttpClient, HttpHeaders } from '#angular/common/http';
import { User } from '../models/user/user.model';
import { AuthenticationService } from 'app/service/authentication.service';
import { UserSerializer } from '../serializer/user-serializer';
import { NgForm } from '#angular/forms';
#Injectable()
export class UserService extends SharedService<User>{
constructor(httpClient: HttpClient, authenticate: AuthenticationService) {
super(httpClient,
'http://localhost:8084/SuperCloud/webresources',
'user',
authenticate,
new UserSerializer()
);
}
UserSerializer:
import { User } from "../models/user/user.model";
import { Serializer } from "./serializer";
import { Resource } from "../models/resource.model";
export class UserSerializer extends Serializer {
fromJson(json: any): Resource {
const user = new User();
user.id = json.id;
user.name = json.name;
user.surname = json.surname;
user.email = json.email;
user.phoneNumber = json.phoneNumber;
user.password = json.password;
user.username = json.username;
user.active = json.active;
console.log('serializer');
console.log(user);
return user;
}
}
User model:
import { Resource } from "../resource.model";
export class User extends Resource{
username: string;
email: string;
name: string;
surname: string;
phoneNumber: string;
password?: string;
active : boolean;
}
UserService inherited inherited:
ngOnInit() {
this.userService.list().subscribe(
(data) => console.log(data)
);
}
Error:
core.es5.js:1020 ERROR TypeError: Cannot read property 'map' of
undefined
at UserService.SharedService.convertData (shared.service.ts:53)
at MapSubscriber.eval [as project] (shared.service.ts:48)
at MapSubscriber._next (map.js:79)
at MapSubscriber.Subscriber.next (Subscriber.js:95)
at MapSubscriber._next (map.js:85)
at MapSubscriber.Subscriber.next (Subscriber.js:95)
at FilterSubscriber._next (filter.js:90)
at FilterSubscriber.Subscriber.next (Subscriber.js:95)
at MergeMapSubscriber.notifyNext (mergeMap.js:151)
at InnerSubscriber._next (InnerSubscriber.js:25)
First of all, I assume the data that you passed into convertData function is not an array.
Only Array or Observable have map function in this case.
Also, chained function has been changed into pipeable operators in RxJS 6
https://github.com/ReactiveX/rxjs/blob/master/doc/pipeable-operators.md
Secondly, looking at the error message - I don't think the data value returned from the endpoint has value.
Third, data.map(item => {this.serializer.fromJson(item)}); - if the arrow function inside the map function is wrapped in curly bracket, you need to have return keyword.
in other word, data.map(item => {this.serializer.fromJson(item)}); should be data.map(item => this.serializer.fromJson(item)); or data.map(item => {return this.serializer.fromJson(item)});
Use subscribe instead of map to return the response.
return this.httpClient
.put<T>(`${this.url}/${this.endpoint}`, JSON.stringify(item), {
headers: this.addHeaders()
})
.subscribe(data => this.serializer.fromJson(data) as T);
BTW RXJs6 has changed the implementation of using observable map function
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));
}
I am trying to load JSON hada into an Angular 2 Component, and I think I have found the way.
datoer.service.ts:
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import { Observable } from 'rxjs/Observable';
#Injectable()
export class DatoService {
dato: Array<any>;
constructor(private http: Http) {}
getDato() {
return this.http.request('./datoer.json')
.map(res => res.json());
}
}
kalender.component.ts:
import { Component } from '#angular/core';
import { ValgteSkolerService } from '../valgteSkoler.service';
import { DatoService } from './datoer.service';
#Component({
selector: 'kalender',
providers: [DatoService],
templateUrl: 'app/kalendervisning/html/kalender.html'
})
export class KalenderComponent {
private valgteSkoleRuter: Array<any>= [];
//private datoer: Array<any> = [];
constructor(private valgteSkolerService: ValgteSkolerService, private DatoService: DatoService) {
this.datoer = this.DatoService.getDato();
}
ngOnInit() {
this.valgteSkolerService.hentLagretData();
this.valgteSkoleRuter = this.valgteSkolerService.delteValgteSkoleRuter;
}
My template is like:
<p *ngFor="let dato of datoer"> {{dato}} </p>
My problem is the this.datoer above in the component. It says it does not exist on type KalenderComponent.
I have tried declaring it like this in the component:
private datoer: Array<any> = [];
But then it says that "Type 'Observable' is not assignable to type 'any[]'. Property 'length' is missing in type 'Observable'.
Any ideas how to solve this?
The http service, according to Angular2 Http class docs, returns an observable not an array with results, that's because it's made asynchronously. Therefore you must subscribe to the observable so you can feed your array when it gets notified (this happens when http request is complete).
For example:
public datoer: any[] = [];
constructor(
private valgteSkolerService: ValgteSkolerService,
private DatoService: DatoService) {
this.DatoService
.getDato()
.subscribe(datoer => { this.datoer = datoer; });
}
I'm struggling to do a http get request with Angular 2. I've made a file with the JSON information that I want to "get" with my TeacherInfo class and use it to display information by the account component which is used in a routing.
If I click in the routerLink for this element nothing is displayed and if I switch to another routerLink there is neither ( there was before, all routerLinks worked just fine )
file: TeacherInfo.service.ts
import {Injectable, OnInit} from '#angular/core';
import { Http, Response , Headers} from '#angular/http';
import { account } from '../components/account.component';
import {Observable} from "rxjs";
#Injectable()
export class TeacherInfo {
constructor ( private http : Http) {}
private url = '../test.json';
getInfo(){
return this.http.get(this.url)
.toPromise()
.then(response => response.json().data as account );
}
}
file: account.component.ts
import {Component, OnInit} from '#angular/core';
import { TeacherInfo } from '../services/TecherInfo.service';
#Component({
template:`
<h2>This is not ready jet!</h2>
<p>
Willkommen {{name}}! <br/>
E-mail: {{email}}<br/>
</p>
`
})
export class account implements OnInit{
public id : number;
public name : string;
public email: string;
private acc : account;
constructor(private accountinfoservice : TeacherInfo) {
}
getInfo() {
this.accountinfoservice.getInfo()
.then(( info : account ) => this.acc = info );
}
ngOnInit () {
this.getInfo();
if ( this.acc != null ) {
this.id = this.acc.id;
this.name = this.acc.name;
this.email = this.acc.email;
}else {
console.log("there is no data! ");
}
}
and finally test.json :
{
"id" : "1",
"name": "testname",
"email": "testemail"
}
I'm using the latest versions of node and npm and I get no compilation errors and just unrelated errors in the browser console ( other SPA's parts which aren't ready yet). The observable implementations are there because at first I tried to do it that way and came to the conclusion it's easier at first to use a promise.
I subscribe for simple json gets
Calling code
ngOnInit(): void {
this._officerService.getOfficers()
.subscribe(officers => this.officers = officers),
error => this.errorMessage = <any> error;
}
And service code
import { Injectable } from 'angular2/core';
import { Http, Response } from 'angular2/http';
import { Observable } from 'rxjs/Observable';
import { Officer } from '../shared/officer';
#Injectable()
export class OfficerService{
private _officerUrl = 'api/officers.json';
constructor(private _http: Http){ }
getOfficers() : Observable<Officer[]>{
return this._http.get(this._officerUrl)
.map((response: Response) => <Officer[]>response.json())
.catch(this.handleError);
}
private handleError(error: Response){
console.error(error);
return Observable.throw(error.json().error || 'Server error');
}
}
That is returning the data as an array and casting it to the correct type though you can also use any and return [0] if you just expect one.
Hope that helps