Purpose of the presentation • Learn how AngularJS works by building a simplified version of it • Understand design decisions behind the implementation of AngularJS
AngularJS AngularJS is a JavaScript framework developed by Google. It intends to provide a solid base for the development of CRUD Single-Page Applications (SPA). • two-way data binding • dependency injection • separation of concerns • testability • abstraction
Partials The partials are HTML strings. They may contain AngularJS expressions inside the elements or their attributes. One of the distinctions between AngularJS and the others frameworks is the fact that AngularJS' templates are not in an intermediate format which needs to be turned into HTML.
Controllers The AngularJS controllers are JavaScript functions which help handling the user interactions with the web application (for example mouse events, keyboard events, etc.) by attaching methods to the scope. All required external components, for the controllers are provided through the Dependency Injection mechanism of AngularJS.
Scope In AngularJS the scope is a JavaScript object which is exposed to the partials. The scope could contains different properties - primitives, objects or methods. All methods attached to the scope could be invoked by evaluation of AngularJS expression inside the partials associated with the given scope, or direct call of the method by any component which keeps reference to the scope.
Filters The filters in AngularJS are responsible for encapsulating logic required for formatting data. Usually filters are used inside the partials but they are also accessible in the controllers, directives, services and other filters through Dependency Injection.
Services Every piece of logic which doesn't belong to the components described above should be placed inside a service. Usually services encapsulate the domain specific logic, persistence logic, XHR, WebSockets, etc.
Provider interface • get(name,
locals) - returns given service
• invoke(fn,
locals) - initialize given service
• directive(name,
fn) - registers a directive
• controller(name,
fn) - registers a controller
• service(name,
fn) - registers a service
• annotate(fn) - returns an array of the dependencies of given service
DOMCompiler Responsibilities: • Compiles the DOM • Traverse the whole DOM tree • Finds registered directives • Invokes the logic associated with them • Manages the current scope
DOMCompiler interface • bootstrap() - bootstraps the app
• callDirectives(el,
scope) - invokes directive associated to given element
• compile(el,
scope) - compiles the subtree with root given element
Scope • Watches expressions • Evaluates all watched expressions on each $digest loop • Invokes the associated logic on change of the expression result, compared to the previous call of $digest
Scope interface • $watch(exp,
fn) - associates given function to an expression
• $eval(exp) - evaluates given expression
• $new() - creates new scope, child of the current
• $destroy() - destroys given scope
• $digest() - performs dirty checking
Traverse the DOM tree Two basic algorithms: • Breath-First Search (BFS) • Finds the shortest path between nods • Depth-First Search (DFS) • Finds any path
We can represent graphs by… • A matrix A, where A[i][j] will be equals to 1 if and only if there is a path between i and j, otherwise it’ll be equals to 0.
//... callDirectives: function (el, $scope) { var isCreated = false; [].map.call(el.attributes || [], function (attr) { var directive = Provider.get(attr.name + Provider.DIRECTIVES_SUFFIX); return directive && { expr: attr.value, provision: directive }; }) // takes all expressions with value evaluated to true // i.e. if such directive exists the literal will // be returned .filter(Boolean) .forEach(function (d) { if (d.provision.scope && !isCreated) { isCreated = true; $scope = $scope.$new(); } d.provision.link(el, $scope, d.expr); }); return $scope; } //...
Scope.prototype.$digest = function () { 'use strict'; var dirty, watcher, current, i; do { dirty = false; for (i = 0; i < this.$$watchers.length; i += 1) { watcher = this.$$watchers[i]; current = this.$eval(watcher.exp); if (!Utils.equals(watcher.last, current)) { watcher.last = Utils.clone(current); dirty = true; watcher.fn(current); } } } while (dirty); for (i = 0; i < this.$$children.length; i += 1) { this.$$children[i].$digest(); // Recursive call } };
setTimeout & setInterval <br/>function Ctrl($scope) {<br/>$scope.counter = 0;<br/>setTimeout(function () {<br/>$scope.counter += 1;<br/>$scope.$digest(); //Note that in AngularJS<br/>you should use $apply<br/>}, 1000);<br/>}<br/>