/ tutorial

[Tutorial] LocalStorage with AngularJS - P.1 - MEAN less the Mongo

Preface:

This started as a one-part tutorial but got out of control as I started from scratch and provided some very detailed explanations on the way. This has now been broken up into several parts. I'm also going to inform you that I haven't completely finished my version of the project using Cordova that I mention in Part 1. When I do I'll let everyone know how it goes!

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

I was dabbling with a mobile application that was to do some simple data management and needed to be able to function offline.

That is, the application would connect to a server, get some data, manipulate the data (outside of a service zone) and then when it regains network connection, send it back to the server for processing and data integrity checks.

I planned to use AngularJS coupled with Apache Cordova to create a Single-Page HTML application and then wrap it up for distribution on a mobile interface. The project was outsourced for a neato project for a group of students that we worked with. They took the project a different direction and left me with a hole of 'I wonder if it would have worked?' So here I am explaining my journey. This is a real layman -term version of my applications functionality. We're going to store some stuff in the browsers localstorage and then fiddle around with it.

We need some stuff first.

I used NodeJS to feign a server (the connection) and built the application using AngularJS and some other nifty javascript libraries.

If you need to install NodeJS, I have detailed instructions in my blog entry : Installing Node without Sudo - Ubuntu 14.04 LTS. Feel free to check it out!

Assuming you have Node installed, for simplicity sake we're going to set up an express application. Install the express-generator to get a cli for initiating express applications, bower for CSS and JS library management (typically for styling and the like), nodemon (to keep resetting the server as we make changes. It's way easier than doing it manually). I'm not going to write tests here but I hear phantomJS is pretty nifty.

$ npm install -g express-generator
$ npm install -g bower
$ npm install -g nodemon

Once you have these installed, go ahead and make an application:

$ express myLocalStorageApp
$ cd myLocalStorageApp && npm install

We're going to use jade. Lots of people like using ejs instead of jade for some reason, but I’m not one of them. I like jade, so I'm going to cover this with jade. Deal with it.

Next we're going to want to get some angular packages and Twitter Bootstrap (because it's my go-to)

$ bower install angularjs
$ bower install angular-route
$ bower install angular-local-storage
$ bower install bootstrap

I like to use nodemon for keeping the application alive while I make changes both server side and JS side. so here's what we're going to do. We're going to install iit with the --save option so that it pops a dependency into our package.json. Then we're going to edit our package.json and change the start script to use nodemon instead of just node.

$ npm install nodemon --save

In your package.json replace

"scripts": {
    "start": "node ./bin/www"
  },

with

"scripts": {
    "start": "nodemon app.js"
  },

This isn't going to do much for us at first. We're going to want to edit our app.js file so that the application will listen on a specific port. Else it just dies!
Add the following just before the final module.exports = app in your app.js file.

app.listen(3000, function(){
    console.log('The application is listening in port '+3000);
});

You can go ahead and start the app up using

$ npm start

This next part is really personal preference. I like to include all my vendor code (javascripts, css, etc) in a special public folder as to avoid confusion and give props where props are due. So I'm going to go ahead and make a vendor folder in my public asset folder. Then I'm going to copy all my bower components into that folder.

$ mkdir public/vendor/
$ cp -R bower_components/* public/vendor

From here, let's include all these vendor items in our layout.jade file. Your file should look something like this when it's all done.

doctype html
html
  head
    title= title
    link(rel='stylesheet', href='/stylesheets/style.css')
    link(rel='stylesheet', href='/vendor/bootstrap/dist/css/bootstrap.css')
    link(rel='stylesheet', href='/vendor/bootstrap/dist/css/bootstrap-theme.css')
  body
    block content
  script(src='/vendor/angular/angular.js')
  script(src='/vendor/angular-route/angular-route.js')
  script(Src='/vendor/angular-local-storage/dist/angular-local-storage.js')

Now let's set up the angular portion of our application. In your public/javascripts folder, create a new file 'app.js'

$ touch public/javascripts/app.js

Open it up with your favorite text editor and we're going to generate our angular application. Your file should end up looking like this:

var app = angular.module('myLocalStorageApp', ['ngRoute', 'LocalStorageModule']);

Then you need to make some more adjustments to your layout.jade file. Firstly, we're going to want to assign the application to the html element using ng-app='myLocalStorageApp'. Then you're going to want to make sure you load the app.js script in your includes at the end of all the inclusions.
Your layoute.jade file should now look something like this

doctype html
html(ng-app='myLocalStorageApp')
  head
    title= title
    link(rel='stylesheet', href='/stylesheets/style.css')
    link(rel='stylesheet', href='/vendor/bootstrap/dist/css/bootstrap.css')
    link(rel='stylesheet', href='/vendor/bootstrap/dist/css/bootstrap-theme.css')
  body
    block content
  script(src='/vendor/angular/angular.js')
  script(src='/vendor/angular-route/angular-route.js')
  script(src='/vendor/angular-local-storage/dist/angular-local-storage.js')
  script(src='/javascripts/app.js')

Hazzah! We're up and running. Sort of. We've got our angular dependencies loaded up and we've got an application ready to roar. It's time to start some coding.
I like to use the angular-route module because it's the way my mind is wired. You could manually assign controllers to sections of code as well, but that's never been my forte.

Let's add a route to our application.
Edit your app.js file and add the following after you've declared your app.

app.config(['$routeProvider',
  function($routeProvider){
    $routeProvider
    .when('/', {
      templateUrl:'partials/index',
      controller:MainCtrl
    })
    .otherwise({
      redirectTo:'/'
    })
}]);

You'll notice that nothing is happening!
There are a few reasons as to why this is. Firstly, we haven't put in an outlet for our application in any of our jade files. Let's add a quick little blip of code to the views/index.jade file. You should look something like this.

extends layout

block content
  h1= title
  p Welcome to #{title}
  div(ng-view)

If you refresh your page now and look at your console output (press f12 and choose console in the menus that pop up), you'll notice a new error!
404!!!! The dreaded 404.
We need to configure our server to return a view for the application to render. We're going to do this by editing the built in file found in routes/index.js.

This is going to help us render the jade files in small snippets instead of huge sections. Let's generate a route for accomplishing this. In routes/index.js add the following code

...
router.get('/partials/:name', function(req, res, next){
  var name = req.param('name');
  res.render('partials/'+name);
});
module.exports = router;

You can refresh your page if you like, but you're still going to get some errors. We haven't generated a page for it to return yet! This is probably going to give you a 500 error if you don't make the partial template first. Let's make a file at views/partials

$ mkdir views/partials
$ touch views/partials/index.jade

Add some content to index.jade just so we know that it's being loaded up. I chose the following:

block content
  h1 Hello There!

Refresh your page now and you will see that the template is being loaded, but your console is still yelling at you. We haven't defined a controller in our application yet, so let's go ahead and do that.
Open up your angular app (I know this is probably confusing with the app.js for Node and app.js for Angular) at public/javascripts/app.js

Underneath your route configuration, we're going to create our first controller.
Add this code in there

app.controller('MainCtrl', ['$scope', function($scope){
  $scope.message = 'What\'s up AngularJS?'
}]);

This $scope variable is allowing us to directly access the request scope of the web page. We're going to set a variable in there called 'message' and then we will edit the partials/index.jade file to bind this variable to our view.

Update your partials/index.jade to look like this

block content
  h1 {{message}}

Now if you referesh your page you will see our awesome message in place of the static text before!

Cheers!

References:

grevory/angular-local-storage - The Local Storage Module