INTE
GRATIN
G ANGULA
R.JS
WIT
H RAILS
J ON
AT
HA
N L
I NO
WE
S
WI C
KE
D G
OO
D R
UB
Y 2
01
3 L
I GH
TE
NI N
G T
AL K
OC
TO
BE
R 1
1,
20
13
DAWNING OF THE AGE OF ANGULARJS
• client-side javascript MVC framework
• makes use of directives, dependency injection, and data binding
• “reasonable magic”, like Rails
• http://angularjs.org/
• by Google
ANGULAR.JS
Directives• Domain specific HTML syntax, reusable
Dependency Injection• Declarative description how app is wired (de-
centralized)
Data Binding• Automatic updating the view whenever model
changes
Community• Documentation, tutorials, testable, ecosystem
ANGULARJS.ORG
ANGULAR IN RAILS
Gemfile
gem 'angularjs-rails’
app/assets/javascripts/application.js
//= require angular
config/environments/production.rb
config.assets.js_compressor = Sprockets::LazyCompressor.new { Uglifier.new(mangle: false) }
HELLO HTML
app/views/layouts/application.html.haml
%html{ 'ng-app' => true }
<html ng-app>
app/views/home/index.html.haml
%p Hello {{'World' + '!'}}
<p>Hello {{'World' + '!'}}</p>
HELLO CNTL
app/assets/javascripts/hello.js.coffee
HelloCntl = ($scope) ->
$scope.name = 'World’
index.html.haml
%div{ ng_controller: 'HelloCntl'}
Your name:
%input{ type: 'text', ng_model: 'name', value: 'World' }
Hello {{name}}
HELLO MODULE
app/assets/javascripts/hello.js.coffee
app = angular.module 'Hello', []
app.controller 'HelloCntl', @HelloCntl = ($scope) ->
$scope.name = 'World'
APP PARTIAL
app/views/home/_hello.html.haml
%div{ 'ng-app' => 'Hello'}
...
POSTS INDEX AJAX
app/assets/javascripts/posts.js.coffee
app = angular.module 'Posts', []
app.controller "PostsCtrl", @PostsCtrl = ($scope, $http) ->
$http.get('/posts.json').success (data) ->
$scope.posts = data
app/views/posts/index.html.haml
%h1 Listing posts
%div{ ng_controller: "PostsCtrl" }
%ul
%li{ ng_repeat: "post in posts" }
{{post.title}}
POSTS INDEX ANGULAR RESOURCE
application.js
//= require angular-resource
posts.js.coffee
app = angular.module 'Posts', ['ngResource’]
app.controller "PostsCtrl", @PostsCtrl = ($scope, $resource) ->
//$http.get('/posts.json').success (data) ->
// $scope.posts = data
Post = $resource('/posts')
$scope.posts = Post.query()
TESTING WITH JASMINE AND TEASPOON
Gemfile
gem 'teaspoon’gem 'phantomjs'
spec/javascripts/support/spec_helper.js
//= require angular-mocks
Run
browse to http://localhost:3000/teaspoon or rake teaspoon
SPEC SETUP
spec/javascripts/posts_spec.js.coffee
describe "PostsCtrl", ->
$fixture = [
{ id: 1, title: "Title 1", intro: "This is posting 1" },
{ id: 2, title: "Title 2", intro: "This is posting 2" }
]
$scope = null
$controller = null
$httpBackend = null
beforeEach module('Posts')
beforeEach inject ($injector) ->
$scope = $injector.get('$rootScope').$new()
$controller = $injector.get('$controller')
$httpBackend = $injector.get('$httpBackend')
$httpBackend.when('GET','/posts.json').respond($fixture)
SPEC EXAMPLE
it 'creates posts model with index request', ->
$httpBackend.expect('GET', '/posts').respond($fixture)
$controller(PostsCtrl, {$scope: $scope }) //instantiate controller
expect($scope.posts).toEqualData []
$httpBackend.flush()// mock the ajax success
expect($scope.posts.length).toBe 2
expect($scope.posts).toEqualData $fixture
http://vaporbase.com/postings/integrating-angular-dot-js-with-rails
Jonathan Linowes
@linojon
github.com/linojon
http://www.parkerhill.com/