basic SPA (single page app) boilerplate for fast prototyping in angular

Creating this inside a Vagrant image (meaning there are extra steps to install the needed basic packages):

Creating the Vagrant workspace:

vagrant init ubuntu/trusty64;
vagrant up; vagrant ssh;
vi Vangrant # and then port forward 8080 to 8080 on the host

Creating the boilerplate:

mkdir myapp; cd myapp;
sudo apt-get update;
sudo apt-get install git;
sudo apt-get install npm;
sudo ln -s /usr/bin/nodejs /usr/bin/node;
npm init;
sudo npm install -g bower;
bower init;
touch main.js
touch index.html
git init
vi .gitignore
# include the following inside your .gitignore file:
mode_modules
bower_components
# back to the command line:
bower install --save angular
bower install --save ui-router

HTML / JS basic bootstrap

main.js

angular.module(“MyAppName”, [‘ui-router’])

.config(function ($stateProvider, $urlRouterProvider) {

});

index.html

<head>
<title>
My App Title
</title>
</head>
<body ng-app=MyAppName>
<h1>My App</h1>
<div ui-view></div>
<script src=bower_components/angular/angular.js></script>
<script src=bower_components/angular-ui-router/release/angular-ui-router.js></script>
<script src=main.js></script>
</body>

Quickly testing the page without setting up apache:

sudo npm install -g http-server

http-server

Angularjs: template to setup just the UI part

Setting this up with Vagrant (therefore the extra steps / packages downloaded):

$ mkdir src; vagrant init ubuntu/trusty64

$ vi Vagrantfile // modify the following lines:

from:  # config.vm.network “forwarded_port”, guest: 80, host: 8080

to:

  config.vm.network :”forwarded_port”, guest: 3000, host: 3000

from: # config.vm.synced_folder “../data”, “/vagrant_data”

to:

 config.vm.synced_folder “./src”, “/home/vagrant/[your project name]”

$ vagrant ssh

Once you are inside the vagrant instance:

$ sudo apt-get install git

git clone –depth=1 https://github.com/angular/angular-seed.git [your_project_name]

Installing NPM:

sudo apt-get update

$ sudo apt-get install nodejs

$ sudo apt-get install npm

$ sudo ln -s /usr/bin/nodejs /usr/bin/node

Initialize and start the project:

npm install

$ python -m SimpleHTTPServer 3000 // to see your files in a simple server

The rest of the details (like testing, and updating angular and the other packages) are in:

https://github.com/angular/angular-seed

Running your code in production:

You just need all the static files inside your app/ directory in a server, that’s it!

angular.js: CRUD operations via $http

These are the basic operations you can do with $http:

  • $http.get(): Accepts aURL and optional config object. Performs an HTTP GETrequest.
  • $http.head(): Accepts a URL and optional config object. Performs an HTTP HEADrequest.
  • $http.post(): Accepts a URL, data object, and optional config object. Performs an HTTP POST request.
  • $http.put(): Accepts a URL, data object, and optional config object. Performs an HTTP PUT request.
  • $http.delete(): Accepts a URL and optional config object. Performs an HTTPDELETE request.
  • $http.jsonp(): Accepts a URL and optional config object. The callback name should be the string JSON_CALLBACK.
  • $http.patch(): Accepts a URL, data object, and optional config object. Performs an HTTP PUT request.

Example of calling the get() method:

$http({
    method: 'GET',
    url: 'http://localhost:8000'
});

$http.get('http://localhost:8000');

You can easily chain promises to handle success or error returns from the server:

$http.get('http://localhost:8000')
.success(function(data){
    $scope.contacts = data;
})
.error(function(){
    window.alert('There was an error!');
});

Example of a post:

$http.post('http://localhost:8000', {
    name: 'Declan Proud',
    email: 'declan@example.com',
    ...
});

angular.js: directives

Basic structure:

myModule.directive('directiveName', function(){
    return {
        restrict: 'AE',
        replace: true,
        template: '<div class="{{class}}">some html, this could also be a reference to an external template. In which case, you can use templateUrl</div>', link: function(scope, elem, attrs){ // elem is the HTML element the directive was applied to, and attrs are the attributes of the same } } })

The possible restrict values:

  • A: This restricts the directive to be attached using an attribute
  • E: This allows the directive to be used as a custom element
  • C: This allows the directive to be used by adding it as a class to the element
  • M: Allows the directive to be executed via an HTML comment

Other options:

replace: true substitutes the DOM element for the template (instead of just filling out their inner HTML)

boostrapping a MEAN app (angular.js, express, node.js, mongodb)

Complete code example here

– in the folder you are creating your app, place a manifest file called package.json, example:

{
“name”: “yourappnamehere”
}

– install express and other dependencies:

$ sudo apt-get update

$ sudo apt-get install npm

sudo apt-get install nodejs

Note: copying and pasting these commands sometimes will give you the following error message:

TypeError: Cannot read property ‘latest’ of undefined

make sure to retype the “–” part manually, and you’ll be all right

$ sudo npm install –save express

$ sudo npm install –save body-parser

– if you want node to automatically restart when there are changes in the files, you can also install the following package:

$ npm install –global nodemon

– and then, when you start your server, you need to start it as:

$ npm install –global nodemon

– install mongodb, create the following file on your home directory: mongo_install.bash, with the following content:

apt-key adv --keyserver keyserver.ubuntu.com --recv 7F0CEB10
echo "deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen" | tee -a /etc/apt/sources.list.d/10gen.list
apt-get -y update
apt-get -y install mongodb-10gen

– run sudo bash ./mongo_install.bash

– the installation starts mongod by default, but that is the daemon you need to start if you don’t see it running

– install mongoose:

$ npm install –save mongoose

– to enter the console mode (and verify the installation), type mongo, if you want to connect to an specific db, you do:

mongo nameofyourdbhere

> db.posts.find()

that will give you all the records saved under the Post model

– create a server.js file, that will host your app (see code in heroku instance for content details)

– run your server:

nodejs server.js

(config.vm.network :forwarded_port, guest: 3000, host: 3000 on Vagrantfile if you are running inside vagrant)

– server.js is kind of your single point of entry for your app. It is always a good idea to keep it lean, and move as much code as possible away from it into other files. Some things that are worth having at this file are:

— the server listening loop

— global configuration and other middleware packages

— logging and error handling

— controllers spawning and mounting

– on static files: it is a good idea not to serve them via nodejs. Try to keep your node instance as an API, and let apache and other cache services to do the static servers job. But if you must, it is always a good idea to spin them into:

/controllers/static.js

and inside that file:

var express = require(‘express’)
var router = express.Router()

router.use(express.static(__dirname + ‘/../assets’))

– so now any file you put on your /assets folder will be served by node

– on services: things like $http are better constructed via a service, and then injected to wherever they are needed. Below is an example of doing just that:

app.service(‘PostsSvc’, function ($http) {

  this.fetch = function () {

    return $http.get(‘/api/posts’)

  }

  this.create = function (post) {

    return $http.post(‘/api/posts’, post)

  }

});

– and then, the controllers that consume it would looks something like this:

    // create the PostsCtrl module

    // dependency inject $scope

    app.controller(‘PostsCtrl’, function ($scope, PostsSvc) {

      // the function runs when the “Add Post” button is clicked

$scope.addPost = function () {

    if ($scope.postBody) {

      PostsSvc.create({

        username: ‘ramiro’,

        body: $scope.postBody

      }).success(function (post) {

        $scope.posts.unshift(post)

        $scope.postBody = null

      })

    }

  };

 Deploying to Heroku

– create a .gitignore file with the following lines:

node_modules
assets

– if you are in a new vagrant instance, install heroku tools first:

$ wget -qO- https://toolbelt.heroku.com/install-ubuntu.sh | sh

$ heroku login

heroku create your-app-name-here

heroku addons:create mongolab

– check what is the address of your mongolabs instance:

$ heroku config

– modify your db.js file according to what you see printed by that command, it would look something like this:

var mongoose = require(‘mongoose’);

var url = process.env.MONGOLAB_URI || ‘mongodb://localhost/social’;

mongoose.connect(url);

module.exports = mongoose;

– you also need to do a similar move for the listen command in the server.js file:

// process.env.PORT for the benefit of Heroku

app.listen(process.env.PORT || 3000, function () {

  console.log(‘Server listening on’, 3000)

});

bootstapping an angular.js app on top of rails

Complete example here

– The following will skip jquery and turbolinks:

rails new [your-app-name-here] –skip-javascript

– But now you need to manually create the following file: app/assets/javascripts/application.js

– And include the following in the file contents:

//= require_tree .

– put all your angular app files inside app/assets/javascripts, and include the following in your app/views/layouts/application.html.erb template:

<%= javascript_include_tag ‘application’ %>

– Your app/views/layouts/application.html.erb will also be the home page of your single page app, so make sure you put all your initial HTML code in there, and get rid of the default  <%= yield %> in there.

– Add the following to your application controller:

def angular

render ‘layouts/application’

end

– and in your config/ routes.rb file, add a route to it:

root to: ‘application#angular’

– install bower, if you haven’t done so already:

npm install -g bower

– initialize it inside your rails project:

bower init

– create a .bowerrc file, with the following, to tell bower where you are storing your js dependencies:

{ “directory“:“vendor/assets/bower_components” }

– install the dependencies you need, and tell bower to save them in your config file:

bower install angular angular-ui-router bootstrap –save

– now that the libraries you need are installed, modify your app/assets/javascripts/application.js file to call them at page load time:

//= require angular

//= require angular-ui-router

//= require_tree .

– to add the bootstrap CSS, add the following line to the comments section on app/assets/stylesheets/application.css

*= require bootstrap/dist/css/bootstrap

*= require_tree .

Important: that goes inside the header /* comments */

At this point, you have all your js / css dependencies managed by bower, and minified and pulled in the right places by the rails app.

– place your template files inside public/templates, and modify your config file as follows:

.config([

'$stateProvider',
'$urlRouterProvider',
function($stateProvider, $urlRouterProvider) {

$stateProvider
.state(‘home’, {
url: ‘/home’,
templateUrl: ‘templates/_home.html’,
controller: ‘MainCtrl’
}).state(‘posts’, {
url: ‘/posts/{id}’,
templateUrl: ‘templates/_posts.html’,
controller: ‘PostsCtrl’
});

$urlRouterProvider.otherwise(‘home’);
}])

– move your JS to folders inside app/assets/javascripts (create mainCtrl.js inside the home folder, and postsCtrl.js inside the posts folder), so the only thing left inside app.js is the configuration and routes.

– generate your models:

rails generate model Post title:string link:string upvotes:integer

rails generate model Comment body:string upvotes:integer post:references

rake db:migrate

– declare your associations inside your models:

class Post < ActiveRecord::Base

  has_many :comments

end

– if you have children models from one of your models, and you want the children to be returned as part of your JSON, put the following method inside your model as well:

def as_json(options = {})

     super(options.merge(include: :comments))

end

 – setup your base routes:

root to: ‘application#angular’

resources :posts, only: [:create, :index, :show] do

   resources :comments, only: [:show, :create] do

      member do put ‘/upvote’ => ‘comments#upvote’

   end

end

member do put ‘/upvote’ => ‘posts#upvote’ end end

Nesting resources will create urls like this: posts/1/comment/3

By specifying member do, you are also simplifying some of the resources out of the deep nesting

 – using “rake routes” at this point will tell you what kind of routes you have defined

– now is time to create your controllers, make sure you skip the templates and assets creation when doing so:

rails generate controller Posts –skip-assets –skip-template-engine

rails generate controller Comments –skip-assets –skip-template-engine

 – add the following line to your application controller, so it responds back in json format:

respond_to :json

– in the newer versions of rails, you will need to add this gem to your Gemfile to get that respond_to functionality:

gem ‘responders’, ‘~> 2.0’

 – tell your controllers which parameters are allowed, and also create the basic controller methods:

def index
respond_with Post.all
end

def create
respond_with Post.create(post_params)
end

def show
respond_with Post.find(params[:id])
end

def upvote
post = Post.find(params[:id])
post.increment!(:upvotes)

respond_with post
end

private
def post_params
params.require(:post).permit(:link, :title)
end
end

– back to your front end: setup your factory to be able to retrieve all of your records by means of hitting your index method (see how $http is injected, and how the getAll function is implemented as a promise)

angular.module(‘flapperNews’)

.factory(‘posts’, [‘$http’, function(){

  var o = {

    posts: []

  };

  o.getAll = function() {

    return $http.get(‘/posts.json’).success(function(data){

      angular.copy(data, o.posts);

    });

  };

  return o;

}]);

– now, if you want your view to refresh with the server data every time the UI calls “home”, you need to set your stateProvider with the “resolve” property:

$stateProvider

    .state(‘home’, {

      url: ‘/home’,

      templateUrl: ‘templates/_home.html’,

      controller: ‘MainCtrl’,

      resolve: {

            postPromise: [‘posts’, function(posts){

               return posts.getAll();

           }]

      }

    })

 – to be able to create post, add the following to your post service:

o.create = function(post) { return $http.post(‘/posts.json’, post).success(function(data){ o.posts.push(data); }); };

– and, in your main controller:

$scope.addPost = function(){ if(!$scope.title || $scope.title === ) { return; } posts.create({ title: $scope.title, link: $scope.link, }); $scope.title = ; $scope.link = ; };

– by default, rails has protection against fake posts, so in order to save the data you are posting, you will need to add the following gem (otherwise you will be getting “422 unprocessable entry” error messages

gem ‘angular_rails_csrf’

– to add user authentication via Device, you need to install the gem first:

gem ‘devise’, ‘~> 3.4.0’

– after you bundle install, initialize it, and create the user’s model:

rails generate devise:install

rails generate devise User

– if you need to, you can add more fields to the default devise model, which only contain email and password by default. We will also make the username unique:

rails generate migration AddUsernameToUser username:string:uniq

– in order to integrate devise with the front end, you can install the following js package helper via bower:

bower install angular-devise –save

– in application.js, require the newly installed package:

//= require angular-devise

– and inject the module in the main app:

angular.module(‘flapperNews’, [‘ui.router’, ‘Devise’]).

# note: when you try your registration / login forms, if there is a devise error, it may manifest in the front end as a 422 error message, you need to handle the errors as they come from the server

– to secure your posts savings (or any other controller actions for that matter) you can now use the following:

class PostsController < ApplicationController before_filter :authenticate_user!, only: [:create, :upvote]

– if you want to associate two of the models you are working with (in this case posts and users), run the following command, that will create the db migration necessary to do the work:

rails g migration AddUserRefToPosts user:references

rails g migration AddUserRefToComments user:references

rake db:migrate

– if you do so, you need to enhance the models to reflect that association:

class Comment < ActiveRecord::Base

  belongs_to :user

– and your update and create methods also need to include the relationship, so rails knows at save time what users are assigned what records:

def create

respond_with Post.create(post_params.merge(user_id: current_user.id))

end

Angularjs with Ruby On Rails app from scratch

# Assuming node is already installed, install the stuff needed to bootstrap the app:

npm install -g yo

npm install -g generator-angular

# create your app (the name of the folder will be the name of your app):

mkdir myapp; cd myapp;

yo angular

# in the series of questions, pick bootstrap (to make it easier on you)

# get git going:

git init; git add .

git commit -am “initial commit”

# (optional): open your Gruntfile.js file, and edit out the following line:

tasks: [‘newer:jshint:all’], and also tasks: [‘newer:jshint:test’, ‘karma’]

to:

tasks: [] # to avoid running jshint for each edit, and the test suite with any new test / changes

# bowel.json will contain all app dependencies, if you need anything more, you add it there

# package.json will contain all the development dependencies

# app/index.html has your single page app bootstrapped HTML page

# to make sure we are up and running, run:

grunt serve # you shall have a basic page running at this point

Angular.js: the basics

$scope

$scope is what connects views and controllers.

Most logic goes in the controllers. Controllers shouldn’t know what view they are operating on, their job is to produce a $scope (to keep dependencies light)

The $scope can be considered a “quasi model”, it is the pure data variables and data objects that binds both the view and the controller.

Example (defining the controller, and then applying the variables in the view):

<script type=”text/javascript”>
function MyController($scope) {

$scope.musicList = [
{ title: ‘With or without you’, artist: ‘U2’ },
{ title: ‘Two princes’, artist: ‘Spin Doctor’ }
];

}
</script>
<div class=”container” ng-controller=”myController”>
<ul>
<li ng-repeat=”song in musicList”>{{song.title}} by {{song.artist}}</li>
</ul>
</div>

Note that each li will create it’s own internal $scope, but inherit from the parent $scope controller. Angular apps mirror the DOM in regards to parent DOM elements inheriting values down to their children elements.

Application flow

Routers take the URL from the browser, and call an specific controller. The controller will have the logic to handle the URL request, and pass the data to the corresponding view, via the $scope.

Controllers don’t do it all, usually they delegate work to services, factories, providers, or resources.

View also don’t do a lot on their own, they rely on directives and filters to make the DOM dance.

So, in summary: routes calls controllers, which in turn call services, factories, etc to complete tasks, and then load the final result into the $scope. Views take over from there.

Code organization

It’s always a good idea to encapsulate stuff in modules. The start line to do so in angular is:

var myApp = angular.module(‘myApp’, [ ‘someOtherModuleHere’ ]);

someOtherModuleHere is optional, you can chain any other helper modules you need here for your ‘myApp’ module. Kind of the requireJS of angular.

So now that you have your myApp module namespaced, you can start attaching controllers to it:

myApp.controller(‘MyMusicController’, function ($scope) {

$scope.musicList = [
{ title: ‘With or without you’, artist: ‘U2’ },
{ title: ‘Two princes’, artist: ‘Spin Doctor’ }
];

});

So now, the way to put that controller to use, is to mark what DOM element of the HTML will be controlled by it, by means of the ng-controller directive:

<html ng-app=”myApp”>

<div ng-controller=”myMusicCtrl“>

<!— everything here will have access (and be binded) to the variables defined in $scope –>

</div>

By convention, you usually name your controllers [something]Ctrl. Each controller can support multiple views, so it can be used to get things synched. A view can also hold more than one controller.

This is how you declare it:

myApp.controller("dayCtrl", function ( $scope) {

Regarding code organization, it is better to keep everything separate. Usually you put the kickstarterd of the application on app.js (defining the namespace and the routers), and then controllers and services in their own space (for definition of services, see factories / services below). They can be tested separately, and called for different controllers this way, in a more modular fashion.

 Defining routes

Since angular 1.2, router functionality exist separate from the angular library itself, you would need to download separately from:

https://github.com/angular-ui/ui-router

Using config, you can define what URLs load what templates:

demoApp.config(function ($routeProvider) {
$routeProvider
.when(‘/’,
{
controller: ‘myMusicController’,
templateUrl: ‘View1.html’
})
.when(‘/someURL’,
{
controller: ‘anotherController’,
templateUrl: ‘View2.html’
}).
otherwise({ redirectTo: ‘/’ });

The html files that are being called out are off the root directory, but you can organize them in folders.

The app page has a “ng-view” container, that will automatically receive the template specified in the URL via the router above, the way that part is setup in the page is via a simple tag:

<div class=”container”>
<ng-view><!– the templates will dump their content here –></ng-view>
</div>

 Click events

They are handled in the view files via the ng-click directive, example:

<button ng-click=”doSomething()”>Click me</button>

And, inside the controller:

$scope.doSomething = function () {
// Things happening inside here
}

Reading form fields
It is also done via the $scope:
var localVarInsideController = $scope.objectName.objectVariable;
And in your view:

<input type=”text” data-ng-model=”objectName.objectVariable” />

Now the var localVarInsideController in your controller, and the input field in your view are binded.

To validate, you can add the following to the fields:

ng-disabled=“addForm.$invalid”

where addForms is the name=”addForms” attribute in your <form> tag. And then, in your input fields you mark ones required as:

<input type=“text” id=“name” class=“form-control” ng-model=“contact.name” required>

You can find some examples here

Data providers

In order to keep those modular and reusable, angular has some predefined objects for them. They all do sort of the same work, just in a different way:

1) Factories: creates a closure of methods and variables, which runs once, and expose certain methods so code can deal with its functionality. It is pretty much the thing you would usually do to envelop an object into a closure, with public and private methods.

This is an example of a factory that returns one value:

.factory('demoService', function demoServiceFactory(){
  return 'abc123';
});

In reality, factories are better off returning functions with methods instead of plain data.

2) Service: provides the functionality needed to create the data. Usually in the form of “this.someFunction = [anonymous function here] inside your controller. They are usually singletons that are accesible throughout the application, using the same data. The simplest form is creating constants via:

.value('demoService', 'abc123');

and then you can inject that service anywhere you need that value, like:

.controller('indexCtl', function($scope, demoService){
    $scope.demo = demoService;
});

3) provider: you define a $get inside of it that will contain the data to be returned. They can call other services to build upon.

Example of creating a factory for our app:

myApp.factory(‘simpleFactory’, function ($http) {

// In this case, we injected $http so we can call the data from backend

}

4) Directive: enclose functionality, the equivalent of web components. Example of setting one up:

myApp.directive(“highlight”, function () {
    returnfunction (scope, element, attrs) {
        if (scope.day == attrs[“highlight”]) {
            element.css(“color”, “red”);
        }
    }
});

You pass a factory function, so each time the directive gets called from the HTML, a copy of that factory function gets run (usually called a “worker”). Notice the $scope is passed via “scope” (without the $), element is the HTML element the directive was applied to (wrap in a jquery like object), and the attrs are the set of attributes on that element, including the directive one.

 Angular philosophy regarding UI architecture   

Angular === imperative! Meaning you build your static HTML as normal, and then you add directives to add the dynamic parts, and all the other fun event driven stuff on top of it

$rootScope

this is a global space in your app, that could be used to share data between components. To attach vars to it you could use something like

.controller('indexCtl', function($scope, $rootScope){
    $rootScope.contacts = [your array here...

and then call it on your views via:

<tr ng-repeat="contact in $root.contacts">

using global scope variables is a bad practice in general, use it sparingly

loose ends

Get a hold of angular elements from third party javascript code:

angular.element(document.getElementById(‘yourElement’)).scope().yourFunctionInsideThe$scopeVar();

ng-non-bindable // Use it to indicate an area of your HTML where you don't want the {{}} to be translated to a request for binding

Use $routeParams if you are using ngRoute, if you are using ui-router, use $stateParams instead