Introduction to AngularJS (@oakjug June 2013)
Upcoming SlideShare
Loading in...5
×
 

Introduction to AngularJS (@oakjug June 2013)

on

  • 2,282 views

A quick introduction to AngularJS

A quick introduction to AngularJS

Statistics

Views

Total Views
2,282
Views on SlideShare
2,279
Embed Views
3

Actions

Likes
5
Downloads
189
Comments
0

1 Embed 3

https://twitter.com 3

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

Introduction to AngularJS (@oakjug June 2013) Introduction to AngularJS (@oakjug June 2013) Presentation Transcript

  • @crichardson Introduction to AngularJS Chris Richardson Author of POJOs in Action Founder of the original CloudFoundry.com @crichardson chris@chrisrichardson.net http://plainoldobjects.com
  • @crichardson Agenda Introduction to AngularJS Anatomy of an AngularJS application Automated testing Development tools
  • @crichardson Browser WAR StoreFrontUI Model View Controller Presentation layer evolution.... HTML / HTTP + JavaScript
  • @crichardson Browser Web application RESTful EndpointsModel View Controller ...Presentation layer evolution JSON-REST HTML 5 - JavaScript No elaborate, server-side web framework required Event publisher Events Static content
  • @crichardson MV* frameworks for the browser
  • @crichardson
  • @crichardson AngularJS style MVC Template Controller Model <html> <body ng-app="..."> <h1>Hello {{name}}</h1> <form ng-submit="enterName()" > <input id="firstName" ng-model="firstName" > function MainCtrl ($scope) { ... $scope.enterName = function () { $scope.name = $scope.firstName + ' ' + $scope.lastName; }; MainCtrl Scope enterName: Function firstName: ... lastName: ... name: ... View
  • @crichardson Hello world
  • @crichardson Hello world - index.html <body ng-app="helloworldApp"> <div class="container" ng-view></div> <script src="components/angular/angular.js"></script> <script src="components/angular-resource/angular-resource.js"></script> <script src="components/angular-cookies/angular-cookies.js"></script> <script src="components/angular-sanitize/angular-sanitize.js"></script> <script src="scripts/app.js"></script> <script src="scripts/controllers/main.js"></script> </body> Application’s module and root element Where to render template
  • @crichardson Hello world - main.html <div class="hero-unit"> <h1 ng-show="name">Hello {{name}}</h1> <form name="nameForm" ng-submit="enterName()"> <label for="firstName">First Name:</label> <input id="firstName" type="text" name="firstName" size="30" placeholder="enter your first name" required ng-model="firstName" > <label for="lastName">Last Name:</label> <input id="lastName" type="text" name="lastName" size="30" placeholder="enter your last name" required ng-model="lastName" > <input id="enterNameButton" class="btn-primary" type="submit" value="Enter" ng-disabled="nameForm.$invalid"> </form> </div>
  • @crichardson Hello world - app.js angular.module('helloworldApp', []) .config(function ($routeProvider) { $routeProvider .when('/', { templateUrl: 'views/main.html', controller: 'MainCtrl' }) .otherwise({ redirectTo: '/' }); }); defines the routes URL view + controller creates module with dependencies Dependency injection http://localhost:9000/#/
  • @crichardson Hello world - controllers.js angular.module('helloworldApp') .controller('MainCtrl', function ($scope) { $scope.enterName = function () { $scope.name = $scope.firstName + ' ' + $scope.lastName; }; }); handle form submission Populate the $scope with data and callbacksretrieves module
  • @crichardson Agenda Introduction to AngularJS Anatomy of an AngularJS application Automated testing Development tools
  • @crichardson Food to Go
  • @crichardson Module and route definitions - app.js var orderTakingModule = angular.module('ordertaking', ['orderTakingServices', 'orderstate', 'deliveryinfoutils']). config(function ($routeProvider) { $routeProvider. when('/', {controller: 'DeliveryInfoCtrl', templateUrl: 'views/enterdeliveryinformation.html'}). when('/displayavailable', {controller: 'DisplayAvailableCtrl', templateUrl: 'views/displayavailable.html'}). when('/selectrestaurant/:id', {controller: 'SelectRestaurantCtrl', templateUrl: 'views/displaymenu.html'}). when('/ordersummary', {controller: 'DisplayOrderSummaryCtrl', templateUrl: 'views/ordersummary.html'}). when('/orderfinalsummary', {controller: 'PlaceOrderCtrl', templateUrl: 'views/orderfinalsummary.html'}). when('/orderconfirmation', {controller: 'DisplayOrderConfirmationCtl', templateUrl: 'views/orderconfirmation.html'}). when('/aboutus', {templateUrl: 'views/aboutus.html'}). when('/help', {templateUrl: 'views/help.html'}). when('/howitworks', {templateUrl: 'views/howitworks.html'}). otherwise({redirectTo: '/'}); });
  • @crichardson controller - DeliveryInfoCtrl orderTakingModule.controller('DeliveryInfoCtrl', function ($scope, $location, AvailableRestaurants, OrderState, DeliveryInfoUtils) { $scope.orderState = OrderState; $scope.deliveryHours = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; $scope.deliveryMinutes = [0, 15, 30, 45]; var now = new Date(); $scope.currentTime = now.getHours() * 100 + now.getMinutes(); $scope.isNotInFuture = function () { return DeliveryInfoUtils.isDeliveryTimeNotInFuture($scope.deliveryTime); } $scope.showAvailableRestaurants = function () { .... }; }); the “cart” for selectors validation form submission
  • @crichardson “Shopping cart” - orderstate.coffee angular.module('orderstate', []). factory('OrderState', -> class OrderState menuItemsMaybe: -> @selectedRestaurant?.menuItems || [] getMenuItemQuantities: -> mi.quantity for mi in @menuItemsMaybe() getSelectedMenuItems: -> mi for mi in @menuItemsMaybe() when mi.quantity > 0 noteUpdatedMenuItemQuantities: -> @selectedMenuItems = @getSelectedMenuItems() @totalCost = 0 for mi in @selectedMenuItems @totalCost = @totalCost + mi.quantity * mi.price makeOrder: -> deliveryInfo: @deliveryInfo restaurantId: @selectedRestaurant.id orderLineItems: {name: mi.name, quantity: mi.quantity} for mi in @selectedMenuItems return new OrderState(); );
  • @crichardson View template - enterdeliveryinfo.html <form name="deliveryInfoForm" ng-submit="showAvailableRestaurants()"> <label for="zipCode">Zip code:</label> <input type="text" name="zipCode" ng-model="deliveryZipCode" size="5" placeholder="enter your zip code" required ng-pattern="/^[0-9]{5,5}$/"> <label for="deliveryTimeHour">Delivery Time:</label> <select ng-model="deliveryTime.hour" ng-options="i for i in deliveryHours" required style="width: 4em"> </select> <select ng-model="deliveryTime.minute" ng-options="i for i in deliveryMinutes" required style="width: 4em"> </select> <select ng-model="deliveryTime.ampm" required style="width: 4em"> <option value="am">AM</option> <option value="pm">PM</option> </select> <span class="error" ng-show="isNotInFuture()">Please pick a time in the future</span> <br/> <input id="enterDeliveryInfoButton" class="btn-primary" type="submit" value="Next" ng-disabled="isNotInFuture() || deliveryInfoForm.$invalid"> </form>
  • @crichardson Fetching available restaurants orderTakingModule.controller('DeliveryInfoCtrl', function ($scope, $location, AvailableRestaurants, OrderState, DeliveryInfoUtils) { ... $scope.showAvailableRestaurants = function () { var deliveryInfo = DeliveryInfoUtils.makeDeliveryInfo($scope.deliveryTime, $scope.deliveryZipCode); OrderState.deliveryInfo = deliveryInfo; AvailableRestaurants.get({zipcode: deliveryInfo.address.zipcode, dayOfWeek: deliveryInfo.time.dayOfWeek, hour: deliveryInfo.time.hour, minute: deliveryInfo.time.minute}, function (ars) { OrderState.availableRestaurants = ars.availableRestaurants; $location.path('/displayavailable'); }); }; }); update cart query server update cartchange view
  • @crichardson AvailableRestaurants resource angular.module('orderTakingServices', ['ngResource']). factory('AvailableRestaurants', function($resource) { return $resource('/app/availablerestaurants'); }); CRUD methods RESTful request
  • @crichardson Available restaurants
  • @crichardson Displaying available - route and controller orderTakingModule.controller('DisplayAvailableCtrl', function ($scope, OrderState) { $scope.orderState = OrderState; }); when('/displayavailable', {controller: 'DisplayAvailableCtrl', templateUrl: 'views/displayavailable.html'})
  • @crichardson Displaying available - view <span>Delivering to {{orderState.deliveryInfo.address.zipcode}} at {{orderState.deliveryInfo.timeOfDay}}</span> <table id="availableRestaurantsTable"> <tr ng-repeat="r in orderState.availableRestaurants"> <td>{{r.name}}</td> <td> <a href="#/selectrestaurant/{{r.id}}">select</a> </td> </tr> </table> iterate through restaurants select restaurant
  • @crichardson Creating an order
  • @crichardson Getting restaurant details orderTakingModule.controller('SelectRestaurantCtrl', function SelectRestaurantCtrl($scope, $routeParams, OrderState, Restaurant, $location) { $scope.orderState = OrderState; OrderState.selectedRestaurant = Restaurant.get({id: $routeParams.id}); $scope.$watch("orderState.getMenuItemQuantities()", OrderState.noteUpdatedMenuItemQuantities.bind(OrderState), true); $scope.displayOrderSummary = function () { $location.path('/ordersummary'); } }); Get restaurant from server call this method when this changes Contains values from URL
  • @crichardson Restaurants.js angular.module('orderTakingServices', ...). factory('Restaurant',function ($resource) { return $resource('/app/restaurant/:id'); }); CRUD methods RESTful request
  • @crichardson But wait, there’s more! Directives Define custom DOM attributes (or elements) Behavior or DOM transformation e.g. use JQuery UI widgets Filters for data formatting in the view Using $scope.$apply() to execute an expression in angular from outside it’s event loop ...
  • @crichardson Agenda Introduction to AngularJS Anatomy of an AngularJS application Automated testing Development tools
  • @crichardson AngularJS promotes testability Dependency injection simplifies unit testing MV* = Separation of concerns improves testability AngularJS provides mocks for unit testing Angular Scenario Runner for end to end testing
  • @crichardson Jasmine unit test describe('DeliveryInfoCtrl', function () { beforeEach(inject(function ($controller) { ctrl = $controller('DeliveryInfoCtrl', {$scope: scope, $location: location}); })); describe('showAvailableRestaurants', function () { var availableRestaurants = {...}; it('should fetch available restaurants', function () { $httpBackend.expectGET('/app/availablerestaurants?dayOfWeek=' + dayOfWeek + '&hour=18&minute=15&zipcode=94619'). respond(availableRestaurants); scope.deliveryTime = {hour: 6, minute: 15, ampm: "pm"}; scope.deliveryZipCode = "94619"; scope.showAvailableRestaurants(); $httpBackend.flush(); expect(location.newPath).toBe('/displayavailable'); expect(theOrderState.availableRestaurants).toEqual(availableRestaurants.availableRestaurants); }); }); Mock provided by AngularJS Override dependencies Setup form values Process async requests
  • @crichardson E2E testing with the Angular Scenario Runner describe('Order taking application', function() { it('should work', function() { browser().navigateTo('/'); expect(browser().location().url()).toBe('/'); input('deliveryZipCode').enter('94619'); select('deliveryTime.hour').option(6 - 1); select('deliveryTime.minute').option("45"); select('deliveryTime.ampm').option("pm"); element("#enterDeliveryInfoButton", "enterDeliveryInfoButton").click(); element("#availableRestaurantsTable a:first", "select available restaurant").click(); using("#menuTable tr:first", "mi quantity").input("mi.quantity").enter("3"); expect(element("#orderTotal").text()).toBe("297"); .... }); }); “Selenium-style” testing framework
  • @crichardson Agenda Introduction to AngularJS Anatomy of an AngularJS application Automated testing Development tools
  • @crichardson Scaffold Build, preview, test Package manager
  • @crichardson $ yo angular:app Generates skeleton application
  • @crichardson $ bower install [package] Installs JavaScript libraries { "name": "helloworld", "version": "0.0.0", "dependencies": { "angular": "~1.0.5", "json3": "~3.2.4", "es5-shim": "~2.0.8", "angular-resource": "~1.0.5", "angular-cookies": "~1.0.5", "angular-sanitize": "~1.0.5" }, "devDependencies": { "angular-mocks": "~1.0.5", "angular-scenario": "~1.0.5" } }
  • @crichardson $ grunt server Launches application in browser with live reloading
  • @crichardson $ grunt test Runs tests using the Karma test runner
  • @crichardson $ grunt build Packages application: runs jshint, minification, concatenation, ...
  • @crichardson Summary MVC has migrated to where it belongs: the browser AngularJS looks like pretty cool framework Yeoman and friends look promising
  • @crichardson Questions? @crichardson chris@chrisrichardson.net http://plainoldobjects.com - code and slides
  • @crichardson Links http://todomvc.com/ http://angularjs.org/ http://pivotal.github.io/jasmine/ http://yeoman.io/ http://karma-runner.github.io/0.8/index.html http://rmurphey.com/blog/2012/04/12/a-baseline-for-front- end-developers/ https://github.com/cer/polyglot-restaurant