Anzeige
Anzeige

Más contenido relacionado

Anzeige

AngularJS Workshop

  1. AngularJS Workshop Gianluca Cacace gianluca.cacace@gmail.com
  2. Summary ● What is AngularJS ○ The Web before AngularJS ○ Traditional Web Applications, SPAs ● How an AngularJS app is made ○ Modules, Controllers, Directives ○ Services, Providers, Factories ● Let’s build something ○ Angular Lab: build your own blog ● Advanced topics ○ Routing, custom directives, Transclusion, Directive- to-Directive Communication
  3. Let’s go back in time What is AngularJS?
  4. Legacy Javascript ● A lot of boilerplate code ● Hard coupling between UI and business logic ● No cross-browser compatibility var box = document.getElementById('box'), hasClass = function (el, cl) { var regex = new RegExp('(?:s|^)' + cl + '(?:s|$)'); return !!el.className.match(regex); }, addClass = function (el, cl) { el.className += ' ' + cl; }, removeClass = function (el, cl) { var regex = new RegExp('(?:s|^)' + cl + '(?:s|$)'); el.className = el.className.replace(regex, ' '); }, toggleClass = function (el, cl) { hasClass(el, cl) ? removeClass(el, cl) : addClass(el, cl); }; addClass(box, 'wrap'); removeClass(box, 'wrap'); toggleClass(box, 'wrap'); Legacy Javascript I’m a box!I’m a box! #box $('#box').addClass('wrap'); $('#box').removeClass('wrap'); $('#box').toggleClass('wrap');
  5. Traditional Web Application Client Server GET /blog GET /blog Page refresh
  6. ● Several OSs (Win, MacOSX, Linux, Mobile) ● Mobile devices are performant ● HTML5, CSS3, SVG give a better UX ● Write once, run everywhere The concept of app is evolving... ● Website user interfaces are becoming like standalone applications ● Fast loading of server-side data so...
  7. Single Page Application (SPA) Client Server GET / GET /api/blog/posts [{ "id": 123, "title": "Hello!" }, { "id": 125, "title": "Bye!" }]
  8. So, what is AngularJS?
  9. What is AngularJS? ● A structural framework for dynamic web apps ● A solution for creating SPA (One Page Application) ● MVC done right (separation of application logic, data models and views) ● A new way to enrich HTML, turning it in a declarative language ● Data models are plain Javascript objects ● Context aware communication ● Dependency Injection (Increased testability)
  10. Why AngularJS?
  11. How an Angular App is made
  12. View HTML CSS Controller Javascript API Model User Server
  13. Architecture overview Module Config Filter Directive Factory Controller Routes Service Provider Value
  14. Module You can think of a module as a container for the different parts of your app – controllers, services, filters, directives, etc. angular.module("fooLib", []); var myApp = angular.module("myApp", ["fooLib"]); In Java this concept is close to packages
  15. Controller ● Business logic for user interaction ● Model-View wiring code angular.module("myApp", []) .controller("MainCtrl", function($scope) { $scope.name = "John Doe"; $scope.countChars = function(text) { return text.length; } });
  16. Directive ● Reusable component (like a widget) ● View + Controller ● Declarative HTML angular.module("myApp", []) .directive("movieCard", function() { return { templateUrl: "partials/movie-card.html", controller: function($scope, $element) { [...] } }; });
  17. Service/Provider/Factory ● Lazy-loaded Singleton object ● Provide a method to keep data around for the lifetime of the app ● Communicate across controllers in a consistent manner angular.module("myApp", []) .factory("UserService", function($http) { var _currUser; return { getCurrentUser: function() { return _currUser; }, setCurrentUser: function(user) { _currUser = user; } } });
  18. The basics <html> <head> <script src="js/angular.min.js"></script> <script src="js/app.js"></script> </head> <body> <div ng-app="myFirstApp"> </div> </body> </head> </html> The library ng-app: boundary of an AngularJS app Our app components
  19. Setup Labs Environment 1. download labs here: https://goo.gl/cVI6De 2. open the workshop directory in Intellij IDEA 3. open the terminal (bottom left) 4. npm install 5. npm start
  20. http://jsbin.com/mejeno/edit ● Declarative HTML ● Automatic data binding (two-way data binding) ● Easy composability ● Application state organized in scopes ● IDs everywhere ● Low level DOM events management ● Mixed UI and behavioural logic ● Require knowledge of the entire DOM structure < ng-lab="0" />
  21. You will surely write less code! ● Registering callbacks ● Manipulating HTML DOM programmatically ● Marshalling data to and from the UI ● Writing tons of initialization code just to get started ● Registering callbacks ● Manipulating HTML DOM programmatically ● Marshalling data to and from the UI ● Writing tons of initialization code just to get started
  22. The magic of two-way data binding ● In classic web frameworks (no javascript), view reflects the model at render time. Template HTML Model template.render(model) View HTML Model snapshot
  23. The magic of two-way data binding ● In AngularJS the template is compiled into a reactive view, bindable to a changing model View HTML Reactive components linkFn = $compile(template) Model linkFn($scope) Template HTML with directives
  24. Scope ● Source of truth for the application state ● The glue between application controller and the view ● Follows a hierarchical structure ● Can propagate events and monitor watch expressions
  25. Scope Hierarchy $rootScope $scope $scope $scope $$nextSibling $parent $parent $parent
  26. Scope Inheritance $scope ● Child scope (prototypical inheritance) ● Isolated scope (no properties inheritance) p1 p2 $scope p1 p2 p3 $parent $scope p1 p2 $scopep3 $parent
  27. Scope Events ● Watch on scope changes ● Publish - Subscribe paradigm ● Powerful to keep components decoupled $watch $broadcast $emit $on Register to an expression on the model Publish from parent to children Publish from child to parents Subscribe to incoming events
  28. Scope Events - $watch var fooBar; $scope $scope $scope.$watch("fooBar", function(newValue, oldValue) { console.log(newValue) }); View Enter fooBar: My foobar
  29. $watch out for {{ }} ● Every time there is a change on the $scope, expressions on it and its children are evaluated and the result, if changed, reflected into the DOM ● Dangerous for long tables rendering (thousands of watches) One-time binding to the rescue! <div>{{ ::person.firstName }}</div>
  30. Scope Events - $broadcast $scope $scope $scope $scope $scope.$on("fooBar", function(evt, msg) { console.log(msg); }); $scope.$broadcast("fooBar", "This is an important message!");
  31. Scope Events - $emit $scope $scope $scope $scope $scope.$on("fooBar", function(evt, msg) { console.log(msg); }); $scope.$emit("fooBar", "This is an important message!"); $scope.$on("fooBar", function(evt, msg) { console.log(msg); });
  32. Who can create scopes? Controllers ● Using ng-controller ● Scopes can only be child scopes (prototypical inheritance) Directives ● With a specific configuration, when they are included in the HTML ● Scopes can be both child or isolated
  33. Built-in Directives All the ng- directives come from the core library: ● ng-show or ng-hide: show or hide a component ● ng-if: adds or removes a component from the DOM ● ng-switch: allocates or deallocates a component on the DOM based on a switch-case logic ● ng-click: runs an expression when clicking on a component ● ng-repeat: iterates an array and repeat a component ● ng-model: binds a form component to a scope property ● ng-class: conditionally adds or removes CSS classes
  34. Expressions ● Used all over AngularJS apps ● Executed in the current $scope context and have access to $scope properties ● They don’t throw errors if it’s TypeError or ReferenceError ● They don’t allow for any control flow functions (e.g., if/else) ● They can accept a filter and/or filter chains <div>{{ person.firstName }}</div> <div>{{ 5 * 2 + 10 }}</div> <div><input type="text" ng-model="num" /> <div ng-show="num > 10">Can you see me?</div>
  35. Directive ng-repeat ● Iterates over a collection and instantiates a new template for each item in the collection. ● Each item in the collection is given its own template and therefore its own scope. <ul ng-controller="PeopleController"> <li ng-repeat="person in people"> {{person.name}} lives in {{person.city}} </li> </ul>
  36. < ng-lab="1" /> ● Refactor the blog page example ● Iterate over posts array on the current $scope ● Toggle comments box ● Enable the user to remove blog posts by clicking on the specific link
  37. Filters ● Provide a way to format the data we display to the user ● Angular gives us several built-in filters as well as an easy way to create our own ● Filters can be used in views, controllers and services
  38. Usage Syntax in HTML templates {{ name | uppercase }} ● Without params {{ 123.4567890 | number: 2 }} {{ user.birthday | date: 'short' }} {{ posts | filter: 'animal' }} ● With params JOHN DOE 123.46 7/23/15 9:59 PM [list filtered by object containing “animal”]
  39. Usage Syntax in controllers/services $filter("uppercase")(name); ● Without params JOHN DOE $filter("number")(123.4567890, 2); $filter("date")(user.birthday, "short"); $filter("filter")(posts, "animal"); ● With params 123.46 7/23/15 9:59 PM [list filtered by object containing “animal”]
  40. < ng-lab="2" /> ● Add date filter to each blog post ● Enable filtering blog posts by freetext
  41. Service, Provider and Factory ● Shared components ● Not related to presentation layer (only access to $rootScope) ● Singleton components ● Lazy-loaded when injected the first time
  42. Service ● Syntax: angular.module("myApp", []) .service("MyService", function MyServiceImpl() { this.hello = function() { return "Hello World"; }; }); ● Result: new MyServiceImpl() is called and returned ● Usage: Can be useful for sharing utility functions to invoke by simply appending () to the injected function reference.
  43. Factory ● Syntax: angular.module("myApp", []) .factory("MyService", function MyServiceImpl() { var privateStuff = ""; return { hello: function() { return "Hello World"; } }; }); ● Result: The value returned by invoking MyServiceImpl() ● Usage: Can be useful for returning a “class” function that can then be new'ed to create instances.
  44. Provider ● Syntax: angular.module("myApp", []) .provider("MyService", function() { var _key; return { setApiKey: function(key) { _key = key; } $get: function() { return { function() { return "Hello World " + _key; } } } }; }); ● Result: The value returned by invoking $get() ● Usage: Can be useful when you need to configure your service before using it (e. g.: setting an ApiKey or a baseUrl)
  45. Config ● Allows to configure Providers before the app is bootstrapped ● Useful for setting API keys, view routes In Java this concept is close to property files
  46. Config angular.module("myApp", []) .config(function(MyServiceProvider) { MyServiceProvider.setApiKey("a73841b619ef5901490dc3f42dce4c5a"); }) .provider("MyService", function() { var _key; return { setApiKey: function(key) { _key = key; } $get: function() { return { function() { return "Hello World " + _key; } } } }; });
  47. So, how to use them? Dependency Injection!
  48. Going back to the controller definition... angular.module("myApp", []) .controller("MainCtrl", function($scope, FooService) { $scope.name = "John Doe"; $scope.countChars = function(text) { return FooService.countStringLength(text); } }); angular.module("myApp", []) .controller("MainCtrl", ["$scope", "FooService", function($scope, FooService) { $scope.name = "John Doe"; $scope.countChars = function(text) { return FooService.countStringLength(text); } }]); That notation is used to keep injection working when compressing the final javascript artifact using, for example, requirejs r.js compiler (with uglify).
  49. Promises ● Problem: ○ callback “pyramid of Doom” step1(function (value1) { step2(value1, function(value2) { step3(value2, function(value3) { step4(value3, function(value4) { // Do something with value4 }); }); }); });
  50. Angular $q service to the rescue! ● Let’s flatten the pyramid: promisedStep1 .then(promisedStep2) .then(promisedStep3) .then(promisedStep4) .then(function (value4) { // Do something with value4 }) .catch(function (error) { // Handle any error from all above steps }) .finally(function (error) { // Handle finally from all above steps });
  51. Parallel requests var firstRequest = $http.get("/first.html"); var secondRequest = $http.get("/second.html"); // Wait for all $q.all([firstRequest, secondRequest]) .then(function(results) { // Do something with both results }) .catch(function (error) { // Handle any error from all above steps }) .finally(function (error) { // Handle finally from all above steps });
  52. Deferred objects var getBlogPosts = function() { var deferred = $q.defer(); $timeout(function() { deferred.resolve([{"title": "a blog post!"}]; }, 5000); return deferred.promise; }; Can resolve or reject their promises ● Usage: Could be useful when you want to convert a callback-paradigm into a Promise philosophy!
  53. Angular $http service // Simple GET request example: $http.get('/someUrl') .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. }); ● Reflects HTTP request methods: ○ GET, POST, PUT, DELETE, HEAD ● Returns a promise with 2 extra methods:
  54. < ng-lab="3" /> ● Remove $scope.posts from MainController ● Implement $http methods in BlogApi service ● Load posts from BlogApi.getPosts(); ● Wire removePost() to BlogApi.removePost();
  55. Multi screen navigation ● Problem: ○ Multi-screen single web application ○ Complex tabs, navigation ○ History management (back button) ○ Query params
  56. UI Router to the rescue! angular.module("myApp", ["ui.router"]) .config(function($stateProvider) { $stateProvider.state({ name: "home", url: "/home", templateUrl: "/partials/home.html", controller: "HomeCtrl", resolve: { BlogPosts: function(BlogApi) { return BlogApi.getPosts(); } } }); });
  57. Injection point <html> <head> <title>The best site ever!</title> </head> <body> <div ui-view></div> </body> </html> HTML side:
  58. How to navigate <a ui-sref="home">Go Home</a> <a ui-sref="viewBlogPost({ id: post.id })">Show More…</a> HTML side: Code side: angular.module("myApp", []) .controller("MainCtrl", function($scope, $state) { $scope.goToBlogPost = function(id) { $state.go("viewBlogPost", { id: id }); }; });
  59. < ng-lab="4" /> ● Move Blog Post list template into a separate HTML fragment ● Create routes for home and addPost ● Add ui-sref to navigation bar for addPost
  60. Forms in Angular ● Controls (input, select, textarea) are ways for a user to enter data ● A Form is a collection of controls for the purpose of grouping related controls together. ● Angular provides validation services, the user can be notified of invalid input before submitting a form.
  61. Directive ng-model ● Binds an input to the current $scope ● Applies validation behaviours ● Registers itself with the parent form ● Works with input, select, textarea <input type="text" ng-model="person.name" /> <input type="email" ng-model="person.email" /> <input type="number" ng-model="person.age" min="1" /> <textarea ng-model="person.bio" ng-required="true"></textarea>
  62. Validation in forms ● ng-model retains an internal state: ○ $touched/$untouched ○ $pristine/$dirty ○ $valid/$invalid ○ $error ● It propagates its state to the parent form: ○ $pristine/$dirty ○ $valid/$invalid ○ $error
  63. Validation in forms <form name="myForm" ng-submit="myForm.$valid && save(person)"> <input type="text" name="name" ng-model="person.name" ng-required="true" /> <div ng-show="myForm.name.$invalid">Name is required!</div> <input type="text" name="surname" ng-model="person.surname" /> <input type="email" name="email" ng-model="person.email" /> <input type="number" name="age" ng-model="person.age" min="1" /> <input type="submit" ng-disabled="myForm.$invalid" value="Save User" /> </form>
  64. < ng-lab="5" /> ● Create form for adding a new blog post ● Ensure validation on all fields ● Call BlogApi.addPost(newPost); ● Redirect to the home page ($state.go());
  65. Custom Directives angular.module("myApp", []) .directive("blogPost", function() { return { restrict: "AEC" templateUrl: "partials/blog-post.html", scope: { showMoreLabel: "@" post: "=blogPost", canRemove: "&" } controller: function($scope, $element) { [...] }, link: function(scope, elem, attrs, ctrls) { [...] } }; });
  66. ● restrict ○ defines when the directive could be injected: Attribute A CSS Class C Element E
  67. ● restrict ○ defines when the directive could be injected: Attribute A <div blog-post="data" button-label="Show more..." can-remove="user.isAdmin()"> </div>
  68. ● restrict ○ defines when the directive could be injected: Element E <blog-post content="data" button-label="Show more..." can-remove="user.isAdmin()"> </blog-post>
  69. ● restrict ○ defines when the directive could be injected: CSS Class C <div class="blog-post" content="data" button-label="Show more..." can-remove="user.isAdmin()"> </div>
  70. ● templateUrl ○ Url of the file containing an HTML fragment representing the directive template ○ It can be a function returning an Url ● template ○ A string containing an HTML fragment representing the directive template ○ It can be a function returning an Url <div> <h1>{{title}}</h1> <p>{{text}}</p> </div> The template must have exactly one root element!
  71. ● scope ○ false (default): it doesn’t create a new scope ○ true: it creates a new child scope ○ {}: it creates a new isolated scope scope: { buttonLabel: "@" content: "=", canRemove: "&" } <blog-post button-label="Show more..." content="post.data" can-remove="user.isAdmin()"> </blog-post> @ = & one-way binding of a string two-way binding between scopes expression evaluation on parent scope
  72. < ng-lab="6" /> ● Refactor the main page wrapping each blog post into a directive ● Use isolate scope to pass data into the new directive
  73. Transclusion Nesting of HTML content (also directives) inside a directive This mechanism allows you to grab the content of the DOM element of your directive and include it anywhere in the directive's template. Tabs Accordion
  74. Transclusion How to start using it the “easy” way 1. Directive definition angular.module("myApp", []) .directive("movieCard", function() { return { transclude: true, scope: { title: "@" }, templateUrl: "partials/movie-card.html", controller: function($scope, $element) {}, link: function(scope, elem, attrs, ctrl) {} }; });
  75. Transclusion How to start using it the “easy” way 2. Directive template (injection point) <div> <h1>{{title}}</h1> <div ng-transclude></div> </div>
  76. Directive-to-Directive communication How to create a DSL in Angular Tabs <tabs> <tab title="Tab 1"> <div><input placeholder="Say something…"/></div> <div><button>Post Comment</button> </tab> [...] </tabs>
  77. Directive-to-Directive communication How to create a DSL in Angular Accordion <accordion> <accordion-section title="Section 1"> <div>Quis nostrud exercitation ullamco…</div> </accordion-section> [...] </accordion>
  78. 1. Child directive definition example angular.module("myApp", []) .directive("tab", function() { return { require: "^tabs", transclude: "true", templateUrl: "partials/tab.html", scope: { title: "@" }, link: function(scope, elem, attrs, tabsCtrl) { tabsCtrl.registerTab(scope); } }; }); Directive-to-Directive communication How to create a DSL in Angular
  79. How to use require in directives Directive-to-Directive communication How to create a DSL in Angular fooBar ?fooBar ^fooBar ?^fooBar Require fooBar on same element and pass it to linking function Pass fooBar controller if available on same element to linking function. If not, pass null. Require fooBar on one of the parent elements and pass it to linking function Pass someDirective controller if available on one of parent elements to linking function. If not, pass null
  80. 2. Child directive template Directive-to-Directive communication How to create a DSL in Angular <div ng-if="active" ng-transclude></div> We want to show that tab only if it’s active. That’s why we are using ng-if
  81. 3. Parent directive definition example angular.module("myApp", []) .directive("tabs", function() { return { transclude: "true" templateUrl: "partials/tabs.html", controller: function() { this.tabs = []; this.registerTab = function(tab) { this.tabs.push(tab); } } }; }); Directive-to-Directive communication How to create a DSL in Angular
  82. 4. Parent directive template Directive-to-Directive communication How to create a DSL in Angular <ul class="tabs"> <li ng-repeat="tab in ctrl.tabs"> <button ng-click="ctrl.selectTab(tab)"> {{tab.title}} </button> </li> </ul> <div ng-transclude></div> http://jsbin.com/vadalo/edit The complete example:
  83. ? || /** **/
  84. Suggested Teaching Materials ng-book The AngularJS bible ● Complete overview of the framework ● Online labs http://egghead.io/ Basic to advanced video trainings ● Life's too short for long boring lectures
  85. ng-thanks Gianluca Cacace gianluca.cacace@gmail.com
Anzeige