/ tutorial

[Tutorial] LocalStorage with AngularJS - P.2 - API Data Requests

Welcome to part 2 of this my was-going-to-be-short-tutorial-but-now-is-huge.
Here's a list of what we've worked on so far.

  1. Installing NodeJS
  2. P.1 - MEAN less the Mongo
  3. P.2 - API Data Requests - you are here
  4. P.3 - Local Storage of JSON Objects

Let's dive right into it then!

We're going to use NodeJS as an API for the application. In this project we're going to simplify my project and just pass back a list of products. Let's say we're trying to keep track of our stuff for insurance purposes. Their structure is going to look like this:

[
	{
    	_id:1,
        name:'Gibson SG Standard Silverburst',
        vendor:'Long and McQuade',
        purchaseValue:200.00,
        appraisalValue:0
    },
    {
    	_id:2,
        name:'Custom Built PC',
        vendor:'Memory Express',
        purchaseValue:50.00
    },
    {
    	_id:3,
        name:'Isis',
        vendor:'Cat Store',
        purchaseValue:0
    }
]

In this list I have a guitar, my PC, and my cat. I want to have this information with me as I wander around to check if I have them all still.

We're going to configure a route in NodeJS to return this data back to us. For now, we're going to omit the connection to a Database and simply retrn this JSON object.

I'm going to make a dummy model that is really just going to give us a variable in return.

$ mkdir models
$ touch models/inventory.js

Add the following code to inventory.js to create a JSON object that we're going to pretend came from a database.

module.exports = {
	var inventory : [
      {
          _id:1,
          name:'Gibson SG Standard Silverburst',
          vendor:'Long and McQuade',
          purchaseValue:200.00,
          appraisalValue:0
      },
      {
          _id:2,
          name:'Custom Built PC',
          vendor:'Memory Express',
          purchaseValue:50.00
      },
      {
          _id:3,
          name:'Isis',
          vendor:'Cat Store',
          purchaseValue:0
      }
	]
};

Next we're going to generate an API route for our application to access.

$ touch routes/inventory.js

Have your new route contain the following code. It is going to load the information from the inventory.js model we generated and, when requested by a client, return it.

var express = require('express');
var router = express.Router();

var inventory = require('../models/inventory');

router.get('/', function(req, res, next){
	res.json(inventory.inventory);
});

module.exports = router;

INB4 : That's not a request for an inventory, that looks a lot like a root request.

This is because we haven't edited our app.js file and instructed it to use this file as an inventory route.

Adjust your app.js file to contain the following just before the declaration of your app variable.

var routes = requre('./routes/index');
var users = require('./routes/users');
var inventory = require('./routes/inventory');

Then tell the application to use this variable append the instruction to use the inventory variable where you tell the app to use the other routes.

app.use('/', routes);
app.use('/users', users);
app.use('/inventory', inventory);

Since your nodemon is still running, you should be able to go to the url and see the JSON object getting returned now.
navigate to localhost:3000/inventory and you should see this pop up in the main screen of your browser. If you're using I.E. (firstly I apologize for your frustration), I have sometimes experienced the browser attempt to download the JSON instead of displaying it. You can accept the download if you want but it's going to look like what pops up below.

[{"_id":1,"name":"Gibson SG Standard Silverburst","vendor":"Long and McQuade","purchaseValue":200,"appraisalValue":0},{"_id":2,"name":"Custom Built PC","vendor":"Memory Express","purchaseValue":50},{"_id":3,"name":"Isis","vendor":"Cat Store","purchaseValue":0}]

Programming against an API has huge advantages. It allows us to make very small, distinct requests to the server when trying to get, post, put and delete items. I'm not going to go into huge detail about restful architecture but typically you're going to overuse these four verbs for

  1. GET : Request a resource from the server
  2. POST: Post a new resource to the server
  3. PUT: Put a resource back to the server (save a resource)
  4. DELETE: Delete a resource from the server

Enough rambling, Let's code!
We're going to add a small bit of code to the Angular portion of our application in order to get the data from the server.

In your MainCtrl controller in your public/javascripts/app.js file, we're going to want to inject some more dependencies and then make an http request to our fancy new Node API. We're going to inject the $http module and then use it to call our localhost:3000/inventory request. Your controller should now look something like this.

app.controller('MainCtrl', ['$scope','$http', function($scope, $http){
  $scope.message = 'Check out this inventory';
  //get us some datas
  $http.get('/inventory') //make the http request
  .success(function(data, status, headers, config){ //if it returns a success status
    $scope.inventory = data; //set the inventory to the data returned
  })
  .error(function(data, status, headers, config){ //if an error status is returned
    $scope.message = 'Something exploded! '+data.message; //set a message
  });
}]);

We're going to want to display this data, so let's edit our partials/index.jade to make a pretty table to display some information for us. We're going to use angular pipes to format numbers as well. This is how I decided to make my table look. Feel free to make it however you please!

block content
  
  .row
    .col-md-6
      h1 {{message}}
      table.table-striped.table-condensed
        tr
          th id
          th vendor
          th name
          th purchaseValue
          th appraisalValue
          th actions
        tr(ng-repeat = "i in inventory")
          td {{i._id}}
          td {{i.vendor}}
          td {{i.name}}
          td {{i.purchaseValue | currency}}
          td {{i.appraisalValue | currency}}
          td 
            a.btn.btn-primary(href = '' ng-click = 'edit($index)') Edit

You've probably noticed I added a function in the ng-click section of the actions button. You caught me. We're going to edit the record selected. The $index variable indicates which item in the array we are on. It is a part of the ng-repeat functionality and proves super useful when we want to limit requests to our server.
Let's add a section to the bottom of our MainCtrl in our public/javascripts/app.js file to handle this edit functionality.

$scope.edit = function(index){
  $scope.item = $scope.inventory[index];
};

That's it. We've now taken a single item of the inventory and put it into our scope. We can now add in some UI elements so that we can edit the model.

Add this directly after the aforementioned code in your partials/index.js file

    .col-md-3(ng-show = 'editing')
      form
        h1 Editing!
        .form-group
          label id
          input.form-control(ng-model='item._id' readonly)
        .form-group
          label vendor
          input.form-control(ng-model='item.vendor')
        .form-group
          label name
          input.form-control(ng-model = 'item.name')
        .form-group
          label Purchase Cost
          input.form-control(type = "number" ng-model = 'item.purchaseValue')
        .form-group
          label Appraisal Value
          input.form-control(type="number" ng-model = 'item.appraisalValue')
        .form-group
          a.form-control.btn-primary(href = '' ng-click = 'finishEditing()') Done

If you refresh and click edit, you're not going to see anything. Nope it's not broken! Look at the .col-md-3(ng-show = 'editing'). This is how we can have awesome UI control and maintain a single page application!
This is very rudimentry and isn't very elegant, but it gets the job done! Let's add a scope variable in our edit function to tell the UI we are in the editing state. Your function should now look like the following.

$scope.edit = function(index){
  $scope.editing = true;
  $scope.item = $scope.inventory[index];
};

Now refresh your page and click the edit button. You've got your editing field! Directly to the right you can update your values. If you pay close attention you will see that the values are updating in your table as well! Right before our eyes! This is Angular's data binding. We can associate inputs with model attributes and get rid of that nasty middle man getters-and-setters that plague us in conventional OOP. This makes life super easy for the developer and makes the internet appear super neat.
We could probably make a temporary variable to hold the data in an 'editing' state then have a 'save' and 'cancel' button and then handle it appropriately but that looks way less cool. Let's just bask in the awesomeness that is this data binding.

This leaves us with our final function. The finishEditing function. I like to leave my code in my controllers and away from the UI. Clear deliniation of responsibility! It's good programming practice in my opinion. The only code in UI should be for program flow, and ui realization.
Let's plunk the last function into our controller and we'll call it a day on this section.

  $scope.finishEditing = function(){
    $scope.editing = false;
    $scope.item = {};
  }

I'm going to push this section to my github and is commit #4.
https://github.com/Daimonos/localstorage-tutorial

Cheers until next time!