Angular 2 Tour of Heroes doesn't work - javascript

I tried to write application based on tour of heroes.
I have Spring application which shares resources and client app which should get this data. I know that resources get to client app, but I can't print it.
import { HeroesService } from './shared/HeroesService';
import { Observable } from 'rxjs/Observable';
import { Hero } from './shared/Hero';
import { OnInit } from '#angular/core';
import { Component } from '#angular/core';
#Component({
selector: 'app',
template: require('app/app.component.html!text')
})
export class AppComponent implements OnInit {
errorMessage: string;
items: Hero[];
mode: string = 'Observable';
firstItem: Hero;
constructor(private heroesService: HeroesService) { }
ngOnInit(): void {
this.getHeroes();
console.log(this.items);
//this.firstItem = this.items[0];
}
getHeroes() {
this.heroesService.getHeroes()
.subscribe(
heroes => this.items = heroes,
error => this.errorMessage = <any>error
);
}
}
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';
import { Hero } from './Hero';
#Injectable()
export class HeroesService {
private heroesUrl = 'http://localhost:8091/heroes';
constructor(private http: Http) { }
getHeroes(): Observable<Hero[]> {
return this.http.get(this.heroesUrl)
.map(this.extractData)
.catch(this.handleError);
}
private extractData(res: Response) {
let body = res.json();
console.log(body);
return body || { };
}
private handleError(error: Response | any) {
let errMsg: string;
if (error instanceof Response) {
const body = error.json() || '';
const err = body.error || JSON.stringify(body);
errMsg = `${error.status} - ${error.statusText || ''} ${err}`;
} else {
errMsg = error.message ? error.message : error.toString();
}
console.error(errMsg);
return Observable.throw(errMsg);
}
}
In method extract data when I printed by console.log(body.data) I get undefined, but when I printed console.log(body) I get list of objects, therefore I return body instead body.data.
And when I print objects in extractData I get list of objects, but in AppComponent when I print console.log(this.items) I get undefined.
What's going on?

this.getHeroes() returns an Observable which means that you can't get data out of it unless you subscribe to it. Think about it like a magazine subscription, by calling this.getHeroes(), you have registered for the magazine but you don't actually get the magazine until it gets delivered.
In order to get a console.log of the data that comes back in AppComponent, replace the .subscribe block with the following:
.subscribe(
(heroes) =>{
console.log(heroes);
this.items = heroes;
},
error => this.errorMessage = <any>error
);
To further the magazine analogy, inside the subscribe block, you have received the magazine and here we are console logging its contents.
Hope this helps

Related

how do i assign data from the subscribe call to a local variable in Angular

I have tried to declare the variable inside the ngOnInit() but it goes out of scope. I would like to use the variable to iterate over the data and populate my html component. I have followed the answered questions on the same issue but most of them suggest calling a function inside the subscribe function, unfortunately in my case i just need to view the returned data.
import { Component, OnInit } from "#angular/core";
import { Playlist } from "../playlist";
import { PlaylistService } from "../playlist.service";
#Component({
selector: "app-market-place",
templateUrl: "./market-place.component.html",
styleUrls: ["./market-place.component.css"],
})
export class MarketPlaceComponent implements OnInit {
_playList: Playlist[] = []; //never gets assigned here
errorMessage;
constructor(private playListService: PlaylistService) {}
ngOnInit() {
this.playListService.getPlaylist().subscribe({
next: (playList) => {
this._playList = playList;
console.log(this._playList); // am able to log the data but its never assigned to my _playList variable
},
error: (err) => (this.errorMessage = err),
});
}
}
here is the service class
import { Injectable } from "#angular/core";
import {HttpClient, HttpErrorResponse} from '#angular/common/http'
import { Observable, throwError } from "rxjs";
import {catchError, map, tap} from 'rxjs/operators'
import { Playlist } from "./playlist";
#Injectable({
providedIn: "root",
})
export class PlaylistService {
private playListUrl = "https://reqres.in/api/users";
constructor(private http: HttpClient) {}
getPlaylist():Observable<Playlist[]> {
return this.http.get<Playlist[]>(this.playListUrl).pipe(
map((response) => <Playlist[]>response),
catchError(this.handleError),
);
}
private handleError(err:HttpErrorResponse){
let errorMessage = '';
if (err.error instanceof ErrorEvent){
errorMessage = `An error occurred: ${err.error.message}`
}else {
errorMessage = `Server returned code: ${err.status}, error message is: ${err.message}`;
}
console.error(errorMessage);
return throwError(errorMessage)
}
}
I do not know, what your Playlist object looks like, but maybe I have a simple idea, that could solve your issue. I suppose that console.log can not show the array of your objects. Did you try the following?
console.log(this._playList[0]);

Angular 6 : Issue of component data binding

I call service which make http call, I assign response to component variable now when I try access that component variable to view it display blank.
Means component variable assign in subscribe successfully but cant acceess in html view.
I think view is loaded before values assign to component data.
component
import {Component, OnInit, ChangeDetectionStrategy} from '#angular/core';
import { UserService } from '../../../../../core/services/users/user.service';
import { HttpClient } from '#angular/common/http';
#Component({
selector: 'm-user-list',
templateUrl: './user-list.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class UserListComponent implements OnInit {
list;
roles = {};
current_page: any
totalRecords: any
public showContent: boolean = false;
constructor(private userService: UserService, private http: HttpClient) {
}
ngOnInit() {
this.getRecords();
}
getRecords(){
this.getResultedPage(1);
}
getResultedPage(page){
return this.userService.getrecords()
.subscribe(response => {
this.list = response.data;
});
}
}
Service
import { Injectable } from '#angular/core';
import { Observable, of, throwError } from 'rxjs';
import { HttpClient, HttpParams , HttpErrorResponse, HttpHeaders } from '#angular/common/http';
import { map, catchError, tap, switchMap } from 'rxjs/operators';
const httpOptions = {
headers: new HttpHeaders({'Content-Type': 'application/json'})
};
import { UtilsService } from '../../services/utils.service';
import { AppConfig } from '../../../config/app'
#Injectable({
providedIn: 'root'
})
export class UserService{
public appConfig: AppConfig;
public API_URL;
constructor(private http: HttpClient, private util: UtilsService) {
this.appConfig = new AppConfig();
this.API_URL = this.appConfig.config.api_url;
}
private extractData(res: Response) {
let body = res;
return body || { };
}
private handleError(error: HttpErrorResponse) {
if (error.error instanceof ErrorEvent) {
// A client-side or network error occurred. Handle it accordingly.
console.error('An error occurred:', error.error.message);
} else {
// The backend returned an unsuccessful response code.
// The response body may contain clues as to what went wrong,
console.error(
`Backend returned code ${error.status}, ` +
`body was: ${error.error}`);
}
// return an observable with a user-facing error message
return throwError('Something bad happened; please try again later.');
};
getrecords(): Observable<any> {
return this.http.get('/api/users', httpOptions).pipe(
map(this.extractData),
catchError(this.handleError));
}
}

Angular 2 API service to display error UI message

I'm new to Angular 2, so excuse me if the question is silly.
I have to fetch data from the server and display it in the component. The server has some API methods, so I've created the api.service.ts which looks like this:
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
const protocol = 'http';
const domain = 'mydomain.ng';
const port = ':4200';
#Injectable()
export class ApiService {
constructor(private http: HttpClient) { }
buildQuery(apiMethod: string) {
return `${protocol}://${domain}${port}/${apiMethod}`;
}
get(apiMethod: string): Observable<Response> {
const query = this.buildQuery(apiMethod);
return this.http.get<Response>(query)
.map(
resp => {
if (resp.ok) {
return resp;
} else { // Server returned an error
// here I need to show UI error in the component
}
}
)
.catch( // Error is on the client side
err => {
// here I need to show UI error in the component
}
);
}
getGeneralReport(): Observable<Response> {
return this.get('generalReport');
}
}
Server API has a lot of methods, so the get() method is designed to perform the actual request and handle common mistakes. Then I will have methods like getGeneralReport() which will call the get method with the parameter specifying which API method should be used.
Also I have a component called general.component.ts where the api.service is injected:
import { Component, OnInit } from '#angular/core';
import { ApiService } from '../../shared/api/api.service';
#Component({
selector: 'app-general',
templateUrl: './general.component.html',
styleUrls: ['./general.component.scss']
})
export class GeneralComponent implements OnInit {
generalInfo: Response;
constructor(private apiService: ApiService) { }
ngOnInit() {
this.apiService.getGeneralReport().subscribe(
data => {
this.generalInfo = data;
// Display the received data
}
);
}
}
There will be more components like general.component which will use the api.service. Now I'm stuck because I need to pop up the UI window in all the components which use api.service if the error occurs in api.service. Is it possible or should I use some different approach?
Yes it is possible, do it like this:
this.apiService.getGeneralReport().subscribe(
data => {
this.generalInfo = data;
// Display the received data
},
err => {
// yourPopupmethod(err)
}
);
and in service throw error. So update your service by adding HandleError method:
handleError(error: Response | any) {
return Observable.throw(new Error(error.status))
}
get(apiMethod: string): Observable<Response> {
const query = this.buildQuery(apiMethod);
return this.http.get<Response>(query)
.map(
resp => {
if (resp.ok) {
return resp;
} else { // Server returned an error
this.handleError(resp);
}
}
)
.catch(
err => {
this.handleError(err);
}
);
}

Cannot assign Object[] to Observable<Object[]>

I'm currently bumbling my way through an Angular 4 project. I've manageed to overcome most errors myself, so far, however I cannot figure out this one.
I am trying to use *ngFor (async) to display a list of Observable objects.
However, I get the error "Cannot assign Course[] to Observable< Course[] >", however I feel like my service is returning an Observable< Course[] >.
course-list.component.ts:
import { Component, OnInit } from '#angular/core';
import { Http } from '#angular/http';
import { CourseCardComponent } from '../course-card/course-card.component';
import { CourseCardService } from '../course-card/course-card.service';
import { CourseCard } from '../course-card/course-card.model';
import { Observable } from 'rxjs/Observable';
#Component({
selector: 'app-course-list',
templateUrl: './course-list.component.html',
styleUrls: ['./course-list.component.css']
})
export class CourseListComponent implements OnInit {
courseCards : Observable<CourseCard[]>;
loaded = false;
constructor(private http:Http, private coursecardService:CourseCardService) { }
ngOnInit() {
this.coursecardService.getCourses()
.subscribe(
courses => {
this.courseCards = courses;
console.log(this.courseCards);
this.loaded = true;
},
err => {
console.log("Error", err);
}
)
}
}
course-card.service.ts
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';
import { CourseCard } from './course-card.model';
#Injectable()
export class CourseCardService {
// Returns this JSON data:
// [{"firstName":"Jane"},{"firstName":"John"}]
private URL = '/api/getcourses';
constructor (private http: Http) {}
getCourses(): Observable<CourseCard[]> {
return this.http.get(this.URL)
.map((response) => {
let data = response.text() ? response.json():[{}];
if(data) {
return data;
}
}
)
.catch((error:any) => Observable.throw(error.json().error || 'Server error'));
}
}
And the HTML for the course-list component
Courses
<ul>
<li *ngFor="let course of courses|async">
<app-course-card [name]='course.name' [wordcount]=0></app-course-card>
</li>
</ul>
This part does return an Observable<CourseCard[]>:
this.coursecardService.getCourses()
But then you are manually subscribing to it, and inside of the subscribe, courses is of type CourseCard[]. So when you try to assign this.courseCards = courses;, that's when you're getting the type mismatch.
The async pipe will do the subscription for you, so you can change your code to:
ngOnInit() {
this.courseCards = this.coursecardService.getCourses();
}
Nevermind, I read more about the .subscribe method. It returns a subscription object, I just needed to change it to:
ngOnInit() {
this.courseCards = this.coursecardService.getCourses();
}
Is your list properties name is correct? let course of courses or supposed to be
let course of courseCards?
<ul>
<li *ngFor="let course of courseCards|async">
<app-course-card [name]='course.name' [wordcount]=0></app-course-card>
</li>
</ul>
Try like this :
getCourses(): Observable<CourseCard[]> {
return this.http.get(this.URL)
.map((response) => <CourseCard[]>response.json())
.catch((error: any) => Observable.throw(error.json().error || 'Server error'));
}

Angular 2 binding not updating after async operation

In my Angular 2 app, I want to start by loading a number of SVGs, before starting the app proper. To do this, I first load a list of svg locations from the server, then fetch each one in turn.
I have a 'loading' property on my AppComponent thats controlling a couple of ngIfs to show/hide some Loading text. Problem is, once the svgs are all loaded, Angular doesn't update the binding in AppComponent.
Why is this? I thought zones took care of this?
The SvgLoader
import {Injectable, Output, EventEmitter, NgZone} from 'angular2/core';
import {Http, Response} from 'angular2/http';
import 'rxjs/Rx';
import {Observable} from 'rxjs/Observable';
const SVG_LIST:string = 'svg/list.json';
#Injectable()
export class SvgLoader {
#Output() state: EventEmitter<string> = new EventEmitter();
private svgs:{[file:string]:string};
constructor(private http:Http){
this.svgs = {};
}
getSvg(path:string):string {
return this.svgs[path];
}
load():void {
this.http.get(SVG_LIST)
.map(res => {
return <Array<string>> res.json().files;
})
.flatMap((files) => Observable.forkJoin(files.map(file => {
return this.http.get('svg/' + file);
})))
.catch(this.handleError)
.mergeAll()
.subscribe(
res => {
let index = res.url.indexOf('svg');
let path = res.url.substring(index);
this.svgs[path] = res.text();
},
error => console.error(error),
() => {
this.state.emit('loaded');
}
);
}
private handleError(error:Response) {
console.error(error);
return Observable.throw(error.json().error || 'Server error');
}
}
AppComponent
export class AppComponent {
// On start, list the game sessions
private state:string;
public loading:boolean;
constructor(private svgLoader:SvgLoader){
this.loading = true;
this.state = 'joining';
}
ngOnInit():void {
this.svgLoader.state.subscribe(this.loaded);
this.svgLoader.load();
}
loaded():void {
console.log('loaded');
this.loading = false;
}
}
The template
<div>
<h1 *ngIf="loading">Loading...</h1>
<div *ngIf="!loading">
Loaded
</div>
</div>

Categories

Resources