The Art of AngularJS - DeRailed 2014

132,711 views
141,935 views

Published on

AngularJS is one of today's hottest JavaScript MVC Frameworks. In this session, we'll explore many concepts it brings to the world of client-side development: dependency injection, directives, filters, routing and two-way data binding. We'll also look at its recommended testing tools and build systems. Finally, you'll learn about my experience developing several real-world applications using AngularJS, HTML5 and Bootstrap.

Published in: Technology
0 Comments
176 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
132,711
On SlideShare
0
From Embeds
0
Number of Embeds
105,665
Actions
Shares
0
Downloads
1,876
Comments
0
Likes
176
Embeds 0
No embeds

No notes for slide

The Art of AngularJS - DeRailed 2014

  1. 1. The Art of Matt Raible • http://raibledesigns.com Photos by
  2. 2. Modern Principles in Web Development Design for mobile first (even if you’re not building a mobile app) Build only single page apps Create and use your own REST API “Sex sells” applies to web apps © 2014 Raible Designs
  3. 3. Jobs on Dice.com February 2014 500 375 250 125 © 2014 Raible Designs ut ko oc Kn be r Em lar gu An Ba ck bo n e 0
  4. 4. LinkedIn Skills February 2014 30,000 22,500 15,000 7,500 © 2014 Raible Designs be r Em ut ko oc Kn lar gu An Ba ck bo n e 0
  5. 5. Google Trends © 2014 Raible Designs
  6. 6. Indeed Job Trends Absolute Relative © 2014 Raible Designs
  7. 7. Stack Overflow © 2014 Raible Designs
  8. 8. Who wants to learn © 2014 Raible Designs ?
  9. 9. The History of AngularJS Started by Miško Hevery in 2009 GWT = 3 developers, 6 months AngularJS = 1 developer, 3 weeks Learn more: https://www.youtube.com/watch?v=X0VsStcCCM8 © 2014 Raible Designs
  10. 10. The History of AngularJS AngularJS GWT 18000 17,000 13500 9000 4500 0 1,000 Lines of Code © 2014 Raible Designs
  11. 11. Hello World <!doctype html> <html ng-app> <head> <title>Hello World</title> </head> <body> <div> <label>Name:</label> <input type="text" ng-model="name" placeholder="Enter a name here"> <hr> <h1>Hello {{name}}!</h1> </div> <script src="http://code.angularjs.org/1.2.13/angular.min.js"></script> </body> </html> © 2014 Raible Designs
  12. 12. Architecture Principles Boilerplate Structure D.R.Y. Testability © 2014 Raible Designs
  13. 13. Code Organization Start with Angular Seed* ! git clone https://github.com/angular/angular-seed.git ! ! ! * more options to be discussed later… © 2014 Raible Designs
  14. 14. App Definition var app = angular.module('myApp', []); <!DOCTYPE html> <html ng-app="myApp"> © 2014 Raible Designs
  15. 15. App Definition with separate files app.js ! ! angular.module('myApp', ['ngRoute', 'myApp.filters', 'myApp.services', 'myApp.directives', 'myApp.controllers' ]) controllers.js angular.module('myApp.controllers', []). controller('MyCtrl1', [function() { ! }]) © 2014 Raible Designs
  16. 16. Model View Controller © 2014 Raible Designs
  17. 17. Data Binding friend.js ! $scope.friend = { name: "Fernand" }; ! friend.html {{friend.name}} // 1-way <input ng-model="friend.name"> // 2-way © 2014 Raible Designs
  18. 18. Solving FOUC This will work just fine — if it’s not on the first page: ! <p>{{friend.name}}</p> Use ng-cloak or ng-bind attribute: <p ng-cloak>{{friend.name}}</p> ! <p ng-bind="friend.name"></p> © 2014 Raible Designs
  19. 19. Directives <div ng-repeat="entry in news.entries"> <span ng-bind="entry.title"></span> <button ng-click="delete($index)"> Delete </button> </div> © 2014 Raible Designs
  20. 20. Directives with valid HTML5 <div data-ng-repeat="entry in news.entries"> <span data-ng-bind="entry.title"></span> <button data-ng-click="delete($index)"> Delete </button> </div> <div data-ng:repeat="entry in news.entries"> <span data-ng:bind="entry.title"></span> <button data-ng:click="delete($index)"> Delete </button> </div> © 2014 Raible Designs
  21. 21. Custom Directives $scope.customer = { name: 'Franklin', address: '1830 Blake' }; <div ng-controller="MyController"> <my-customer></my-customer> </div> .directive('myCustomer', function() { return { template: 'Name: {{customer.name}} Address: {{customer.address}}' }; }); © 2014 Raible Designs
  22. 22. Built-In Directives ng-href ng-selected ng-src ng-class ng-disabled ng-style ng-checked ! ng-readonly © 2014 Raible Designs
  23. 23. Services var services = angular.module('myApp.services', ['ngResource']); ! services.factory('LoginService', function($resource) { return $resource(':action', {}, { authenticate: { method: 'POST', params: {'action': 'authenticate'}, headers: {'Content-Type': 'application/x-www-form-urlencoded'} } } ); }); ! services.factory('NewsService', function($resource) { return $resource('news/:id', {id: '@id'}); }); © 2014 Raible Designs
  24. 24. $http $http({method: 'GET', url: '/news'}). success(function(data, status, headers, config) { // this callback will be called asynchronously // when the response is available }). error(function(data, status, headers, config) { // called asynchronously if an error occurs // or server returns response with an error status. }); $http.get('/news').success(successCallback); $http.post('/news', data).success(successCallback); © 2014 Raible Designs
  25. 25. $q myApp.factory('HelloWorld', function($q, $timeout) { ! var getMessages = function() { var deferred = $q.defer(); ! $timeout(function() { deferred.resolve(['Hello', 'world!']); }, 2000); ! return deferred.promise; }; ! return { getMessages: getMessages }; }); © 2014 Raible Designs
  26. 26. $q myApp.controller('HelloCtrl', function($scope, HelloWorld) { ! HelloWorld.getMessages().then(function(messages) { $scope.messages = messages; }); ! }); © 2014 Raible Designs
  27. 27. Dependency Injection .controller('LoginController', function($scope, $rootScope, $location, $http, $cookieStore, LoginService) { $scope.login = function () { LoginService.authenticate($.param({username: $scope.username, 
 password: $scope.password}), function (user) { $rootScope.user = user; $http.defaults.headers.common[xAuthTokenHeaderName] = user.token; $cookieStore.put('user', user); $location.path("/"); }); }; }) © 2014 Raible Designs
  28. 28. Filters ! ! ! ! {{ name | uppercase }} ! <!-- Displays: 123.46 --> {{ 123.456789 | number:2 }} ! <!-- In en-US locale, '$1000.00' will be shown --> {{ 1000 | currency }} ! <!-- all of the words with e in them ["Lerner","Likes","Eat"] --> {{ ['Ari', 'Lerner', 'Likes', 'To', 'Eat', 'Pizza'] | filter:'e' }} ! also: lowercase, limitTo, orderBy © 2014 Raible Designs
  29. 29. Routes .config(['$routeProvider', '$locationProvider', '$httpProvider', function ($routeProvider, $locationProvider, $httpProvider) { $routeProvider.when('/create', { templateUrl: 'partials/create.html', controller: 'CreateController' }); $routeProvider.when('/edit/:id', { templateUrl: 'partials/edit.html', controller: 'EditController' }); $routeProvider.when('/login', { templateUrl: 'partials/login.html', controller: 'LoginController' }); $routeProvider.otherwise({ templateUrl: 'partials/index.html', controller: 'IndexController' }); ! $locationProvider.hashPrefix('!'); }] ) © 2014 Raible Designs
  30. 30. Routing: Navigation $rootScope.logout = function () { delete $rootScope.user; delete $http.defaults.headers.common[xAuthTokenHeaderName]; $cookieStore.remove('user'); $location.path("/login"); }; © 2014 Raible Designs
  31. 31. Routing: Navigation $rootScope.logout = function () { delete $rootScope.user; delete $http.defaults.headers.common[xAuthTokenHeaderName]; $cookieStore.remove('user'); $location.path("/login"); }; © 2014 Raible Designs
  32. 32. Code Organization Revisited Lineman helps you build fat-client JavaScript apps It produces happiness by building assets, mocking servers, and running specs on every file change git clone https://github.com/linemanjs/lineman-angular-template.git my-app cd my-app sudo npm install -g lineman npm install lineman run © 2014 Raible Designs
  33. 33. Google's Recommendations for Angular App Structure
  34. 34. Testing Testem - test runner, framework agnostic Jasmine - unit tests, framework agnostic Protractor - integration tests, angular specific Lineman - productivity, framework agnostic © 2014 Raible Designs
  35. 35. Testing: Controllers describe("controller: LoginController", function() { ! beforeEach(function() { module("app"); }); ! beforeEach(inject(function($controller, $rootScope, $location, AuthenticationService, $httpBackend) { this.$location = $location; this.$httpBackend = $httpBackend; this.scope = $rootScope.$new(); this.redirect = spyOn($location, 'path'); $controller('LoginController', { $scope: this.scope, $location: $location, AuthenticationService: AuthenticationService }); })); © 2014 Raible Designs
  36. 36. Testing: Controllers afterEach(function() { this.$httpBackend.verifyNoOutstandingRequest(); this.$httpBackend.verifyNoOutstandingExpectation(); }); ! describe("successfully logging in", function() { it("should redirect you to /home", function() { this.$httpBackend.expectPOST('/login', this.scope.credentials).respond(200); this.scope.login(); this.$httpBackend.flush(); expect(this.redirect).toHaveBeenCalledWith('/home'); }); }); }); © 2014 Raible Designs
  37. 37. Testing: Directives beforeEach(inject(function($rootScope, $compile) { this.directiveMessage = 'ralph was here'; this.html = "<div shows-message-when-hovered message='" + this.directiveMessage + "'></div>"; this.scope = $rootScope.$new(); this.scope.message = this.originalMessage = 'things are looking grim'; this.elem = $compile(this.html)(this.scope); })); ! describe("when a user mouses over the element", function() { it("sets the message on the scope to the message attribute", function() { this.elem.triggerHandler('mouseenter'); expect(this.scope.message).toBe(this.directiveMessage); }); }); © 2014 Raible Designs
  38. 38. Testing: Directives with CoffeeScript describe "directive: shows-message-when-hovered (coffeescript)", -> ! Given -> module("app") ! Given inject ($rootScope, $compile) -> @directiveMessage = 'ralph was here' @html = "<div shows-message-when-hovered message='#{@directiveMessage}'></div>" @scope = $rootScope.$new() @scope.message = @originalMessage = 'things are looking grim' @elem = $compile(@html)(@scope) ! describe "when a user mouses over the element", -> When -> @elem.triggerHandler('mouseenter') Then "the message on the scope is set to the message attribute", -> @scope.message == @directiveMessage © 2014 Raible Designs
  39. 39. Testing: End-to-End protractor = require("protractor") require "protractor/jasminewd" require 'jasmine-given' ! describe "my angular app", -> ptor = protractor.getInstance() describe "visiting the login page", -> Given -> ptor.get "/" ! describe "when a user logs in", -> Given -> ptor.findElement(protractor.By.input("credentials.username")).sendKeys "Ralph" Given -> ptor.findElement(protractor.By.input("credentials.password")).sendKeys "Wiggum" When -> ptor.findElement(protractor.By.id("log-in")).click() Then -> ptor.findElement(protractor.By.binding("{{ message }}")).getText().then (text) -> expect(text).toEqual "Mouse Over these images to see a directive at work" © 2014 Raible Designs
  40. 40. Building with Grunt sudo npm install ! sudo npm install -g grunt-cli ! vi package.json ! "grunt": "~0.4.1", "grunt-contrib-concat": "~0.3.0", "grunt-contrib-uglify": "~0.2.7", "grunt-contrib-cssmin": "~0.7.0", "grunt-usemin": "~2.0.2", "grunt-contrib-copy": "~0.5.0", "grunt-rev": "~0.1.0", "grunt-contrib-clean": "~0.5.0", "matchdep": "~0.3.0" © 2014 Raible Designs
  41. 41. Gruntfile.js module.exports = function (grunt) { ! ! ! ! grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), clean: ["dist", '.tmp'], copy: { main: { expand: true, cwd: 'app/', src: ['**', '!js/**', '!lib/**', '!**/*.css'], dest: 'dist/' } }, rev: { files: { src: ['dist/**/*.{js,css}'] } }, © 2014 Raible Designs
  42. 42. Gruntfile.js ! useminPrepare: { html: 'app/index.html' }, ! usemin: { html: ['dist/index.html'] }, ! uglify: { options: { report: 'min', mangle: false } } }); ! © 2014 Raible Designs
  43. 43. Gruntfile.js require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks); ! // Tell Grunt what to do when we type "grunt" into the terminal grunt.registerTask('default', [ 'copy', 'useminPrepare', 'concat', 'uglify', 'cssmin', 'rev', 'usemin' ]); }; © 2014 Raible Designs
  44. 44. index.html comments <head> <title>My AngularJS App</title> <!-- build:css css/seed.min.css --> <link rel="stylesheet" href="css/app.css"/> <link rel="stylesheet" href="css/app2.css"/> <!-- endbuild --> </head> <body> <!-- build:js js/seed.min.js --> <script src="lib/angular/angular.js"></script> <script src="lib/angular/angular-route.js"></script> <script src="js/app.js"></script> <script src="js/services.js"></script> <script src="js/controllers.js"></script> <script src="js/filters.js"></script> <script src="js/directives.js"></script> <!-- endbuild --> </body> © 2014 Raible Designs
  45. 45. dist/index.html <head> <title>My AngularJS App</title> <link rel="stylesheet" href="css/f050d0dc.seed.min.css"/> </head> <body> ! <script src="js/8973cf0f.seed.min.js"></script> </body> © 2014 Raible Designs
  46. 46. After Grunt http://raibledesigns.com/rd/entry/using_grunt_with_angularjs_for © 2014 Raible Designs
  47. 47. You shouldn’t have to worry about FEO http://raibledesigns.com/rd/entry/you_shouldn_t_have_to © 2014 Raible Designs
  48. 48. HTTP/2 Performance Anti-Patterns? Split dominant content domains Reduce requests Merging Sprites DataURIs http://www.slideshare.net/andydavies © 2014 Raible Designs
  49. 49. CloudBees © 2014 Raible Designs
  50. 50. UI Bootstrap http://angular-ui.github.io/bootstrap <script src="lib/angular/ui-bootstrap-0.10.0.min.js"></script> <script src="lib/angular/ui-bootstrap-tpls-0.10.0.min.js"></script> angular.module('myApp', ['ui.bootstrap']); © 2014 Raible Designs
  51. 51. Ionic Framework http://ionicframework.com © 2014 Raible Designs
  52. 52. AngularJS Batarang © 2014 Raible Designs
  53. 53. My Experience Developing with AngularJS Series Part I: The Basics
 Part II: Dialogs and Data
 Part III: Services
 Part IV: Making it Pop
 © 2014 Raible Designs
  54. 54. My Experience http://vimeo.com/mraible/angularjs-deep-dive #dv13javaweb$ © 2014 Raible Designs
  55. 55. How to Become an Artist Part 1 of 3: Learn the Basics on Your Own Take some time and try various mediums of art Recognize your strengths Do your research and learn the basics Get the supplies you will need Observe the world around you Make time for your art every day Seek out the opinions of others Develop your own style http://www.wikihow.com/Become-an-Artist © 2014 Raible Designs
  56. 56. Shortcut to becoming an Angular Artist JUST DO IT. © 2014 Raible Designs
  57. 57. Questions? Contact Me! http://raibledesigns.com @mraible Presentations http://slideshare.net/mraible Code http://github.com/mraible © 2014 Raible Designs
  58. 58. Who to follow on Twitter AngularJS Team at Google Web Performance Miško Hevery - @mhevery Ilya Grigorik - @igrigorik Igor Minar - @IgorMinar Andy Davis - @andydavies Brian Ford - @briantford Steve Souders - @Souders ! © 2014 Raible Designs
  59. 59. Resources Angular Dart https://angulardart.org Devoxx AngularJS Talks on Parleys.com http://parleys.com/play/5148922b0364bc17fc56c91b (2012) http://parleys.com/play/529321a5e4b054cd7d2ef4e1 (2013) Egghead.io - https://egghead.io/ © 2014 Raible Designs
  60. 60. Code Angular Seed https://github.com/angular/angular-seed Lineman Application Template using AngularJS https://github.com/linemanjs/lineman-angular-template AngularJS + Rest + Spring Security https://github.com/joshlong/boot-examples/tree/master/x-auth-security © 2014 Raible Designs

×