ANGULAR JS – SHAPING UP (LEVEL 5)

1 Dependencies and Services

http://campus.codeschool.com/courses/shaping-up-with-angularjs/level/5/section/2/video/1

Our code’s getting long. Let’s clean it up by pulling all the product-related directives into their own file (products.js)—and they define “all” as productTitle, productGallery, and productPanels. I don’t have a productGallery directive. I’d better make one.

    app.directive('productGallery', function(){
        return{
            restrict: 'E',
            templateUrl: 'product-gallery.html',
            controller: function(){
                this.current = 0;
                this.setCurrent = function(imageNumber){
                    this.current = imageNumber || 0;
                };
            },
            controllerAs: 'gallery'
        };
    });

Good luck, me! Everything is still showing up on the page…except the gallery. But that’s better than nothing. And that’s probably because I’ve removed all that script and am not loading it on the page anyway!

1.1 A new module

Now we take this file, products.js and create a new module for it by wrapping all the code we have in the file currently in an IIFE, and then:

var app = angular.module('store-products' [ ]);

which, let’s compare that to the one we have in index:

var app = angular.module('gemStore', [ ]);

Since it’s wrapped in an IIFE, the two app variables are isolated from one another. But in the meantime, how do we get the two bits of isolated code to talk to one another? Like this:

var app = angular.module('gemStore', ['store-products']);

And of course we have to include the file in the HTML:

<a href="http://products.js">http://products.js</a>

And now everything works again. I’m still not sure why we didn’t move the other product-related directives into this module.

2 Challenges

Ah hah! It says we do put all the product-related directives into this module after all.

3 Services

Currently we’re loading all our product info from an array within our JavaScript file. It’s more likely that we’d be calling it from some external source. So, rather than this.products = gems, what would we be doing?

We need a service! I don’t know what that means. But Angular includes a bunch of services that can be used to give a controller additional functionality. Sounds like less work for me. Some of these include:
$http to get JSON data from a web service
$log to log messages to the console
$filter to filter an array

We’re going to focus on the $http service. Here’s two ways:

// 1
$http({ method: 'GET', url: '/products.json' });

// 2
$http.get(/'products.json', { apiKey: 'myApiKey' });

Both return a Promise object with .success() and .error(). Also, if we tell it to fetch JSON, it’ll automatically decode the results in JS objects and arrays.

We’re taking a break here, and we’ll be back at 2-minute mark in a moment.

3.1 Promises

http://jquery-part2.codeschool.com/levels/6/sections/2

Let’s begin with the idea that we’ll have a button to “get weather”, which, when clicked, performs an AJAX request and returns the weather.

<div class="favorite">
  <h3>Favorite</h3>
  <p class="loc">Paris, France</p>
  <p class="weather"></p>
  Show Weather
</div>
$('button').on('click', function(){
  // get location from the HTML
  var location = $('.loc').text();

  $.ajax('/weather', {
    data: {q: location},
    success: function(result){
      $('.weather').text(result.weather);
    }
  });
});

You click the button, it performs the AJAX call using the location from the HTML for the location, receives the weather as JSON, and displays it on the page.

What if we want to use the same information in multiple pages on the site? So here’s how we can use a promise:

// an object
var Weather = {
  // a function
  today: function(location){
    // ajax returns a promise object
    var promise = $.ajax('/weather', {
      data: {q: location}
  }); 
    return promise;
  }
}

$('button').on('click', function(){
  // get location from the HTML
  var location = $('.loc').text();
  // new variable
  var todayPromise = Weather.today(location);

  // jQuery will automatically call the done method if the promise is returned successfully...if we've defined a done method
  todayPromise.done(function(result){
    $('.weather').text(result.weather);
  });
});

They say this code is brittle because it requires us to call result.weather on the returned results, which means we have to know that there’s a weather function on the results. I don’t know why we wouldn’t know this, but okay. They’d prefer we just call weather instead.

3.2 Deferred

So now we’ll scrap this built in Promise stuff and build our own. Why? Because that’s how we’re going to learn about Deferred. See, now instead of having to remember to write result.weather, we can remember the significantly easier to remember EVERYTHING BELOW:

var Weather = {
  today: function(location){
    var promise = $.Deferred()
    $.ajax('/weather', {
      data: {q: location},
      // trigger Done and pass result.weather
      success: function(result){
        // promise.resolve triggers .done
        promise.resolve(result.weather)
      }
  }); 
    return promise;
  }
}

$('button').on('click', function(){
  var location = $('.loc').text();
  var todayPromise = Weather.today(location);

  // jQuery will automatically call the done method if the promise is returned successfully...if we've defined a done method
  todayPromise.done(function(result){
    $('.weather').text(result);
  });
});

So, in short: this is how you create a promise object in jQuery

var promise = $.Deferred();

and to do the Done callback, you can do this:

promise.resolve(value);

which automatically does this:

promise.done(function(value){});

Similarly, we could do

promise.reject(value);

which automatically does this:

promise.fail(function(value){});

And here it is in action:

var Weather = {
  today: function(location){
    var promise = $.Deferred()
    $.ajax('/weather', {
      data: {q: location},
      // trigger Done and pass result.weather
      success: function(result){
        // promise.resolve triggers .done
        promise.resolve(result.weather)
      },
      error: function(result){
        var error="invalid location";
        promise.reject(error);
      }
  }); 
    return promise;
  }
}

$('button').on('click', function(){
  var location = $('.loc').text();
  var todayPromise = Weather.today(location);

  // and now we're calling .fail similarly, which automatically would get the fail details from the promise and would print the error we wrote here.
  todayPromise.done(function(result){
    $('.weather').text(result);
  }).fail(function(error) {
    console.log(error)
  }
});

3.3 .When, .Then

We just wrote this. I don’t know why.

var City = {
  find: promise = $.ajax('/cities', {
    data: { loc: location }
  })
  return promise;
}

The goal is that we build something where you have a bunch of city names, and when you click on one, the code fetches some data about the city and displays it on the page.

Okay, well, now we’re getting into a situation where we have a lot of pages and the point it’s trying to make is that if you have multiple AJAX requests, they’ll run asynchronously and therefore return at different times, and in the case it’s presenting—they’ll load their responses up in a different order each time, which isn’t what we want. In this case, at least.

The answer is to use .when to bundle the promises up, wait for all of them to return, and then .then to return the results in order. It would look like this:

$.when(promise1, promise2)

.then(function(promise1data, promise2data){});

3.4 Challenges

Create a Vacation JS object.

var Vacation = {};

is that it? I’m not sure.

which contains a getPrice function which takes one argument: location

var Vacation = {
  getPrice: function(location){
  }
};

Add the AJAX call currently in the application.js file to the getPrice function

var Vacation = {
  getPrice: function(location){
        $.ajax('/vacation/prices', {
        data: {q: location},
      success: function(result){
        $('.price').text(result.price);
      }
    });
  }
};

It no work. It says it’s not returning a promise. I thought a promise got automatically returned when we do an AJAX thing. Whoa. Okay, this is how it’s done.

var Vacation = {
  getPrice: function(location){
      var promise = $.ajax('/vacation/prices', {
        data: {q: location},
      success: function(result){
        $('.price').text(result.price);
      }
    });
    return promise;
  }
};

See, we create a variable called promise, and then before the getPrice function ends, we do return promise and that’s that. Next, add a .done callback to this code, doing the same thing as the current .success callback.

So first we remove this:

$.ajax('/vacation/prices', {
      data: {q: location},
      success: function(result){
        $('.price').text(result.price);
      }
    });

And replace it with:

    var price = Vacation.getPrice(location);

And then expand it to…

$(document).ready(function() {
  $('button').on('click', function(){
    var location = $('.location').text();
    var price = Vacation.getPrice(location);

    price.done(function(result){
      $('.price').text(result.price);
    });

  });
});

Which I don’t know how I figured out. Here’s the success function added in:

var Vacation = {
  getPrice: function(location){
   var promise = $.Deferred()
   $.ajax('/vacation/prices', {
     data: {q: location},
     success: function(result){
        promise.resolve(result.price)  
     }
   });

Okay—I finished all the challenges, though I have no idea what I’m doing.

# 4 Services, Continued

http://campus.codeschool.com/courses/shaping-up-with-angularjs/level/5/section/2/video/1

They ask, how does a controller use a service like $http? That is, how does a Controller tell Angular that it needs to use the $http service? Like this:

```javascript
app.controller('SomeController', ['$http', function($http){

} ]);

First, in that array, we’re specifying the name of the service ($http), and then we’re including it as an argument in the controller function. This is called dependency injection. Multiple services would look like this:

app.controller('SomeController', ['$http', '$log', function($http, $log){

} ]);

So, remember this bit of code?

    app.controller('StoreController', function () {
        this.products = gems;
    });

We’re going to get rid of that gems bit because that’s the whole reason we’re doing any of this. Here’s how we do the dependency injection, then:

app.controller('StoreController', ['$http',function($http){
        this.products = gems;
    }]);

That’s what it turns into. And just be careful that the array wraps around the original function. Here’s where we are next:

    app.controller('StoreController', ['$http',function($http){
        this.products = gems;

$http.get('/products.json').success(function(data){
            weReallyWantThisToBeThis.Products = data;
        });
    }]);

So $http.get returns a promise, and success() receives the data so we can do something with it. What we’d like to do with it is assign it to this.products…but this refers to $http in this context, so we can’t say this.products. So we’ll do something creative here.

<br />    app.controller('StoreController', ['$http',function($http){
        var store = this;

        // $http.get returns a promise, and .success() receives the data.
        $http.get('/products.json').success(function(data){
            store.products = data;
        });
    }]);

For added fun, we should make sure to create store.products as an empty array to ensure that when the page loads it doesn’t look funny.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s