I'trying to use the HTTP service in Angular2 and i have some concerns.
I'm taking meteo datas from openweather API and I just want to put it inside a typeScript variable (meteo: {}) and use it as i want in my template.
Here are my .ts files:
meteo.service.ts
import {Injectable} from "angular2/core";
import {Http, Response} from "angular2/http";
import {Observable} from "rxjs/Observable";
import {MeteoComponent} from "../widgets/meteo/meteo.component";
import {Meteo} from "../widgets/meteo/meteo";
#Injectable()
export class MeteoService {
constructor(private http: Http) {}
// Nom de la ville sans accent
private _ville = 'Montreal';
// Initiales du pays
private _country = 'ca';
// Units (metric/imperial)
private _units = 'metric';
// API KEY
private _APPID = 'ewfw54f5646';
// url to get data
private _meteoUrl = 'http://api.openweathermap.org/data/2.5/weather?q='+this._ville+','+this._country+'&units='+this._units+'&APPID='+this._APPID;
getMeteo (): Observable<Meteo> {
return this.http.get(this._meteoUrl)
.map(this.extractData)
.catch(this.handleError);
}
private extractData(res: Response) {
if(res.status < 200 || res.status >= 300) {
throw new Error('Bad response status: ' + res.status);
}
let body = res.json();
return body || { };
}
private handleError(error: any) {
let errMsg = error.message || 'server error';
console.error(errMsg);
return Observable.throw(errMsg);
}
}
meteo.component.ts
import {Component, OnInit, OnChanges, AfterContentInit} from "angular2/core";
import {MeteoService} from "../../services/meteo.service";
import {Meteo} from "./meteo";
#Component({
selector: 'meteo',
templateUrl: 'dev/widgets/meteo/meteo.component.html',
providers: [MeteoService]
})
export class MeteoComponent implements OnInit {
errorMessage: string;
meteo: Meteo;
// We inject the service into the constructor
constructor (private _meteoService: MeteoService) {}
// Instantiate data in the ngOnInit function to keep the constructor simple
ngOnInit() {
this.getMeteo();
}
getMeteo() {
this._meteoService.getMeteo()
.subscribe(
data => this.meteo = data,
error => this.errorMessage = <any>error);
}
}
meteo.ts
export class Meteo {
data: {};
}
and meteo.component.html
<span class="meteo">{{meteo | json}}°C</span>
Actually the result is the entire json object:
{
"coord": {
"lon":-73.59,
"lat":45.51
},
"weather":[
{
"id":803,
"main":"Clouds",
"description":"broken clouds",
"icon":"04d"
}
],
"base":"cmc stations",
"main":{
"temp":3.96,
"pressure":1020,
"humidity":32,
"temp_min":2,
"temp_max":6.67
},
"wind":{
"speed":2.1
},
"clouds":{
"all":75
},
"dt":1461594860,
"sys":{
"type":1,
"id":3829,
"message":0.004,
"country":"CA",
"sunrise":1461577807,
"sunset":1461628497
},
"id":6077243,
"name":"Montreal",
"cod":200
}
And I would like to display just the temp field.
If you have any idea guys it's welcomed!
Thanks a lot.
You could leverage the Elvis operator since your data are loaded asynchronously:
<span class="meteo">{{meteo?.main.temp | json}}°C</span>
Try setting the data on this.meteo.data
getMeteo() {
this._meteoService.getMeteo()
.subscribe(
data => this.meteo.data = data,
error => this.errorMessage = <any>error);
}
and then displaying it with
<span class="meteo">{{meteo.data.main.temp}}°C</span>
Related
I am working with FirestoreDocument and I am trying to retrieving document data.I am getting the post undefined.Below is my code for service and Component
PostService.ts
import { Injectable } from '#angular/core';
import { AngularFirestore,AngularFirestoreCollection,AngularFirestoreDocument} from 'angularfire2/firestore'
import {Post} from './post';
import { Observable } from 'rxjs';
import 'rxjs/add/operator/map';
#Injectable()
export class PostService {
postsCollection: AngularFirestoreCollection<Post>
postDoc: AngularFirestoreDocument<Post>
constructor(private afs:AngularFirestore) {
this.postsCollection = this.afs.collection('posts',ref => ref.orderBy('published','desc'));
}
getPosts(){
return this.postsCollection.snapshotChanges().map(actions=>{
return actions.map(a=>{
const data=a.payload.doc.data() as Post
const id=a.payload.doc.id
return {id, ...data}
})
})
}
getPostData(id: string){
this.postDoc=this.afs.doc<Post>(`posts/${id}`)
return this.postDoc.valueChanges()
}
}
PostDetailComponent.ts
import { PostService } from './../post.service';
import { Component, OnInit } from '#angular/core';
import {ActivatedRoute} from '#angular/router';
import {Post} from '../post';
#Component({
selector: 'app-post-detail',
templateUrl: './post-detail.component.html',
styleUrls: ['./post-detail.component.css']
})
export class PostDetailComponent implements OnInit {
post: Post
constructor(
private route:ActivatedRoute,
private postService:PostService
) { }
ngOnInit() {
this.getPost();
console.log(this);
}
getPost(){
const id= this.route.snapshot.paramMap.get('id')
return this.postService.getPostData(id).subscribe(data=>this.post=data)
}
}
PostDetailComponent from console
PostDetailComponent {route: ActivatedRoute, postService: PostService}
post:undefined
postService:PostService {afs: AngularFirestore, postsCollection: AngularFirestoreCollection, postDoc: AngularFirestoreDocument}
route:ActivatedRoute {url: BehaviorSubject, params: BehaviorSubject, queryParams: BehaviorSubject, fragment: BehaviorSubject, data: BehaviorSubject, …}
__proto__:Object
My post from PostDetailComponent is undefined.
The rest of your code looks right, I think it's just that your console.log(this) is in the wrong spot.
ngOnInit() {
this.getPost(); <----- your getPost() function is asynchronous
console.log(this); <----- meaning this console.log runs before you get data from Firebase
so when this runs, posts is still undefined.
If you would console.log(this) *AFTER* you get data
from Firebase, then you should see the values OK.
}
getPost(){
const id= this.route.snapshot.paramMap.get('id')
return this.postService.getPostData(id).subscribe(data=>this.post=data)
^^^^^^^^^^^
ASYNC portion is right here
}
To fix it, move your console.log inside your subscription:
ngOnInit() {
this.getPost();
<--- From here
}
getPost(){
const id= this.route.snapshot.paramMap.get('id')
return this.postService.getPostData(id).subscribe(data=> {
this.post=data;
console.log(this); <--- To here
});
}
EDIT - Additional troubleshooting ref comments below.
getPost(){
const id= this.route.snapshot.paramMap.get('id')
console.log('id from route params is: ' + id); <---- log the *id* too
return this.postService.getPostData(id).subscribe(data=> {
this.post=data;
console.log(data); <--- log *data* instead
});
}
Since angular 6 there were changes on rxjs
// replace import 'rxjs/add/operator/map';
with import { map } from 'rxjs/operators';
Then include .pipe() inside PostService.ts file in getPosts() function
like this:
getPosts() {
return this.postCollection.snapshotChanges().pipe(map(actions => {
return actions.map( a => {
const data = a.payload.doc.data() as Post
const id = a.payload.doc.id
return { id, ...data }
})
})
)
}
Home someone helps, cheers !!!
I have created an api using ASP .net WebApi to get a list of companies and get a single company. Service call GetCompanies works fine gets the data and prints the list. But, issues is with GetCompany service, it gets the company when I print it in log, but it does not get in the Company object. What am I doing wrong in the Angular Component and or Service. Any help is appreciated.
Here is the output of the application. GetCompanies lists all the companies, but GetCompany prints as [object Object]. . here is the output
Here is the screen shot of data coming from APIs.
This is companies.component.ts
import { Component, OnInit } from '#angular/core';
import { CompaniesService } from './companies.service';
import { Company } from './company.model';
#Component({
selector: 'app-companies',
template: `
<p>
company name = {{company}}
</p>
<ul>
<li *ngFor = "let c of companies"> {{c.Name}} - {{c.CompanyID}} </li>
</ul>
`
})
export class CompaniesComponent implements OnInit {
text: string;
errorMessage: string;
public company: Company;
public companies: Company[];
constructor(private cService: CompaniesService) { }
ngOnInit() {
this.getCompanies();
this.getCompany(5);
console.log(this.company);
}
getCompanies() {
return this.cService.getCompanies()
.subscribe(companies => this.companies = companies,
error => this.errorMessage =<any>error);
}
getCompany(id: number) {
return this.cService.getCompany(id)
.subscribe(company => this.company = company,
error => this.errorMessage =<any>error);
}
}
This is companies.service.ts
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import { Headers, RequestOptions } from '#angular/http';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';
import { Company } from './company.model';
#Injectable()
export class CompaniesService {
constructor(private http: Http) {
}
getCompany(id: number): Observable<Company> {
return this.http.get(`api/getcompany/?id=${id}`)
.map ((res:Response) => res.json() )
.catch(this.handleError) ;
}
getCompanies(): Observable<Company[]> {
return this.http.get('api/getcompanies')
.map ((res:Response) => res.json() )
.catch(this.handleError) ;
}
private extractData(res: Response) {
let body = res.json();
return body.data || [];
}
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);
}
}
code of company.model.ts
export class Company {
CompanyID: number;
Name: string;
Description: string;
EmailAddress: string;
Phone: string;
Address: string;
CreatedBy: number;
CreatedDate: Date;
UpdatedBy: number;
UpdatedDate: Date;
IsActive: boolean;
}
As you get data asynchronously you can use safe navigation operator like:
{{ company?.Name }}
I'm trying to make http call and if there will be any error do my things.
Here is my code:
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/throw';
// Operators
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/distinctUntilChanged';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/switchMap';
import 'rxjs/add/operator/toPromise';
#Injectable()
export class HttpCallService {
constructor(private http: Http) { }
getHeroes(): Observable<Hero[]> {
console.log('entered');
return this.http.get('Url')
.map(this.extractData)
.catch(this.handleError);
}
private extractData(res: Response) {
console.log('extract entered');
let body = res.json();
return body.data || {};
}
private handleError(error: Response | any) {
console.log('error entered');
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);
}
}
export class Hero {
constructor(
public id: Date,
public author: string,
public text: string
) { }
}
When I call it it logs in console only entered which is in getHeroes. I know there will be error I haven't valid url but why it doesn't go to catch?
You need to invoke the subscribe() method to make actual HTTP call. Your method getHeroes() just declares that it'll return the Observable when someone will subscribe to it. That's why you only see the log from getHeroes() - extractData() and handleErrors() are not even invoked.
You need to do getHeroes().subscribe() somewhere in your code.
An angular2 app, try to register an email.
import {Component, Directive, provide, Host} from '#angular/core';
import {NG_VALIDATORS, NgForm} from '#angular/forms';
import {ChangeDetectorRef, ChangeDetectionStrategy} from '#angular/core';
import {ApiService} from '../../services/api.service';
import {actions} from '../../common/actions';
import {EmailValidator} from '../../directives/email-validater.directive';
import * as _ from 'lodash';
import * as Rx from 'rxjs';
#Component({
selector: 'register-step1',
directives: [EmailValidator],
styleUrls: ['app/components/register-step1/register.step1.css'],
templateUrl: 'app/components/register-step1/register.step1.html'
})
export class RegisterStep1 {
email: string;
userType: number;
errorMessage: string;
successMessage: string;
constructor(private _api: ApiService, private ref: ChangeDetectorRef) {
this.successMessage = 'success';
this.errorMessage = 'error';
}
submit() {
var params = {
email: this.email,
type: +this.userType
};
params = {
email: '1#qq.com',
type: 3
};
this._api.query(actions.register_email, params).subscribe({
next: function(data) {
if(data.status) {
console.log("success register");
this.successMessage = "ok ,success";
console.log(this.errorMessage, this.successMessage);
}else{
this.errorMessage = data.message;
console.warn(data.message)
}
},
error: err => console.log(err),
complete: () => console.log('done')
});
}
}
my ApiService is simple:
import {Injectable} from '#angular/core';
import {Http, Headers, RequestOptions} from '#angular/http';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/toPromise';
import {AjaxCreationMethod, AjaxObservable} from 'rxjs/observable/dom/AjaxObservable';
import {logError} from '../services/log.service';
import {AuthHttp, AuthConfig, AUTH_PROVIDERS} from 'angular2-jwt';
#Injectable()
export class ApiService {
_jwt_token:string;
constructor(private http:Http) {
}
toParams(paramObj) {
let arr = [];
for(var key in paramObj) {
arr.push(key + '=' + paramObj[key]);
}
return arr.join('&')
}
query(url:string, paramObj:any) {
let headers = new Headers({'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'});
let options = new RequestOptions({headers: headers});
return this.http.post(url, this.toParams(paramObj), options).map(res=>res.json())
}
}
this is my html :
<form #f="ngForm">
usertype<input type="text" name="userType" [(ngModel)]="userType"><br/>
<input type="text" name="email" ngControl="email" email-input required [(ngModel)]="email">
<button [disabled]="!f.form.valid" (click)="submit(f.email, f.userType)">add</button>
</form>
{{f.form.errors}}
<span *ngIf="errorMessage">error message: {{errorMessage}}</span>
<span *ngIf="successMessage">success message: {{successMessage}}</span>
I can success send the api to server and received response, I subscribe an observer to the http response which is a Observable object, inner the next function, I console.log() my successMessage, but i got 'undefined', and when I change the successMessage my html has no change.
It seems like I have lost the scope of my component, then I can't use this keyword
That's because you use the function keyword inside TypeScript. Never do this. Always use the arrow notation () => {}.
You should change your next function to:
next: (data) => {
if(data.status) {
console.log("success register");
this.successMessage = "ok ,success";
console.log(this.errorMessage, this.successMessage);
}else{
this.errorMessage = data.message;
console.warn(data.message)
}
I'm starting with angular2. And I try to get data from an php script.
I followed the turorial at the angular docs. But I recently get this confusing error messeage:
zone.js:463 ViewWrappedException {_wrapperMessage: "Error in app/components/catch-data/catch-data.component.html:7:5", _originalException: TypeError: Cannot read property 'name' of undefined
at DebugAppView._View_CatchDataComponent0.de…, _originalStack: "TypeError: Cannot read property 'name' of undefine…t/node_modules/#angular/core/core.umd.js:9996:18)", _context: DebugContext, _wrapperStack: "Error: Error in app/components/catch-data/catch-da…tChangesInternal (AppComponent.template.js:121:8)"}
containing this message:
TypeError: Cannot read property 'name' of undefined
at DebugAppView._View_CatchDataComponent0.detectChangesInternal (CatchDataComponent.template.js:62)
at DebugAppView.AppView.detectChanges (core.umd.js:9996)
at DebugAppView.detectChanges (core.umd.js:10084)
at DebugAppView.AppView.detectViewChildrenChanges (core.umd.js:10016)
at DebugAppView._View_CatchDataComponent_Host0.detectChangesInternal (CatchDataComponent_Host.template.js:36)
at DebugAppView.AppView.detectChanges (core.umd.js:9996)
at DebugAppView.detectChanges (core.umd.js:10084)
at DebugAppView.AppView.detectContentChildrenChanges (core.umd.js:10011)
at DebugAppView._View_AppComponent0.detectChangesInternal (AppComponent.template.js:121)
at DebugAppView.AppView.detectChanges (core.umd.js:9996)
I have no idea where this error comes from. Here is my code:
model.service.ts
import { Injectable } from '#angular/core';
import { Http, Headers } from '#angular/http';
import { Model } from '../../class/model/model';
#Injectable()
export class ModelService
{
constructor(private http: Http){}
private modelUrl = '../../server/clientFunc/getModel.php';
getModel (): Promise<Model> {
return this.http.get(this.modelUrl).toPromise().then(response => response.json()).catch(this.handleError);
}
private handleError(error: any) {
console.error('An error occurred', error);
return Promise.reject(error.message || error);
}
}
catch-data.component.ts
import { Component, OnInit } from '#angular/core';
import { Router } from '#angular/router-deprecated';
import {Model} from '../../class/model/model';
import {ModelService} from '../../services/model/model.service';
#Component({
selector: 'catch-data',
templateUrl: 'app/components/catch-data/catch-data.component.html',
providers: [ModelService]
})
export class CatchDataComponent implements OnInit
{
constructor(private modelService:ModelService) {
}
model:Model;
errorMessage:string;
testItem = "Test-Item";
ngOnInit():any {
this.getModel();
}
getModel() {
this.modelService.getModel()
.then(response => {
this.model = new Model();
this.model.deserialize(response);
})
.catch(error => {
this.errorMessage = error;
console.log(error);
}); // TODO: Display
}
}
and the model.ts
export class Model
{
id:number;
name:string;
constructor(){
}
deserialize(object){
this.name = object.name;
this.id = object.id;
}
}
The template looks like:
<h1>Search and catch data</h1>
<h3>Model: {{testItem}}</h3>
<div>Name: {{model.name}}</div>
So as I could detect the CatchDataComponent gets null as I call getModel().
So in ngOnInit this is not null but a call later in getModel() this is null.
I have no Idea why this happens.
I hope you have an idea or any suggestions.
I think that you could use the Elvis operator:
<div>Name: {{model?.name}}</div>
because the model property is loaded asynchronously and not available when the model.name is evaluated at first...