I'm 9 hours a day on Angular trying to make some little projects mainly with services. Today, I'm trying to make a service that loops on data's fetching and the components update themselves according to new data. I've like 6 components using the service and the standard way to do it makes 6 times more requests that only one component does.
I heard about IntervalObservable but I don't know how to implement it on the component side. (And maybe I failed in the service too ...)
Here is some code.
app.module.ts :
import { FormsModule } from '#angular/forms';
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { HttpModule } from '#angular/http';
import { AppComponent } from './app.component';
import { ROUTING } from './app.routes';
import {HardwareService} from "./services/hardware.service";
import {AfficheurComponent} from "./components/hardware/afficheur.component";
import {HardwareListComponent} from "./views/hardwarelist/hardwarelist.component";
#NgModule({
imports: [ BrowserModule, ROUTING, HttpModule, FormsModule],
declarations: [
AppComponent,
AfficheurComponent,
HardwareListComponent
],
bootstrap: [ AppComponent ],
providers: [ HardwareService ]
})
export class AppModule { }
hardware.service.ts :
import { Injectable } from '#angular/core';
import { Headers, Http, Response } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';
import 'rxjs/add/observable/interval'
#Injectable()
export class HardwareService{
private apiUrl = 'http://10.72.23.11:5000'; // URL to web API
constructor (private http: Http) {}
getHardware(){
return Observable.interval(5000)
.flatMap(() => {
return this.http.get(this.apiUrl)
.map(this.extractData)
.catch(this.handleError);
});
}
private extractData(res: Response) {
let body = res.json();
return body || { };
}
private handleError (error: Response | any) {
// In a real-world app, you might use a remote logging infrastructure
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);
}
}
afficheur.component.ts :
import { Component } from '#angular/core';
import {HardwareService} from "../../services/hardware.service";
#Component({
selector: 'afficheur',
templateUrl: 'app/components/hardware/afficheur.component.html'
})
export class AfficheurComponent{
public state: Boolean;
constructor(private service: HardwareService){
this.service
.getHardware()
.subscribe(data => (console.log(data), this.state = data.afficheur),
error => console.log(error),
() => console.log('Get etat afficheur complete'))
}
}
I took the information about IntervalObservable here (SO thread)
As always, I hope you'll be able to help me find my way through this problem :).
ERROR TypeError: Observable_1.Observable.interval is not a function
Regards, Jérémy.
(PS: English is not my mother language, don't hesitate to tell me if i told something you don't understand)
The solution would look something like:
// create an observable which fetch the data at intervals of 1 second
this._data$ = Observable
.timer(0, 1000)
.switchMap(() => this.getData())
// if an error is encountered then retry after 3 seconds
.retryWhen(errors$ => {
errors$.subscribe(error => this.logError(error));
return errors$.delay(3000);
})
.share();
timer(0, 1000) - produce the first value after 0ms and then at intervals of 1 second. Using interval(1000) instead is ok but the first value will come with a delay of 1 second.
switchMap(() => this.getData()) - switch to the observable provided by the callback which queries the actual resource
retryWhen(...) - if an error is encountered then logs the error and then retries
share() - shares a single subscription among the subscribers. This has the effect of calling getData() only once, instead of calling it for as many subscribers we might have.
Example - emit current dates, when getData() is called 5th time in a row then an error is thrown in order to test also the error situation.
Here is the working Plunker.
HardwareService
import { Injectable } from '#angular/core';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/observable/of';
import 'rxjs/add/observable/timer';
import 'rxjs/add/observable/throw';
import 'rxjs/add/operator/delay';
import 'rxjs/add/operator/retry';
import 'rxjs/add/operator/retryWhen';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/share';
import 'rxjs/add/operator/switchMap';
import {Subject} from 'rxjs/Subject';
#Injectable()
export class HardwareService {
private _fetchCount = 0;
private _fetchCount$ = new Subject<number>();
private _data$: Observable<Date>;
public get data$(): Observable<Date> {
return this._data$;
}
public get fetchCount$(): Observable<number> {
return this._fetchCount$;
}
constructor() {
// create an observable which fetch the data at intervals of 1 second
this._data$ = Observable
.timer(0, 1000)
.switchMap(() => this.getData())
// if an error is encountered then retry after 3 seconds
.retryWhen(errors$ => {
errors$.subscribe(error => this.logError(error));
return errors$.delay(3000);
})
.share();
}
private logError(error) {
console.warn(new Date().toISOString() + ' :: ' + error.message);
}
private getData(): Observable<Date> {
this._fetchCount++;
this._fetchCount$.next(this._fetchCount);
// from time to time create an error, after 300ms
if (this._fetchCount % 5 === 0) {
return Observable.timer(300).switchMap(() => Observable.throw(new Error('Error happens once in a while')));
}
// this will return current Date after 300ms
return Observable.timer(300).switchMap(() => Observable.of(new Date()));
}
}
AfficheurComponent
import {Component, Input, OnInit} from '#angular/core';
import {HardwareService} from '../services/hardware.service';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/map';
#Component({
selector: 'app-afficheur',
templateUrl: './afficheur.component.html',
styleUrls: ['./afficheur.component.css']
})
export class AfficheurComponent implements OnInit {
#Input()
public label: string;
public data$: Observable<string>;
constructor(private hardwareService: HardwareService) {
this.data$ = hardwareService.data$.map(item => this.label + ' - ' + item.toISOString());
}
ngOnInit() {
}
}
AfficheurComponent template
<div style="margin-top: 10px;">{{ data$ | async }}</div>
Usage
<app-afficheur label="afficheur 1"></app-afficheur>
<app-afficheur label="afficheur 2"></app-afficheur>
<app-afficheur label="afficheur 3"></app-afficheur>
<app-afficheur label="afficheur 4"></app-afficheur>
<app-afficheur label="afficheur 5"></app-afficheur>
<app-afficheur label="afficheur 6"></app-afficheur>
<div style="margin-top: 10px">
Times called: {{ hardwareService.fetchCount$ | async }}
</div>
Related
when running ng serve am getting the following error, I just created new service , it was working okay but suddenly everything is down :(,
I tried veverything but I couldnt get the job done, google also didnt help :(
WARNING in ./src/app/Booking.service.ts
There are multiple modules with names that only differ in casing.
This can lead to unexpected behavior when compiling on a filesystem with other case-semantic.
Use equal casing. Compare these module identifiers:
* C:\Users\Bonge\Documents\Projects\bookingapp\booking-client\node_modules\#ngtools\webpack\src\index.js!C:\Users\Bonge\Documents\Projects\bookingapp\booking-client\src\app\Booking.service.ts
Used by 2 module(s), i. e.
C:\Users\Bonge\Documents\Projects\bookingapp\booking-client\node_modules\#ngtools\webpack\src\index.js!C:\Users\Bonge\Documents\Projects\bookingapp\booking-client\src\app\about\about.component.ts
* C:\Users\Bonge\Documents\Projects\bookingapp\booking-client\node_modules\#ngtools\webpack\src\index.js!C:\Users\Bonge\Documents\Projects\bookingapp\booking-client\src\app\booking.service.ts
Used by 2 module(s), i. e.
C:\Users\Bonge\Documents\Projects\bookingapp\booking-client\node_modules\#ngtools\webpack\src\index.js!C:\Users\Bonge\Documents\Projects\bookingapp\booking-client\src\app\app.module.ts
i 「wdm」: Compiled with warnings.
here is my app.module
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { FormsModule } from '#angular/forms';
import { JsonpModule } from '#angular/http';
import { RouterModule } from '#angular/router';
import { AppComponent } from './app.component';
import { HttpClientModule } from '#angular/common/http';
import { FlashMessagesModule } from 'angular2-flash-messages';
import { ReactiveFormsModule } from '#angular/forms';
import { AboutComponent } from './about/about.component';
import {CalendarModule} from 'primeng/calendar';
import { BookingService } from './booking.service';
#NgModule({
declarations: [
AppComponent,
AboutComponent
],
imports: [
BrowserModule,
JsonpModule,
CalendarModule,
ReactiveFormsModule,
FormsModule,
HttpClientModule,
FlashMessagesModule.forRoot(),
RouterModule.forRoot([
{ path: 'about', component: AboutComponent }
]),
],
providers: [BookingService],
bootstrap: [AppComponent]
})
export class AppModule { }
and here is my booking service.ts
import { Injectable } from '#angular/core';
import { Response } from '#angular/http';
import { catchError, map } from 'rxjs/operators';
import { Observable, throwError } from 'rxjs';
import { HttpClient, HttpHeaders, HttpErrorResponse } from '#angular/common/http';
const httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};
const apiUrl = 'http://localhost:8000/booking';
#Injectable({
providedIn: 'root'
})
export class BookingService {
bookingsUrl = '/booking';
addBookingsUrl = '/bookings';
constructor(private http: HttpClient) { }
// function to extract data from rensponse
private extractData(res: Response) {
// tslint:disable-next-line:prefer-const
let body = res;
return body || {};
}
// Return Booking
getBookings(id: string): Observable<any> {
const url = `${apiUrl + this.bookingsUrl}/${id}`;
return this.http.get(url, httpOptions).pipe(
map(this.extractData),
catchError(this.handleError));
}
// Adds Booking
addBooking(date, email, city, hotel): Observable<any> {
const uri = `${apiUrl + this.addBookingsUrl}`;
const obj = {
date: date,
email: email,
city: city,
hotel: hotel
};
return this.http.post(uri, obj);
}
// Errors Handler
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.');
}
}
what is wrong with my codes? any idea or suggestion will be apreciated , thanks
This is usually a result of a minuscule typo.
Check all your components,services,modules, If you are importing like import 'smallcase'
In your case you have not imported Rxjs
import { Observable } from 'Rxjs/Observable';
You are not importing rxjs packages properly change your code like this it will work
import { Injectable } from '#angular/core';
import { Response } from '#angular/http';
import { map } from 'rxjs/operators/map';
import { Observable } from 'Rxjs/Observable';
import { catchError,throwError } from 'rxjs/operators';
import { HttpClient, HttpHeaders, HttpErrorResponse } from '#angular/common/http';
In my case the issue was that i capitalized the name of a file where i shouldn't in one of my imports
I used
import { AuthService } from '../../Services/Auth.service';
instead of
import { AuthService } from '../../Services/auth.service';
Auth vs auth
I am trying to use Nodejs/Express as my back end for producing data from a database. I currently have an api route setup so that a database query will result in its directory. So if I visit localhost:3000/api currently I will see the following:
{"status":200,"data":[{"Issuer_Id":1,"Data_Id":2,"Data_Name":"Name 1"},{"Issuer_Id":2,"Data_Id":14,"Data_Name":"Name 2"},{"Issuer_Id":2,"Data_Id":1,"Data_Name":"Name 3"}],"message":null}
This leads me to believe I have everything setup correctly on the back end.
Now how do I get this data to display on my Angular front end?
I have been through hours of tutorials and this is what I have come up with:
nav.component.ts
import { Component, OnInit } from '#angular/core';
import { DataService } from '../../data.service';
import { Series } from '../../data.service';
import {Observable} from 'rxjs/Rx';
#Component({
selector: 'app-fixed-nav',
templateUrl: './fixed-nav.component.html',
styleUrls: ['./fixed-nav.component.css']
})
export class FixedNavComponent implements OnInit{
serieses: Series[] ;
constructor(private dataService: DataService) {}
ngOnInit() {
this.dataService.getSeries().subscribe((serieses: Series[]) => this.serieses = serieses);
}
}
data.service.ts
import { Injectable } from '#angular/core';
import { HttpClient } from'#angular/common/http';
import { Http, Headers, RequestOptions, Response } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/toPromise';
export class Series {
Issuer_Id: number;
Data_Id: number;
Data_Name: string;
}
#Injectable()
export class DataService {
constructor(private _http: Http) {}
getSeries(): Observable<Series[]> {
return this._http.get("http://localhost:3000/api/")
.map((res: Response) => res.json());
}
}
app.module.ts
import { Form1Module } from './modules/form1/form1.module';
import { FixedNavModule } from './modules/fixed-nav/fixed-nav.module';
import { HeaderModule } from './modules/header/header.module';
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { NgbModule } from '#ng-bootstrap/ng-bootstrap';
import { AppComponent } from './app.component';
import { HttpClientModule } from '#angular/common/http';
import { HttpModule } from '#angular/http';
import { DataService } from './data.service';
#NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
HttpModule,
HttpClientModule,
HeaderModule,
FixedNavModule,
Form1Module,
NgbModule.forRoot()
],
providers: [DataService],
bootstrap: [AppComponent]
})
export class AppModule { }
What do I need to enter in the nav.component.html to see the results?
Also note that when I refresh my angular page on lcoalhost:4200 I can see that the GET request is hitting the /apiu/ on the 3000 express server.
I am trying to help with best practices which might help get the intended result. I will amend this answer as we troubleshoot and hopefully arrive at the right answer.
So in your dataServices service I wanted to point out a couple things. Angular recommends we use the httpClient and not http and warn that http will soon be depreciated. I am fairly new to angular myself and have only ever used httpClient and have gotten great results so I recommend using that. I think this means that the promise that you are returned is changed too. Namely, you pust use a .pipe method inorder to use rxjs operators like map on the result. So this is what your dataService file would look like:
import { Injectable } from '#angular/core';
import { HttpClient } from'#angular/common/http';
import { Http, Headers, RequestOptions, Response } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import { map } from 'rxjs/operators';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/toPromise';
export class Series {
Issuer_Id: number;
Data_Id: number;
Data_Name: string;
}
#Injectable()
export class DataService {
constructor(private _http: HttpClient) {}
getSeries(): Observable<Series[]> {
return this._http.get<Series[]>("http://localhost:3000/api/")
.pipe(
map((res) => {
console.log(res);
return <Series[]> res
})
)
}
}
Note that I have imported map in a different rxjs/operators.
In actuality you dont even need to pipe or map the return since you have already declared the type of return in the get method of _http. HttpClient will cast the return into a Series[] for you so this one liner: return this._http.get("http://localhost:3000/api/") would work. I've written the code how it is however to console.log the return that your getting.
In the comments, could you tell me what is logged?
I am unable to correct your code I am providing my own setup Works for Me
In server.js
module.exports.SimpleMessage = 'Hello world';
Now in App.js
var backend = require('./server.js');
console.log(backend.SimpleMessage);
var data = backend.simpleMessage
In index html include App.js
<script src = '/App.js'></script>
alert(simpleMessage)
And you should get 'hello world'
My http-data.service accepts json for output in the component template. Initially, the console shows that the first few calls are given undefined, and the following calls are already taking json, but also if you check the component, then the component shows that the method that outputs the data to the component is called only once and since the data has not yet arrived it writes undefined , But not updated after the arrival of json. Help please understand why? Thank you
My http-data.service:
import {Injectable} from '#angular/core';
import {Http} from '#angular/http';
import {Response} from '#angular/http';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/map';
#Injectable()
export class HttpService{
constructor(private http: Http) {}
getDataOrganizations(): Observable<any[]>{
return this.http.get('http://localhost:3010/data')
.map((resp:Response)=>{
let dataOrganizations = resp.json().organization;
return dataOrganizations;
});
}
getDataModules(): Observable<any[]> {
return this.http.get('http://localhost:3010/data')
.map((resp: Response)=> {
let dataModules = resp.json().modules;
return dataModules;
});
}
getDataPresets(): Observable<any[]> {
return this.http.get('http://localhost:3010/data')
.map((resp: Response)=> {
let dataPresets = resp.json().presets;
return dataPresets;
});
}
getDataModuleItems(): Observable<any[]> {
return this.http.get('http://localhost:3010/data')
.map((resp: Response)=> {
let dataModuleItems = resp.json().module_items;
return dataModuleItems;
});
}
}
My data-all.service
import { Injectable, EventEmitter } from '#angular/core';
import {Response} from '#angular/http';
import { ModuleModel } from './model-module';
import { ModuleItemsModel } from './model-module-items';
import data from '../data/data-all';
import { PriceService } from './price.service';
import { HttpService } from './http-data.service';
#Injectable()
export class ModuleDataService {
constructor(private priceService: PriceService, private httpService: HttpService){
this.dataMinMaxSum = {minSum: 0, maxSum: 0}
}
private currentPopupView: EventEmitter<any> = new EventEmitter<any>();
private dataModules: ModuleModel[] = this.getDataModules();
private dataMinMaxSum: {};
private dataCalculateVariationOrg: any[];
private dataChangeExecutor: any[];
subscribe(generatorOrNext?: any, error?: any, complete?: any) {
this.currentPopupView.subscribe(generatorOrNext, error, complete);
}
calculte(){
return this.priceService.getDataPrice();
}
getDataModules(){
this.httpService.getDataModules().subscribe(((modules)=>{this.dataModules = modules; console.log(this.dataModules);}));
console.log('dataModules');
console.log(this.dataModules);
return this.dataModules;
}
---------------------------------------------------------------------------
}
My left-block.component
import { Component, OnInit} from '#angular/core';
import { ModuleDataService } from '../../service/data-all.service';
import { ModuleModel } from '../../service/model-module';
#Component({
moduleId: module.id,
selector: 'modules-left-block',
templateUrl: './modules-left-block.html',
styleUrls: ['modules-left-block.css']
})
export class ModuleLeft implements OnInit{
modules: ModuleModel[];
constructor(private modulesAll: ModuleDataService){}
ngOnInit(){
this.modules = this.modulesAll.getDataModules();
console.log("view");
console.log(this.modulesAll.getDataModules());
}
onToggle(module: any){
this.modulesAll.toggleModules(module);
}
}
My left-block.component.html
<div class="modules-all">
<div class="modules-all-title">Все модули</div>
<div class="module-item" *ngFor="let module of modules" [ngClass]="{ 'active': module.completed }" (click)="onToggle(module)">{{module?.title}}</div>
</div>
In the component this.modulesAll.getDataModules () method is why it is executed only once without updating (write in console => undefined), if there are any thoughts, write, thanks.
This behaviour is due to the .subscribe() method does not wait for the data to arrive and I'm guessing you already know this. The problem you're facing is because, you have .subscribe to the getDataModules() service in the wron place. You shouldn't subscribe to a service in another service (at leat in this case). Move the subscribe method to the left-block.component and it should work.
getDataModules() {
this.httpService.getDataModules().subscribe(((modules) => {
this.dataModules = modules;
console.log(this.dataModules);
}));
console.log('dataModules');
console.log(this.dataModules);
return this.dataModules;
}
It should look somethig like this:
#Component({
moduleId: module.id,
selector: 'modules-left-block',
templateUrl: './modules-left-block.html',
styleUrls: ['modules-left-block.css']
})
export class ModuleLeft implements OnInit {
modules: ModuleModel[] = new ModuleModel();
constructor(private modulesAll: ModuleDataService, private httpService: HttpService) {}
ngOnInit() {
this.getDataModles();
//this.modules = this.modulesAll.getDataModules();
console.log("view");
//console.log(this.modulesAll.getDataModules());
}
onToggle(module: any) {
this.modulesAll.toggleModules(module);
}
getDataModules(): void {
this.httpService.getDataModules().subscribe(((modules) => {
this.modules = modules;
console.log(this.dataModules);
}));
}
}
Today i'm facing a new problem with services.
I'm trying to make an http service but when I try to store, in my service, the Observable object returned by http.get.map - my app crashs.
I wanted to achieve a "system" where the service loops to update datas and the components which subscribed to the observable update its data according to the service's data.
Here is the code :
afficheur.component.ts :
import { Component } from '#angular/core';
import {HardwareService} from "../../services/hardware.service";
#Component({
selector: 'afficheur',
templateUrl: 'app/components/hardware/afficheur.component.html'
})
export class AfficheurComponent{
public state: Boolean;
constructor(private service: HardwareService){
this.service
.getHardware()
.subscribe(data => (console.log(data), this.state = data.afficheur),
error => console.log(error),
() => console.log('Get etat afficheur complete'))
}
}
hardware.service.ts :
import { Injectable, OnInit } from '#angular/core';
import { Headers, Http, Response } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';
#Injectable()
export class HardwareService implements OnInit{
private apiUrl = 'http://10.72.23.11:5000'; // URL to web API
private ressources: Observable<any>;
constructor (private http: Http) {}
ngOnInit() {
this.loopupdate()
}
loopupdate(): void {
setInterval(() => {
this.update();
}, 5000);
}
update(): void {
this.ressources = this.http.get(this.apiUrl)
.map(this.extractData)
.catch(this.handleError);
}
getHardware(){
return this.ressources;
}
private extractData(res: Response) {
let body = res.json();
return body || { };
}
private handleError (error: Response | any) {
// In a real world app, you might use a remote logging infrastructure
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);
}
}
app.module.ts :
import { FormsModule } from '#angular/forms';
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { HttpModule } from '#angular/http';
import { AppComponent } from './app.component';
import { ROUTING } from './app.routes';
import {HardwareService} from "./services/hardware.service";
import {AfficheurComponent} from "./components/hardware/afficheur.component";
import {HardwareListComponent} from "./views/hardwarelist/hardwarelist.component";
#NgModule({
imports: [ BrowserModule, ROUTING, HttpModule, FormsModule, HttpModule],
declarations: [
AppComponent,
AfficheurComponent,
HardwareListComponent
],
bootstrap: [ AppComponent ],
providers: [ HardwareService ]
})
export class AppModule { }
Thanks again for being here :D
EDIT :
I got an error when i try to launch my app :
ERROR TypeError: Cannot read property 'subscribe' of undefined
I think it's related to the this.ressources initialization, any idea ?
EDIT 2 :
In my service :
initializePolling(){
return IntervalObservable.create(5000)
.flatMap(() => {
return this.getHardware()
});
}
getHardware(): Observable<any> {
return this.http.get(this.apiUrl)
.map(this.extractData)
.catch(this.handleError);
}
How can i subscribe to this with my component ? I don't know what method i should call in my component to fetch datas without make multiple calls if i have multiple components.
The problem is that ngOnInit(), like the one in your Injectable class, is a Lifecycle hook which only works with Directives and Components. You should try calling this.loopUpdate() from within the Injectable class' constructor. You can know more about this on another thread/question.
If you want to set an interval in fetching the data, do that in the component class, not in the service. In the service you should just have methods that return Observables (in your case) from calling http.get().... In that way you wouldn't have an undefined object returned and a more reusable service.
Also, here's another SO link for you to have look at.
I have this function in my Angular 2 component, which calls Web Api:
getNextConjunctionApi(): Observable<any> {
return this._http.get(this.uri + '/GetNextConjunction')
.map((res: Response) => res.json());
}
Web Api returns a complex object, which I would like to map to an Angular 2 model called ClientModel:
export class ClientModel {
prop1: string;
prop2: string;
...
}
Can this mapping be done by rewriting the map functionality, or need I do it in some other way?
.map((res: Response) => res.json());
I accomplished this with a slightly different approach. I had my component call a service that would return an observable. My component could then use a specific type that I created. I will show you what I have done for a blog.
posts.component.ts
import { Component, OnInit } from '#angular/core';
import { PostsService } from './posts.service';
import { PostComponent } from '../post/post.component'; // --> This is my custom type
import { Observable } from 'rxjs/Rx';
#Component({
selector: 'app-posts',
templateUrl: './posts.component.html',
providers: [PostsService]
})
export class PostsComponent implements OnInit {
posts: Observable<PostComponent[]>; // --> I use the type here
constructor( private _postsService: PostsService ) { }
ngOnInit() {
this._postsService.getAllPosts()
.subscribe(
posts => { this.posts = posts }, // --> I add the return values here
error => { console.log(error) }
);
}
}
The above has three key pieces. I import the custom type, PostComponent, set posts to an Observable of type PostComponent array, and as the Observable comes back, I add the values to the posts array.
posts.service.ts
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import { Observable } from 'rxjs/Rx';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
#Injectable()
export class PostsService {
constructor( private _http: Http ) {}
getAllPosts(){
return this._http.get('[INSERT API CALL]')
.map((response: Response) => response.json())
.catch(msg => Observable.throw(msg));
}
}
In my service, I only map the response to response.json. This gives me more information than I need. I 'filter' it in my post.component
post.component.ts
import { Component, Input } from '#angular/core';
#Component({
selector: 'post',
templateUrl: './post.component.html'
})
export class PostComponent{
#Input() curPost: {
'id': number,
'title': string,
'author': string,
'date': string,
'body' : string,
};
constructor() { }
}