Lesson Tuesday

So far we've seen a few different ways to make an API call. However, all of our code is still in one file. Not only does that make this code hard to read, it also makes it impossible for one developer to work on the UI while another works on the business logic. Most of time developers work on teams, so it's important to write your code such that it can be easily hooked up with the rest of the team's code.

In this lesson, we'll walk through how to separate UI and business logic while using a Javascript Promise to make our API call. If you would like to see how to separate your logic when using the JQuery method, check out this tutorial written by Epicodus graduate Jared Eisemann (CSS/Design Oct. 2017). Pay close attention, though, as the tutorial does not use ES6 syntax.

Identify UI Logic and Business Logic

Let's return to the Weather API example from this lesson on promises. The code looks like this:

weather-interface.js
// UI LOGIC BEGIN
$(document).ready(function() {
  $('#weatherLocation').click(function() {
    let city = $('#location').val();
    $('#location').val("");
// UI LOGIC END

// BUSINESS LOGIC BEGIN
    let promise = new Promise(function(resolve, reject) {
      let request = new XMLHttpRequest();
      let url = `http://api.openweathermap.org/data/2.5/weather?q=${city}&appid=[API-KEY-GOES-HERE]`;
      request.onload = function() {
        if (this.status === 200) {
          resolve(request.response);
        } else {
          reject(Error(request.statusText));
        }
      }
      request.open("GET", url, true);
      request.send();
    });
// BUSINESS LOGIC END

// UI LOGIC BEGIN
    promise.then(function(response) {
      let body = JSON.parse(response);
      $('.showHumidity').text(`The humidity in ${city} is ${body.main.humidity}%`);
      $('.showTemp').text(`The temperature in Kelvins is ${body.main.temp} degrees.`);
    }, function(error) {
      $('.showErrors').text(`There was an error processing your request: ${error.message}`);
    });
  });
// UI LOGIC END

});

The code above is labeled as either UI logic or business logic. We want to move any code that does not deal with the UI to another file. Primarily, UI logic is any code that gathers information from the user or displays information to the user.

Move Business Logic to New Class

But we can't simply move the business logic snippet to another file without breaking our app. We'd get an error 'promise' is not defined, since the variable promise would be declared in a separate file. So how do we get around this?

The simplest way to do this is to return the Promise object we want to create from our business logic. We'll make a new file called weather-service.js. It will hold business logic for making the API call. It will look like this:

weather-service.js
export class WeatherService {
  getWeatherByCity(city) {
    return new Promise(function(resolve, reject) {
      let request = new XMLHttpRequest();
      let url = `http://api.openweathermap.org/data/2.5/weather?q=${city}&appid=[API-KEY-GOES-HERE]`;
      request.onload = function() {
        if (this.status === 200) {
          resolve(request.response);
        } else {
          reject(Error(request.statusText));
        }
      }
      request.open("GET", url, true);
      request.send();
    });
  }
}

In the code above we create a new class called WeatherService. It has one method, getWeatherByCity(), which takes one parameter, the name of the city the user inputs. As you can see, the code in this method is the exact same as code originally in weather-interface.js that defined the promise variable, with the addition of a return statement.

Restructure UI Logic

Our UI without the business logic now looks like this:

weather-interface.js
$(document).ready(function() {
  $('#weatherLocation').click(function() {
    let city = $('#location').val();
    $('#location').val("");

    let promise = // code moved to _weather-service.js_

    promise.then(function(response) {
      let body = JSON.parse(response);
      $('.showHumidity').text(`The humidity in ${city} is ${body.main.humidity}%`);
      $('.showTemp').text(`The temperature in Kelvins is ${body.main.temp} degrees.`);
    }, function(error) {
      $('.showErrors').text(`There was an error processing your request: ${error.message}`);
    });
  });

});

Now, obviously we need to give our promise variable a value. Since we know the WeatherService class has an instance method that returns the promise we want, let's set the promise variable in weather-interface.js to a call to this method.

First, since it is an instance method, we need to create an instance of the WeatherService class. Then, we need to call the method on that class and pass in the city name we gather from the user:

weather-interface.js
$(document).ready(function() {
  $('#weatherLocation').click(function() {
    let city = $('#location').val();
    $('#location').val("");


    let weatherService = new WeatherService();  // create instance of WeatherService class
    let promise = weatherService.getWeatherByCity(city);  // call the instance method and pass in user input

    promise.then(function(response) {
      body = JSON.parse(response);
      $('.showHumidity').text(`The humidity in ${city} is ${body.main.humidity}%`);
      $('.showTemp').text(`The temperature in Kelvins is ${body.main.temp} degrees.`);
    }, function(error) {
      $('.showErrors').text(`There was an error processing your request: ${error.message}`);
    });
  });

});

We set the promise variable equal to the Promise object returned from getWeatherByCity(). This means we can still call then() on it like before.

And that's the basic strategy for separating logic! Being able to quickly and neatly separate code by concerns is a vital skill as a programmer, so make sure you understand the concepts behind why and how we should separate logic. Above, we used a very common pattern. We identified which code was business logic, allocated that code to a method on another class, then captured the return value of that method with a variable so we could access that information.