JackSlayer94
JackSlayer94

Reputation: 855

Angular 4 map certain JSON data to class and return observable

So I am trying to learn some basic Angular by creating an application that fetches and displays the current weather of a location using OpenWeather API.

This is what I have in my code currently:

app.component.ts:

import { Component } from '@angular/core';
import { WeatherService } from './weather.service';    
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
  providers: [WeatherService]
})    
export class AppComponent {
  title = 'Ng-Weather';
  cityName: string;
  weather: Weather;    
  constructor(private weather: WeatherService) { }    
  search() {
    this.weather.getWeatherbyName(this.cityName)
      .subscribe(res => this.weather = res);
    console.log(this.weather);
  }
}

weather.service.ts:

import { Injectable } from '@angular/core';
import { Http, Response, URLSearchParams } from '@angular/http';
import { Observable } from 'rxjs';
import { Weather } from './weather';
@Injectable()
export class WeatherService {
  APIurl = "http://api.openweathermap.org/data/2.5/weather";
  Appid = "xxx";
  weather: Weather;
  constructor(private http: Http) { }
  getWeatherbyName(name: string): Observable<any> {
    let myParams = new URLSearchParams();
    myParams.append('appid', this.Appid);
    myParams.append('q', name);
    return this.http.get(this.APIurl , { search: myParams} )
      .map(this.extractData)
      .catch(this.handleError);
  }
  private extractData(res: Response) {
    let body = res.json();
    this.weather.city = body.name;
    this.weather.description = body.weather[0].main;
    this.weather.temp = body.main.temp;
    console.log(this.weather);
  }
  private handleError(error: Response | any) {
    console.error(error.message || error);
    return Observable.throw(error.message || error);
  }
}

weather.ts:

export class Weather {
    city: String;
    description: String;
    temp: String;
}

So basically I am trying to map a JSON returned from OpenWeather API and get only some parts of the data and not the whole thing. The JSON returned is like following:

{  
    "coord":{  
        "lon":80.28,
        "lat":13.09
    },
    "weather":[  
        {  
            "id":802,
            "main":"Clouds",
            "description":"scattered clouds",
            "icon":"03n"
        }
    ],
    "base":"stations",
    "main":{  
        "temp":303.15,
        "pressure":1008,
        "humidity":79,
        "temp_min":303.15,
        "temp_max":303.15
    },
    "visibility":6000,
    "wind":{  
        "speed":3.1,
        "deg":210
    },
    "clouds":{  
        "all":40
    },
    "dt":1504805400,
    "sys":{  
        "type":1,
        "id":7834,
        "message":0.0017,
        "country":"IN",
        "sunrise":1504744074,
        "sunset":1504788314
    },
    "id":1264527,
    "name":"Chennai",
    "cod":200
}

When the above code is executed, I get this error:

weather.service.ts:32 Cannot set property 'city' of undefined

Also how do I return an observable of type Weather and return that variable weather and catch it on the app.component.ts?

Upvotes: 0

Views: 8609

Answers (1)

DeborahK
DeborahK

Reputation: 60548

You are not creating an instance of the weather object before assigning its properties. You can do that explicitly like this:

this.weather = new Weather();
this.weather.city = body.name;
this.weather.description = body.weather[0].main;
this.weather.temp = body.main.temp;
console.log(this.weather);

OR

You can do something like this:

this.weather = { 
                 city: body.name,
                 description: body.weather[0].main,
                 temp: body.main.temp
               }
console.log(this.weather);

And to answer the second part of your question, you should be able to do this:

  getWeatherbyName(name: string): Observable<Weather> {
      // your other code
  }

  private extractData(res: Response) {
    // your other code
    return this.weather;
  }

And to answer the third part of your question ... Observables are asynchronous. This means that they do not immediately return a value. Rather they provide for definition of a callback function that is executed when the data is returned. That means that the data is undefined until the data is returned and the callback function is executed.

So if you want to access the returned data in your code, you need to do in WITHIN the callback function. Like this:

  search() {
    this.weather.getWeatherbyName(this.cityName)
      .subscribe(res => {
             this.weather = res;
             console.log(this.weather);
      });
  }

Upvotes: 5

Related Questions