Andrew Kim bio photo

Andrew Kim

A Full Stack Web Developer

Twitter Facebook Github Stackoverflow

When using AJAX to make client side calls to an API, we often just make the call and just string some jQuery soup along to manipulate the DOM in some way. Other times we use a front end framework to render views from the API and just trust that it’ll “just work”. What we’re going to be doing in this post, is hand rolling an object oriented approach to accessing data and rendering views from an API. The API we’ll be using is the Weather Underground API. I really like this API for teaching new developers because the endpoints are really simple and easy to use.

A general understanding of AJAX and javascript would be recommended for this blog post. In this post, we’ll be doing read only from a 3rd party API. Getting information from an API, converting it into a model in our application and then rendering a view with that model. It will be a client side only application.

Visit weather underground api and click Sign Up for FREE!

After signing up and validating email, agree to terms and sign in. Once there, click on pricing(don’t worry its free!). Make sure you click on the stratus plan(the free one, but still great!) and then click on purchase key. Fill out the form and purchase a key. If you then click on the documentation tab you’ll see a url somewhere in the middle of the page like this:

http://api.wunderground.com/api/<your key here>/conditions/q/CA/San_Francisco.json

Make sure you hold onto this key, we’ll need it later

If we visit that link we’ll see something like this:

If we look at this JSON object. We can see a whole bunch of useful information we may want for our application like temp_f and weather. You can also change the url with different cities and states and see all the information change. We want to encapsulate parts of this data into a JS object.

Let’s start building out our application now.

Folders and Files

In the terminal:

$ mkdir wunderground_oojs
$ cd wunderground_oojs
$ mkdir models
$ mkdir views
$ mkdir js
$ touch index.html
$ touch js/script.js
$ touch js/models/forecast.js
$ touch js/views/forecast.js

HTML Skeleton and Dependencies

Let’s first fill out the skeleton of our app. In index.html:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Weather.ly</title>
  <!-- load jquery dependency and all other script files in this app -->
  <script src="https://code.jquery.com/jquery-2.1.4.js"></script>
  <script src="js/models/forecast.js"></script>
  <script src="js/views/forecast.js"></script>
  <script src="js/script.js"></script>
</head>
<body>
  <!-- form containing 2 inputs to get a city and state, as well as submit button -->
  <form class="search">
    <input class="searchCity" type="text" name ="city" placeholder="City">
    <input class="searchState" type="text" name ="state" placeholder="State">
    <input type="submit" value="Get Weather">
  </form>
  <!-- empty div that will contain our forecast -->
  <div class="forecast">
  </div>
</body>
</html>

Forecast Model

Now that our skeleton’s all set up, let’s define our Forecast model. In js/models/forecast.js:

var Forecast = function(info){
  this.city = info.city;
  this.state = info.state;
}

This is a basic constructor function that accepts an object as an argument to populate some attributes for our Forecast objects. We’ll be supply these attributes by just filling out the form from our HTML and using jQuery to grab the values out of the form.

Well this is great, but wasn’t the whole point of this post to access data from a third party API? It is! Let’s extend the functionality of our Forecast constructor. In js/models/forecast.js:

var Forecast = function(info){
  this.city = info.city;
  this.state = info.state;
}

Forecast.prototype.loadForecast = function(){
  var self = this
  // url to hit the API endpoint, make sure to put your key in!
  var url ="http://api.wunderground.com/api/yourKeyInfoGoesHere/conditions/q/" + this.state + "/" + this.city + ".json"
  // makes the AJAX get request using $.getJSON() passing in the URL above
  var request = $.getJSON(url)
  // in this promise `.then` we'll set some attributes for a Forecast by drilling
  //  through the response we get back from the API
  .then(function(response){
    self.tempF = response.current_observation.temp_f
    self.iconUrl = response.current_observation.icon_url
    self.description = response.current_observation.weather
  })
  // finally we return the request because we need to be able to continue to
  // chain promises on the return of this function
  return request
}

Here we’re extending the functionality of our Forecast model to include the loadForecast() function. When we instantiate a new Forecast, we can call .loadForecast() on it to populate some more attributes from the API call.

Forecast View

Now we need an interface that will take instances of the model we’ve just defined to render a view. We’ll do this by creating another constructor function that abstracts all of the view functionality/interface to a JS object. In views/forecast.js:

var ForecastView = function(forecast){
  // set a Forecast model as a property of the view
  this.forecast = forecast;
  // sets the div with class forecast as the domain of this view
  this.$el = $(".forecast");
}

ForecastView.prototype = {
  // DOM manipulation using a Forecast
  forecastTemplate: function(forecast){
    var html = $("<div></div>");
    html.append("<h2>Weather For " + forecast.city + ", " + forecast.state + "</h2>");
    html.append("<img class='artist-photo' src='" + forecast.iconUrl + "'>");
    html.append("<p class='description'>" + forecast.description + "</p>")
    html.append("<p class='tempF'>Current Temp in Fahrenheit: " + forecast.tempF + "°</p>")
    return(html);
  },
  // function that changes the HTML of the $el(div with class forecast) to what was generated by the template
  render: function(){
    var self = this;
    self.$el.html(self.forecastTemplate(self.forecast).html());
  },
  // emptys all elements withing the $el (div with class forecast)
  clearContainer: function(){
    this.$el.empty()
  }
}

Tying it all together

Sweet! So basically, if we have a city and state, we can render a view based off the information we get from the Weatherunderground API. Well, it just so happens we have 2 inputs for a city and state in our index.html. We just need to grab the values from those input fields, create a new Forecast object then render the corresponding view. In js/script.js:

$(document).ready(function(){
  // sets a submit event to the element with class search(in our case the form)
  $(".search").on("submit", function(e){
    // prevents the default action, the default action here will cause a page refresh, which we don't want
    e.preventDefault()
    // grabs values from the the input fields and stores them to a variable
    var city = $(".searchCity").val()
    var state = $(".searchState").val()
    // creates a new forecast object using our Forecast constructor we define earlier.
    forecast = new Forecast({city: city, state: state})
    // calls .loadForecast to make the API call
    forecast.loadForecast().then(function(){
      // in the promise we create a new view passing in the forecast object
      view = new ForecastView(forecast)
      // empties forecast if one already exists
      view.clearContainer()
      // renders the new forecast
      view.render()
    })
  })
})

If we’ve done everything correctly to this point, we should be able to fill in a City and State and see something like this:


Wonderful! We have a nice little weather app we can run any time to get the current weather, how useful!

This is just a small example of how you could parse an API call into a JS object. In this app, if we weren’t going to change it at all going forward, jQuery soup is probably a better approach just because it’ll be much quicker to code. OOJS separates your concerns and makes it more modular. If I want to add another property, I just add it to the model definition. Let’s also imagine this is a view that needs to be generated in more than one place. Now we won’t have to duplicate any code we can just use our constructors to execute that functionality.