Photos by The Art of in 2015 Matt Raible • http://raibledesigns.com
© 2014 Raible Designs Blogger on raibledesigns.com Founder of AppFuse Father, Skier, Mountain Biker, Whitewater Rafter Web Framework Connoisseur Who is Matt Raible? Bus Lover
© 2015 Raible Designs Devoxx4Kids Denver • Teaching Kids to Program • Java, Minecraft, robots, oh my! • Non profit, looking for donations and speakers http://www.meetup.com/Devoxx4Kids-Denver/
© 2015 Raible Designs 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
© 2015 Raible Designs Jobs on Dice.com January 2015 0 225 450 675 900 Backbone Angular Em ber Knockout React
© 2015 Raible Designs LinkedIn Skills January 2015 0 25,000 50,000 75,000 100,000 Backbone Angular Knockout Em ber React
© 2015 Raible Designs Google Trends
© 2015 Raible Designs Indeed Job Trends Absolute Relative
© 2015 Raible Designs Stack Overflow
© 2015 Raible Designs Who wants to learn ?
© 2015 Raible Designs 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
© 2015 Raible Designs The History of AngularJS 0 4500 9000 13500 18000 Lines of Code 17,000 1,000 AngularJS GWT
© 2015 Raible Designs 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.3.11/angular.min.js"></script> </body> </html>
© 2015 Raible Designs Architecture Principles Structure Testability Boilerplate D.R.Y.
© 2015 Raible Designs Code Organization Start with Angular Seed* * more options to be discussed later… git clone https://github.com/angular/angular-seed.git
© 2015 Raible Designs Quick Demo
© 2015 Raible Designs App Definition var app = angular.module('myApp', []); <!DOCTYPE html> <html ng-app="myApp">
© 2015 Raible Designs App Definition with separate files app.js controllers.js angular.module('myApp', ['ngRoute', 'myApp.filters', 'myApp.services', 'myApp.directives', 'myApp.controllers' ]) angular.module('myApp.controllers', []). controller('MyCtrl1', [function() { }])
© 2015 Raible Designs Model View Controller
© 2015 Raible Designs Data Binding friend.js friend.html $scope.friend = { name: "Fernand" }; {{friend.name}} // 1-way <input ng-model="friend.name"> // 2-way
© 2015 Raible Designs Solving FOUC This will work just fine — if it’s not on the first page: Use ng-cloak or ng-bind attribute: <p>{{friend.name}}</p> <p ng-cloak>{{friend.name}}</p> <p ng-bind="friend.name"></p>
© 2015 Raible Designs Directives <div ng-repeat="entry in news.entries"> <span ng-bind="entry.title"></span> <button ng-click="delete($index)"> Delete </button> </div>
© 2015 Raible Designs 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>
© 2015 Raible Designs 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}}' }; });
© 2015 Raible Designs Built-In Directives ng-href ng-src ng-disabled ng-checked ng-readonly ng-selected ng-class ng-style
© 2015 Raible Designs 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'}); });
© 2015 Raible Designs $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);
© 2015 Raible Designs $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 }; });
© 2015 Raible Designs $q myApp.controller('HelloCtrl', function($scope, HelloWorld) { HelloWorld.getMessages().then(function(messages) { $scope.messages = messages; }); });
© 2015 Raible Designs 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("/"); }); }; })
© 2015 Raible Designs Filters also: lowercase, limitTo, orderBy {{ 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' }}
© 2015 Raible Designs 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('!'); }] )
© 2015 Raible Designs Routing: Navigation $rootScope.logout = function () { delete $rootScope.user; delete $http.defaults.headers.common[xAuthTokenHeaderName]; $cookieStore.remove('user'); $location.path("/login"); };
© 2015 Raible Designs Routing: Navigation $rootScope.logout = function () { delete $rootScope.user; delete $http.defaults.headers.common[xAuthTokenHeaderName]; $cookieStore.remove('user'); $location.path("/login"); };
© 2015 Raible Designs 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
Google's Recommendations for Angular App Structure
© 2015 Raible Designs Testing Karma - test runner, framework agnostic Jasmine - unit tests, framework agnostic Protractor - integration tests, angular specific Lineman - productivity, framework agnostic
© 2015 Raible Designs 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 }); }));
© 2015 Raible Designs 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'); }); }); });
© 2015 Raible Designs 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); }); });
© 2015 Raible Designs 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
© 2015 Raible Designs 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"
© 2015 Raible Designs 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"
© 2015 Raible Designs 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}'] } },
© 2015 Raible Designs Gruntfile.js useminPrepare: { html: 'app/index.html' }, usemin: { html: ['dist/index.html'] }, uglify: { options: { report: 'min', mangle: false } } });
© 2015 Raible Designs 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' ]); };
© 2015 Raible Designs 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>
© 2015 Raible Designs 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>
© 2015 Raible Designs After Grunt http://raibledesigns.com/rd/entry/using_grunt_with_angularjs_for
© 2015 Raible Designs You shouldn’t have to worry about FEO http://raibledesigns.com/rd/entry/you_shouldn_t_have_to
© 2015 Raible Designs HTTP/2 Performance Anti-Patterns? Split dominant content domains Reduce requests Merging Sprites DataURIs http://www.slideshare.net/andydavies
© 2015 Raible Designs UI Bootstrap http://angular-ui.github.io/bootstrap <script src="lib/angular/ui-bootstrap-0.12.0.min.js"></script> <script src="lib/angular/ui-bootstrap-tpls-0.12.0.min.js"></script> angular.module('myApp', ['ui.bootstrap']);
© 2015 Raible Designs UI Bootstrap: Carousel
© 2015 Raible Designs UI Bootstrap: Carousel <div ng-controller="CarouselDemoCtrl"> <div style="height: 305px"> <carousel interval="myInterval"> <slide ng-repeat="slide in slides" active="slide.active"> <img ng-src="{{slide.image}}" style="margin:auto;"> <div class="carousel-caption"> <h4>Slide {{$index}}</h4> <p>{{slide.text}}</p> </div> </slide> </carousel> </div> </div>
© 2015 Raible Designs Foundation for Apps http://foundation.zurb.com/apps
© 2015 Raible Designs Foundation
© 2015 Raible Designs Foundation
© 2015 Raible Designs Sass Mixins <div class="row"> <main class="medium-9 columns"> <p>Main content</p> </main> <aside class="medium-3 columns"> <p>Sidebar</p> </aside> </div>
© 2015 Raible Designs Sass Mixins <div class="layout"> <main class="layout-content"> <p>Main content</p> </main> <aside class="layout-sidebar"> <p>Sidebar</p> </aside> </div>
© 2015 Raible Designs Sass Mixins http://bit.ly/1Kh1ha5 @import "foundation/components/grid"; .layout { // `layout` container functions as a row @include grid-row(); } .layout-content { // Mobile-first: make `layout-container` full-width @include grid-column(12); // On medium-up size, make `layout-container` 9 columns wide @media #{$medium-up} { @include grid-column(9); } } .layout-sidebar { // Mobile-first: make `layout-sidebar` full-width @include grid-column(12); // On medium-up size, make `layout-sidebar` 3 columns wide @media #{$medium-up} { @include grid-column(3); } }
© 2015 Raible Designs Ionic Framework http://ionicframework.com
© 2015 Raible Designs#dv13javaweb$ My Ionic Experience http://raibledesigns.com/rd/entry/developing_an_ios_native_app
© 2015 Raible Designs JHipster http://jhipster.github.io/
© 2015 Raible Designs JHipster Spring Boot Spring Security AngularJS Bootstrap Bower Metrics Java 7 or Java 8 Maven or Gradle Authentication Type: cookie-based or OAuth2 Type of Database: SQL or NoSQL Caching: EhCache or Hazelcast Grunt or Gulp.js http://jhipster.github.io/ Foundational Frameworks Project Options
© 2015 Raible Designs JHipster
© 2015 Raible Designs JHipster: Metrics
© 2015 Raible Designs JHipster: Code Generation
© 2015 Raible Designs JHipster: Code Generation
© 2015 Raible Designs AngularJS Batarang
© 2015 Raible Designs My Experience in 2013 Developing with AngularJS Series Part I: The Basics
 Part II: Dialogs and Data
 Part III: Services
 Part IV: Making it Pop

© 2015 Raible Designs#dv13javaweb$ My Experience in 2013 http://vimeo.com/mraible/angularjs-deep-dive
© 2015 Raible Designs 2015 AngularJS Tutorials Getting Started with AngularJS http://raibledesigns.com/rd/entry/getting_started_with_angularjs Testing AngularJS Applications http://raibledesigns.com/rd/entry/testing_angularjs_applications
© 2015 Raible Designs Spring and AngularJS http://spring.io/blog http://spring.io/blog/2015/01/12/spring-and-angular-js-a-secure-single-page-application
© 2015 Raible Designs Angular 2.0 <input type="text" [value]="firstName"> <button (click)="addPerson()">Add</button> <input type="checkbox" [checked]="someProperty">
© 2015 Raible Designs Concepts Eliminated in 2.0 Controllers Directive Definition Object $scope angular.module jqLite
© 2015 Raible Designs The Bad News No migration path from Angular 1.x to 2.0 Angular 1.3 will be supported for 1.5 - 2 years Will only support Evergreen Browsers (e.g. IE10+) Learn more on http://www.infoq.com/news/2014/10/angular-2-atscript
http://12factor.net/
© 2015 Raible Designs 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
© 2015 Raible Designs Shortcut to becoming an Angular Artist JUST DO IT.
© 2015 Raible Designs Contact Me! http://raibledesigns.com @mraible Presentations http://slideshare.net/mraible Code http://github.com/mraible Questions?
© 2015 Raible Designs Who to follow on Twitter AngularJS Team at Google Miško Hevery - @mhevery Igor Minar - @IgorMinar Brian Ford - @briantford Web Performance Ilya Grigorik - @igrigorik Andy Davis - @andydavies Steve Souders - @Souders
© 2015 Raible Designs 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/ Resources
© 2015 Raible Designs 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 Code

The Art of AngularJS in 2015

  • 1.
    Photos by The Artof in 2015 Matt Raible • http://raibledesigns.com
  • 2.
    © 2014 RaibleDesigns Blogger on raibledesigns.com Founder of AppFuse Father, Skier, Mountain Biker, Whitewater Rafter Web Framework Connoisseur Who is Matt Raible? Bus Lover
  • 3.
    © 2015 RaibleDesigns Devoxx4Kids Denver • Teaching Kids to Program • Java, Minecraft, robots, oh my! • Non profit, looking for donations and speakers http://www.meetup.com/Devoxx4Kids-Denver/
  • 4.
    © 2015 RaibleDesigns 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
  • 5.
    © 2015 RaibleDesigns Jobs on Dice.com January 2015 0 225 450 675 900 Backbone Angular Em ber Knockout React
  • 6.
    © 2015 RaibleDesigns LinkedIn Skills January 2015 0 25,000 50,000 75,000 100,000 Backbone Angular Knockout Em ber React
  • 7.
    © 2015 RaibleDesigns Google Trends
  • 8.
    © 2015 RaibleDesigns Indeed Job Trends Absolute Relative
  • 9.
    © 2015 RaibleDesigns Stack Overflow
  • 11.
    © 2015 RaibleDesigns Who wants to learn ?
  • 12.
    © 2015 RaibleDesigns 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
  • 13.
    © 2015 RaibleDesigns The History of AngularJS 0 4500 9000 13500 18000 Lines of Code 17,000 1,000 AngularJS GWT
  • 14.
    © 2015 RaibleDesigns 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.3.11/angular.min.js"></script> </body> </html>
  • 15.
    © 2015 RaibleDesigns Architecture Principles Structure Testability Boilerplate D.R.Y.
  • 16.
    © 2015 RaibleDesigns Code Organization Start with Angular Seed* * more options to be discussed later… git clone https://github.com/angular/angular-seed.git
  • 17.
    © 2015 RaibleDesigns Quick Demo
  • 18.
    © 2015 RaibleDesigns App Definition var app = angular.module('myApp', []); <!DOCTYPE html> <html ng-app="myApp">
  • 19.
    © 2015 RaibleDesigns App Definition with separate files app.js controllers.js angular.module('myApp', ['ngRoute', 'myApp.filters', 'myApp.services', 'myApp.directives', 'myApp.controllers' ]) angular.module('myApp.controllers', []). controller('MyCtrl1', [function() { }])
  • 20.
    © 2015 RaibleDesigns Model View Controller
  • 21.
    © 2015 RaibleDesigns Data Binding friend.js friend.html $scope.friend = { name: "Fernand" }; {{friend.name}} // 1-way <input ng-model="friend.name"> // 2-way
  • 22.
    © 2015 RaibleDesigns Solving FOUC This will work just fine — if it’s not on the first page: Use ng-cloak or ng-bind attribute: <p>{{friend.name}}</p> <p ng-cloak>{{friend.name}}</p> <p ng-bind="friend.name"></p>
  • 23.
    © 2015 RaibleDesigns Directives <div ng-repeat="entry in news.entries"> <span ng-bind="entry.title"></span> <button ng-click="delete($index)"> Delete </button> </div>
  • 24.
    © 2015 RaibleDesigns 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>
  • 25.
    © 2015 RaibleDesigns 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}}' }; });
  • 26.
    © 2015 RaibleDesigns Built-In Directives ng-href ng-src ng-disabled ng-checked ng-readonly ng-selected ng-class ng-style
  • 27.
    © 2015 RaibleDesigns 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'}); });
  • 28.
    © 2015 RaibleDesigns $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);
  • 29.
    © 2015 RaibleDesigns $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 }; });
  • 30.
    © 2015 RaibleDesigns $q myApp.controller('HelloCtrl', function($scope, HelloWorld) { HelloWorld.getMessages().then(function(messages) { $scope.messages = messages; }); });
  • 31.
    © 2015 RaibleDesigns 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("/"); }); }; })
  • 32.
    © 2015 RaibleDesigns Filters also: lowercase, limitTo, orderBy {{ 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' }}
  • 33.
    © 2015 RaibleDesigns 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('!'); }] )
  • 34.
    © 2015 RaibleDesigns Routing: Navigation $rootScope.logout = function () { delete $rootScope.user; delete $http.defaults.headers.common[xAuthTokenHeaderName]; $cookieStore.remove('user'); $location.path("/login"); };
  • 35.
    © 2015 RaibleDesigns Routing: Navigation $rootScope.logout = function () { delete $rootScope.user; delete $http.defaults.headers.common[xAuthTokenHeaderName]; $cookieStore.remove('user'); $location.path("/login"); };
  • 36.
    © 2015 RaibleDesigns 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
  • 39.
    Google's Recommendations forAngular App Structure
  • 41.
    © 2015 RaibleDesigns Testing Karma - test runner, framework agnostic Jasmine - unit tests, framework agnostic Protractor - integration tests, angular specific Lineman - productivity, framework agnostic
  • 42.
    © 2015 RaibleDesigns 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 }); }));
  • 43.
    © 2015 RaibleDesigns 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'); }); }); });
  • 44.
    © 2015 RaibleDesigns 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); }); });
  • 45.
    © 2015 RaibleDesigns 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
  • 46.
    © 2015 RaibleDesigns 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"
  • 47.
    © 2015 RaibleDesigns 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"
  • 48.
    © 2015 RaibleDesigns 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}'] } },
  • 49.
    © 2015 RaibleDesigns Gruntfile.js useminPrepare: { html: 'app/index.html' }, usemin: { html: ['dist/index.html'] }, uglify: { options: { report: 'min', mangle: false } } });
  • 50.
    © 2015 RaibleDesigns 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' ]); };
  • 51.
    © 2015 RaibleDesigns 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>
  • 52.
    © 2015 RaibleDesigns 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>
  • 53.
    © 2015 RaibleDesigns After Grunt http://raibledesigns.com/rd/entry/using_grunt_with_angularjs_for
  • 54.
    © 2015 RaibleDesigns You shouldn’t have to worry about FEO http://raibledesigns.com/rd/entry/you_shouldn_t_have_to
  • 55.
    © 2015 RaibleDesigns HTTP/2 Performance Anti-Patterns? Split dominant content domains Reduce requests Merging Sprites DataURIs http://www.slideshare.net/andydavies
  • 56.
    © 2015 RaibleDesigns UI Bootstrap http://angular-ui.github.io/bootstrap <script src="lib/angular/ui-bootstrap-0.12.0.min.js"></script> <script src="lib/angular/ui-bootstrap-tpls-0.12.0.min.js"></script> angular.module('myApp', ['ui.bootstrap']);
  • 57.
    © 2015 RaibleDesigns UI Bootstrap: Carousel
  • 58.
    © 2015 RaibleDesigns UI Bootstrap: Carousel <div ng-controller="CarouselDemoCtrl"> <div style="height: 305px"> <carousel interval="myInterval"> <slide ng-repeat="slide in slides" active="slide.active"> <img ng-src="{{slide.image}}" style="margin:auto;"> <div class="carousel-caption"> <h4>Slide {{$index}}</h4> <p>{{slide.text}}</p> </div> </slide> </carousel> </div> </div>
  • 59.
    © 2015 RaibleDesigns Foundation for Apps http://foundation.zurb.com/apps
  • 60.
    © 2015 RaibleDesigns Foundation
  • 61.
    © 2015 RaibleDesigns Foundation
  • 62.
    © 2015 RaibleDesigns Sass Mixins <div class="row"> <main class="medium-9 columns"> <p>Main content</p> </main> <aside class="medium-3 columns"> <p>Sidebar</p> </aside> </div>
  • 63.
    © 2015 RaibleDesigns Sass Mixins <div class="layout"> <main class="layout-content"> <p>Main content</p> </main> <aside class="layout-sidebar"> <p>Sidebar</p> </aside> </div>
  • 64.
    © 2015 RaibleDesigns Sass Mixins http://bit.ly/1Kh1ha5 @import "foundation/components/grid"; .layout { // `layout` container functions as a row @include grid-row(); } .layout-content { // Mobile-first: make `layout-container` full-width @include grid-column(12); // On medium-up size, make `layout-container` 9 columns wide @media #{$medium-up} { @include grid-column(9); } } .layout-sidebar { // Mobile-first: make `layout-sidebar` full-width @include grid-column(12); // On medium-up size, make `layout-sidebar` 3 columns wide @media #{$medium-up} { @include grid-column(3); } }
  • 65.
    © 2015 RaibleDesigns Ionic Framework http://ionicframework.com
  • 66.
    © 2015 RaibleDesigns#dv13javaweb$ My Ionic Experience http://raibledesigns.com/rd/entry/developing_an_ios_native_app
  • 67.
    © 2015 RaibleDesigns JHipster http://jhipster.github.io/
  • 68.
    © 2015 RaibleDesigns JHipster Spring Boot Spring Security AngularJS Bootstrap Bower Metrics Java 7 or Java 8 Maven or Gradle Authentication Type: cookie-based or OAuth2 Type of Database: SQL or NoSQL Caching: EhCache or Hazelcast Grunt or Gulp.js http://jhipster.github.io/ Foundational Frameworks Project Options
  • 69.
    © 2015 RaibleDesigns JHipster
  • 70.
    © 2015 RaibleDesigns JHipster: Metrics
  • 71.
    © 2015 RaibleDesigns JHipster: Code Generation
  • 72.
    © 2015 RaibleDesigns JHipster: Code Generation
  • 73.
    © 2015 RaibleDesigns AngularJS Batarang
  • 74.
    © 2015 RaibleDesigns My Experience in 2013 Developing with AngularJS Series Part I: The Basics
 Part II: Dialogs and Data
 Part III: Services
 Part IV: Making it Pop

  • 75.
    © 2015 RaibleDesigns#dv13javaweb$ My Experience in 2013 http://vimeo.com/mraible/angularjs-deep-dive
  • 76.
    © 2015 RaibleDesigns 2015 AngularJS Tutorials Getting Started with AngularJS http://raibledesigns.com/rd/entry/getting_started_with_angularjs Testing AngularJS Applications http://raibledesigns.com/rd/entry/testing_angularjs_applications
  • 77.
    © 2015 RaibleDesigns Spring and AngularJS http://spring.io/blog http://spring.io/blog/2015/01/12/spring-and-angular-js-a-secure-single-page-application
  • 78.
    © 2015 RaibleDesigns Angular 2.0 <input type="text" [value]="firstName"> <button (click)="addPerson()">Add</button> <input type="checkbox" [checked]="someProperty">
  • 79.
    © 2015 RaibleDesigns Concepts Eliminated in 2.0 Controllers Directive Definition Object $scope angular.module jqLite
  • 80.
    © 2015 RaibleDesigns The Bad News No migration path from Angular 1.x to 2.0 Angular 1.3 will be supported for 1.5 - 2 years Will only support Evergreen Browsers (e.g. IE10+) Learn more on http://www.infoq.com/news/2014/10/angular-2-atscript
  • 81.
  • 82.
    © 2015 RaibleDesigns 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
  • 83.
    © 2015 RaibleDesigns Shortcut to becoming an Angular Artist JUST DO IT.
  • 84.
    © 2015 RaibleDesigns Contact Me! http://raibledesigns.com @mraible Presentations http://slideshare.net/mraible Code http://github.com/mraible Questions?
  • 85.
    © 2015 RaibleDesigns Who to follow on Twitter AngularJS Team at Google Miško Hevery - @mhevery Igor Minar - @IgorMinar Brian Ford - @briantford Web Performance Ilya Grigorik - @igrigorik Andy Davis - @andydavies Steve Souders - @Souders
  • 86.
    © 2015 RaibleDesigns 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/ Resources
  • 87.
    © 2015 RaibleDesigns 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 Code