AngularJS Elements by Vlad Costel Ungureanu for “Learn Stuff” and “CGM Romania” Dependency Injection
Inversion of Control
$injector is responsible for creating and managing dependencies
Services, factories, filters and animations are created by factory methods: angular.module('myModule', []) .factory('serviceId', ['depService', function(depService) { // ... }]) .directive('directiveName', ['depService', function(depService) { // ... }]) .filter('filterName', ['depService', function(depService) { // ... }]);
2 Dependency Injection
Controllers are created by constructor functions: someModule.controller('MyController', ['$scope', 'dep1', 'dep2', function($scope, dep1, dep2) { // ... $scope.aMethod = function() { // ... } // ... }]);
Dependencies can also be implicit if name of component matches the name of the variable
Dependencies can also be injected in a module by using run and config functions: angular.module('myModule', []) .config(['depProvider', function(depProvider) { // ... }]) .run(['depService', function(depService) { // ... }]);
3 Dependency Injection
Dependency injection using $Inject: var MyController = function($scope, reference) { // ... } MyController.$inject = ['$scope', ‘name_for_the_reference']; someModule.controller('MyController', MyController);
$injector is a service locator (design pattern)
It creates controllers and their dependencies as they are found in the document
4 Name Normalization
The following example illustrates the may ways that name normalizations works in Angular:
Normalisation rules: Strip x- and data- from the front of the element/attributes. Convert the :, -, or _-delimited name to camelCase.
5 Useful directives
ng-app: initialize angular apps ng-init: initalize app data ng-model: binds HTML input to app data ng-show: shows or hides an HTML element ng-disabled: sets disabled attribute on an HTML element ng-class: binds app data to class property of HTML element ng-selected: sets selected attribute if provided expression evaluates to true ng-checked: sets checked attribute if provided expression evaluates to true ng-bind: replace HTML element text with expression value# ng-if: removes or recreates a portion of the DOM tree (works by cloning elements) ng-repeat: clones HTML elements for items in a collection ng-[any_DHTML_event]: specify custom behavior on specified event ng-pattern: adds pattern validation ng-maxlength: adds max length character validation ng-required: adds required valdiation
6 Directives
$compile can match directives based on: element names (E)
7 Directives var myModule = angular.module(...); myModule.directive('directiveName', function factory(injectables) { var directiveDefinitionObject = { priority: 0, template: '
', // or // function(tElement, tAttrs) { ... }, // or templateUrl: 'directive.html', // or // function(tElement, tAttrs) { ... }, transclude: false, restrict: 'A', templateNamespace: 'html', scope: false, controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... }, controllerAs: 'stringIdentifier', bindToController: false,8 Directives require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'], multiElement: false, compile: function compile(tElement, tAttrs, transclude) { return { pre: function preLink(scope, iElement, iAttrs, controller) { ... }, post: function postLink(scope, iElement, iAttrs, controller) { ... } } // or // return function postLink( ... ) { ... } }, // or // link: { // pre: function preLink(scope, iElement, iAttrs, controller) { ... }, // post: function postLink(scope, iElement, iAttrs, controller) { ... } // } }; return directiveDefinitionObject; });
9 Directives
priority: if there are multiple directive defined for the same DOM element, indicates the order template: string or function returning the HTML template of the directive templateURL: the ULR where the HTML template is stored transclude: extract elements where the directive appears, compile them and make them available for the directive restrict: restrict to the type or type the directive tag can be used
scope: false will not create a new scope and the parent scope will be used true will create new child scope for directive in scope we can add variables and their bindings this variables become the directive attributes keep in mind that only one scope can be created for a single DOM element (two directives on the same element will share the scope) {} will create an isolate scope (there can be only one for DOM elements)
10 Directives
controller: $scope - current scope associated with the element $element - current element $attrs - current attributes object for the element $transclude - A transclude linking function pre-bound to the correct transclusion scope: function([scope], cloneLinkingFn, futureParentElement, slotName): scope: (optional) override the scope.
controllerAs: the name of the controller scope to be used in the directive and the directives template
bindToController: if true after controller is instantiated, variables will be bound to the controller scope, which means that there is no need to inject $scope bindTocontroller also allows binding of variables instead of declaring them in scope attribute.
11 Directives
require: require and inject another directive into the current directvie string containing the name of the directive to pass to the linking function an array containing the names of directives to pass to the linking function( controller of directives will be passed) an object whose values are directives to pass to the linking function. The argument passed to the linking function will also be an object with matching keys, whose values will hold the corresponding controllers. Require naming conventions: no prefix - Locate the required controller on the current element. ? - Attempt to locate the required controller ^ - Locate the required controller by searching the element and its parents ^^ - Locate the required controller by searching the element's parents ?^ - Attempt to locate the required controller by searching the element and its parents ?^^ - Attempt to locate the required controller by searching the element's parents
12 Directives
multipleElement: if true, collects DOM nodes, between directive start and end tags, as directive elements compile: compile the DOM of the directive link: link the listeners the DOM elements Link function parameters: scope : The scope of the directive elem : Dom element where the directive is applied attr : Collection of attributes of the Dom Element ctrl : Array of controllers required by the directive
preLink: executed before linking; should not change DOM postLink: executed after linking
13 Directives Lifecycle hooks
$onInit() – after binding bot before pre & post linking functions for the directives on this element and should contain initialisation code
$onChanges(changesObj) - Called whenever one-way (<) or interpolation (@) bindings are updated. ChangesObj contains as keys the name of the bindings and the value as { currentValue, previousValue, isFirstChange() }
$doCheck() - Called on each turn of the digest cycle. Provides an opportunity to detect and act on changes.
$onDestroy() - Called on a controller when its containing scope is destroyed. Use this hook for releasing external resources, watches and event handlers.
$postLink() - Called after this controller's element and its children have been linked. Similar to the post-link function this hook can be used to set up DOM event handlers and do direct DOM manipulation.
14 Directives vs Components
Directive Component a bindings No Yes (binds to controller) bindToController Yes (default: false) No (use bindings instead) compile function Yes No controller Yes Yes (default function() {}) controllerAs Yes (default: false) Yes (default: $ctrl) link functions Yes No multiElement Yes No priority Yes No replace Yes (deprecated) No require Yes Yes restrict Yes No (restricted to elements only) scope Yes (default: false) No (scope is always isolate) template Yes Yes, injectable templateNamespace Yes No templateUrl Yes Yes, injectable terminal Yes No transclude Yes (default: false) Yes (default: false)
15 A few words on transclusion app.directive('myCard', function() { return { scope: { title: '=myTitle', pic: '=myPic' }, templateUrl: 'myCard', transclude: true, link: function(scope, el, attrs, ctrl, transclude) { el.find('.content').append(transclude()); } }; });
16 A few words on transclusion
17 A few words on transclusion
Transclusion helps to introduce html elements in the page without the use of Jquery since there is a directive that indicates where to insert the HTML
If you insert data bindings in a transclude HTML block the binding will inherit the value from out scope of the directive
While having access to the out scope, it’s the reference to the transclusion scope is held by the scope that calls the transclusion
When the parent gets destroyed the transclusion scope will also be destroyed
Element transclusion implies that an element and all his descendants form a transcluded scope
Personal opinion: stay away from it if you don’t need it
18 A few words on transclusion
Rememebr the memory leak from closures: ctrl.expand = function() { $transclude(function(transEl) { content.append(transEl); }); ctrl.expanded = true; };
ctrl.collapse = function() { content.empty(); // content empty – transcluded scope still there ctrl.expanded = false; };
19 A few words on transclusion
There is a fix: var content = $element.find('.content'); var transcludedScope; ctrl.expand = function() { $transclude(function(transEl, transScope) { content.append(transEl); transcludedScope = transScope; }); ctrl.expanded = true; }; ctrl.collapse = function() { transcludedScope.$destroy(); transcludedScope = null; content.empty(); ctrl.expanded = false; };
20 Components
Components are meant to control their own view and data
Components only use an isolate scope and are unable to modify DOM outside their scope
Components are meant to modify only own scoped data, even if they allow for two way bindings bindings: { value: '<', // one way binding, changes in child do not reflect in parent scope comment: '@‘, // value binding onUpdate: ‘&’, // callback onComplete: ‘&’ // callback }
In order to change data it should call the correct event (through a callback function) and the changed data as a parameter
21 Ideally, the whole application should be a tree of components that implement clearly defined inputs and outputs, and minimize two-way data binding. That way, it's easier to predict when data changes and what the state of a component is.
Components can require the controller of other components in order to communicate with it’s parents without using events
22 Services
module.service('MyService', function() { this.method1 = function() { //..method1 logic } this.method2 = function() { //..method2 logic } });
Services are singleton (all elements get a references to the same instance of service)
Only initialized when they are required (Lazy Initialization)
It is created with the key word new
Reference can be used to access data; scope is defined by ‘this’ key word
23 Factories
module.factory('MyFactory', function() { var factory = {}; factory.method1 = function() { //..method1 logic } factory.method2 = function() { //..method2 logic }
return factory; });
It always return a new instance of the object
When injected, it will return a instance that can be used but not shared between angular elements
24 Decorators
.config([ '$provide', function($provide) {
$provide.decorator('myService', [ '$delegate', function myServiceDecorator($delegate) {
function helperFn() { // an additional fn to add to the service }
$delegate.aHelpfulAddition = helperFn; return $delegate; } ]);
25 Decorators
The $delegate is a service you want to decorate
The purpose is to enrich or replace the behavior of the $delegate service
Decorators can also be declare as module decorators, but they are executed after the provider decorators
26 THANK YOU!
Vlad Costel Ungureanu [email protected]
This is a free course from LearnStuff.io – not for commercial use –