0

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?

1

1 Answer 1

5

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);
      });
  }
Sign up to request clarification or add additional context in comments.

3 Comments

Thank you! That helped me very much! Just wondering if you can help me catch in the component as well. I have this in my component: search() { this.weatherService.getWeatherbyName(this.cityName) .subscribe(res => this.weather = res); console.log(this.weather); } and I get an undefined when printing to console.
Can you add this to your question because the code does not format well in the comments section.
thanks! helped me understand more with the explanations rather than just the answer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.