- A
view in AngularJS is the HTML after it has been compiled by AngularJS - A
controller is a Javascript object that contains methods and properties. - Controllers should be lightweight and specific to the view they control.
- The glue between the view and controller is
scope - The controller is responsible for exposing methods and properties to the view by
attaching them to scope - a
digest cycle in AngularJS is what keeps the view and controllersynchronized - Digest Cycle
- How does AngularJS know when something has changed and it's time to update?
--> Dirty Checking
- How does AngularJS know when something has changed and it's time to update?
controller-as syntax- Example of NOT using
controller-as syntax:<div ng-app="app" ng-controller="SiteController"> <!-- we are pretty sure we are getting the title from the SiteController --> <h1>{{title}}</h1> <div ng-controller="PersonController"> <!-- is this the title from SiteController or PersonController? is there even a title in both? --> <h2>{{title}}</h2> <div ng-repeat="person in PersonCtrl.people"> <span>{{person.name}}</span> </div> </div> </div>
- Example of using
controller-as syntax:// site.controller.js: (function() { 'use strict'; angular.module('app').controller('SiteController', SiteController); // no need to inject $scope because we don't need it SiteController.$inject = []; function SiteController() { // assigning this to a loacal variable makes it easier to // declare properties and methods in the controller var vm = this; // declare our title on this vm.title = "Welcome!"; } })(); // other.controller.js: (function() { 'use strict'; angular.module('app').controller('OtherController', OtherController); function OtherController() { var vm = this; vm.title = "Other title!"; } })();
<div ng-app="app" ng-controller="SiteController as SiteCtrl"> <!-- we can be sure we are getting the title from the SiteController --> <h1>{{SiteCtrl.title}}</h1> <div ng-controller="OtherController as OtherCtrl"> <!-- we can be sure we are getting the title from the OtherCtrl --> <h2>{{OtherCtrl.title}}</h2> </div> </div>
- Example of NOT using
- ng-repeat
- Simple ng-repeat:
<ul class="list" ng-repeat="status in storyboard.statuses"> <h3 class="status">{{status.name}}</h3> </ul>
- Adding Filters
- ng-repeat with filters:
<li class="story" ng-repeat="story in storyboard.stories | filter: {status:status.name}"></li>
- ng-repeat with filters:
- Simple ng-repeat:
- ng-model
- ng-model is a
directive in Angular.JS that represents models and its primary purpose is tobind the "view" to the "model". - The ng-model directive will ensure that the data in the "view" and that of your "model" are kept
in sync the whole time. - Can also provide a
validation behavior - How to use:
<div ng-app="DemoApp" ng-controller="DemoCtrl"> <form> Name : <input type="text" ng-model="pname"><br> </form> </div> <script> var app = angular.module('DemoApp',[]); app.controller('DemoCtrl', function($scope){ $scope.pname="Guru99"; }); </script>
- ng-model is a
- ng-options
- The ng-options directive fills a
<select> element with<options> . <div ng-app="myApp" ng-controller="myCtrl"> <select ng-model="selectedName" ng-options="item for item in names"></select> </div> <script> var app = angular.module('myApp', []); app.controller('myCtrl', function($scope) { $scope.names = ["Emil", "Tobias", "Linus"]; }); </script>
- The ng-options directive fills a
- a service is a function, or object, that is available for, and limited to, your AngularJS application.
- A service is typically a
class with a narrow, well-defined purpose . It should do somethingspecific and do it well. - Services provide a means for keeping data around for the lifetime of an application while they also can be used across different controllers in a consistent manner.
- Different types of services:
- module.value: Good for storing simple values that may need to change during runtime.
- take two parameters: the name of the service and the value that is to be returned when the value service is instantiated.
- Example:
myModule.value('STORY_TYPES', [ {name: 'Feature'}, {name: 'Enhancement'}, {name: 'Bug'}, {name: 'Spike'} ]);
- module.constant: Good for storing simple values that will never need to change.
- can't be modified during runtime
- Example of a remote url (encapsulate back-end choice into a single place and pass it around as needed):
angular.module('Angello.Common') .constant('CURRENT_BACKEND', 'firebase');
- module.service: Creates a service using a constructor method. This is good for developers who prefer OOP-style programming and like using the this keyword.
- When you define a service using module.service, the instance is returned via a
constructor function. - It's instantiated with the
new keyword. - After instantiated add pass service into controller, properties with
this will be available on the controller through the service. app.controller('myServiceCtrl', function ($scope, myService) { $scope.artist = myService.getArtist(); }); app.service('myService', function () { var _artist = ''; this.getArtist = function () { return _artist; } });
- When you define a service using module.service, the instance is returned via a
- module.factory: Creates a service using a constructor function. Developers using the Revealing Module pattern will be at home with this style.
- Factory creates an object, add properties to it, then return that same object.
- When you pass this service into your controller, those properties on the object will now be available in that controller through your factory.
app.controller('myFactoryCtrl', function ($scope, myFactory) { $scope.artist = myFactory.getArtist() }); app.factory('myFactory', function () { var _artist = ''; var service = {} service.getArtist = function () { return _artist } return service; });
- module.provider: Providesthemostcontroloverdefiningyourservicebutismorecomplexandverbose. Good for modifying behavior of a service while the application is being configured.
- Providers are the only service you can pass into your
.config() function . - Use a provider when you want to provide module-wide configuration for your service object before making it available.
app.controller('myProviderCtrl', function ($scope, myProvider) { $scope.artist = myProvider.getArtist(); $scope.data.thingFromConfig = myProvider.thingOnConfig; }); app.provider('myProvider', function () { this._artist = ''; this.thingFromConfig = ''; //Only the properties on the object returned from $get are available in the controller. this.$get = function () { var that = this; return { getArtist: function () { return that._artist; }, thingonConfig: that.thingFromConfig } } }); app.config(function (myProviderProvider) { myProviderProvider.thingFromConfig = 'This was set in config()'; });
- Providers are the only service you can pass into your
- module.value: Good for storing simple values that may need to change during runtime.
- Dirty Checking: When Angular thinks that the value could change, it will call
$digest() on the value to check whether the value is “dirty.” Hence, when the Angular runtime is running, it will look for potential changes on the value. - Using ng-model
- <input ng-model="person.name" type="text" placeholder="Your name"> <h1>Hello {{ person.name }}</h1>
- directives are markers on a DOM element (such as an attribute, element name, comment or CSS class) that tell AngularJS's HTML compiler ($compile) to attach a specified behavior to that DOM element (e.g. via event listeners), or even to transform the DOM element and its children.
- Directives generally have three parts:
- link function
- controller function
- directive definition object
- Four types
- As elements:
<date-picker></date-picker>
- As attributes:
<input type="text" date-picker/>
- As classes:
<input type="text" class="date-picker"/>
- As comments:
<!--directive:date-picker-->
- As elements:
- Simple directive
-
angular.module('myApp', []).directive('helloWorld', function() { return { restrict: 'AEC', replace: true, template: '<h3>Hello, World!</h3>' }; });
- How to invoke:
- Use in HTML (
Need to use hello-world in HTML to reference helloWorld )<body ng-app="myApp"> <hello-world></hello-world> </body>
- Use as attribute ( if you want to use
hello-world to invoke your directive in your templates, you need to name ithelloWorld when you define it in JavaScript.)<body ng-app="myApp"> <div hello-world></div> </body>
- Use in HTML (
- Configuration Options
restrict : This provides a way to specify how a directive should be used in HTMLA : AttributeE : ElementC : ClassM : Comment
-
- Link Functions
- Inside the link function you can set models on the scope object, watch models for changes, and add event listeners to DOM elements.
- Example of
link function <body ng-app="myApp"> <div ng-controller="MainController"> <input type="text" ng-model="message" placeholder="Enter message" /> <hello-world></hello-world> </div> </body> <script> angular.module('myApp', []); angular.module('myApp') controller---> .controller('MainController', function($scope) { $scope.message = 'I love AngularJS'; }) directive---> .directive('helloWorld', function() { return { restrict: 'AEC', replace: true, template: '<p ng-click="clearMessage()">Hello, World!{{message}} </p>', link function---> link: function(scope, elem, attrs) { scope.$watch('message', function(value) { console.log('Message Changed!'); }); scope.clearMessage = function() { scope.message = ''; } elem.bind('mouseover', function() { elem.css('cursor', 'pointer'); }); } } }); </script>
link function takes three parameters:scope : The scope object for the directive. By default this is same as theparent's scope. For instance, if you have ng-controller in the markup and our directive is used inside it, then the directive will use the parent controller's scope.elem : This is the element on which our directive is applied.attrs : This is a map containing the attributes and their values that are applied to the directive in HTML.
- Changing a directive's scope
- By default, a directive gets its
parent's scope. - Three ways to define a
scope of a directivescope: false : Angulardefault . The directive's scope is exactly the one of its parent scope (parentScope).scope: true : Angular creates a scope for this directive. The scope inherits from parentScope.scope: {...} : isolated scope.
- If we want to make some properties private, we can have two options:
child scope andisolated scope . - The scope can be configured with the scope property of the
directive definition object . - Example: Give the directive a
new child scope thatinherits from the parent scope .angular.module('myApp').directive('helloWorld', function() { return { scope: true, // use a child scope that inherits from parent restrict: 'AE', replace: true, template: '<h3>Hello, World!</h3>' }; });
- Example: This directive uses a
new isolated scope thatdoesn't inherit from its parent.
Isolated scopes are good when we want to createangular.module('myApp').directive('helloWorld', function() { return { scope: {}, // use a new isolated scope restrict: 'AE', replace: true, template: '<h3>Hello, World!</h3>' }; });
reusable components . By isolating the scope we guarantee that the directive isself-contained and can be easily plugged into an HTML app. This protects theparent scope from getting polluted , as it'snot accessible inside the directive .
- By default, a directive gets its
- Binding Between Parent Scope and Isolated Scope Models
- The topic about using symbols
@, = and & are talking about scenarios usingisolatedScope . - Using @ For One-Way Binding: @ simply
passes the property from parentScope to isolatedScope .-
<body ng-app="myApp"> <div ng-controller="MainController"> <input type="text" ng-model="message" placeholder="Enter message" /> <!-- if message changed at here, message-attr also changes --> <hello-world message-attr="{{message}}"></hello-world> <!-- if message-attr changed, message above does not change due to one-way binding --> </div> </body> <script> angular.module('myApp', []); angular.module('myApp') .controller('MainController', function($scope) { $scope.message = 'I love AngularJS'; }) .directive('helloWorld', function() { return { restrict: 'AEC', scope: { message:'@messageAttr' // isolated scope property: message, should bind to messageAttr }, replace: true, template: '<p ng-click="clearMessage()">Hello, World!{{message}} </p>', link: function(scope, elem, attrs) { scope.$watch('message', function(value) { console.log('Message Changed!'); }); scope.clearMessage = function() { scope.message = ''; // this scope is isolated scope, which will empty message-attr } elem.bind('mouseover', function() { elem.css('cursor', 'pointer'); }); } } }); </script>
- We call this
one-way binding because with this technique you can only passstrings to the attribute (using expressions, {{}} or just static values). When theparent scope property changes , yourisolated scope model also changes . However, thereverse is not true ! You can't change theparent scope model by manipulating theisolated scope model.
-
- Using = For Two-Way Binding: means any modification in
childScope will also update the value inparentScope , and vice versa. (for both primitives and objects) - Useful resources about binding
- The topic about using symbols
- Transclusion
- Use
transclude when you want to preserve the contents of an element when you're using a directive. - the following code will finally
replace "some content here" with "the template":
Final result:<!-- HTML --> <div foo> some content here </div> <!-- JavaScript --> .directive("foo", function() { return { template: "<div>the template</div>" }; })
<div foo> the template </div>
- Using
transclude to preserve the original markup
Final result:<!-- HTML --> <div foo> some content here </div> <!-- JavaScript --> .directive("foo", function() { return { transclude: true, template: "<div>the template</div><div ng-transclude></div>" }; })
<div foo> the template some content here </div>
ng-transclude is used to define where external content will be placed in a directive’s template.- Useful resources about
transclude :
- Use
- module is basically a
container that groups together different components of your application under a common name.(reuseable) - To create a module:
angular.module()
function
The first argument to the function is themodule name . The second argument is an array that specifies theadditional modules upon which this module depends. - Example:
angular.module('firstModule',[]); //defines a module with no dependencies angular.module('firstModule',['moduleA','moduleB']); //defines a module with 2 dependencies
- think of
angular.module() as a global API forcreating or retrieving modules and registering components. - Example of attaching
controller anddirective var firstModule=angular.module('firstModule',[]); //define a module firstModule.controller('FirstController',function($scope){ // register a controller //your rocking controller }); firstModule.directive('FirstDirective',function(){ // register a directive return { }; }); // Having one parameter in angular.module is used for retrieving a module. see following angular.module('firstModule',[]); //specify dependency list here angular.module('firstModule').controller('FirstController' ➥,function($scope){ //just use it //your rocking controller }); angular.module('firstModule').directive('FirstDirective',function(){ return { }; }); // or even simpler angular.module('firstModule',[]) .controller('FirstController' ➥,function($scope){ //your rocking controller }) .directive('FirstDirective',function(){ //your directive return { }; });
- Module best practice
- modularization by
layers - app.js
- controllers.js
- directives.js
- filters.js
- services.js
- modularization by
features - According to different features
- modularization by
- Module Configuration
angular.module('myApp', []) .config(function($provide) { });
- Angular executes blocks of configuration during the provider registration and configuration phases in the bootstrapping of the module. This phase is the only part of the Angular flow that may be modified before the app starts up.
- Run Blocks
- run blocks are executed after the injector is created and are the first methods that are executed in any Angular app.
- The task of a controller is to augment the
scope by attachingmodels and functions to it that are subsequently accessed in theview - Example:
The first parameter is controller's name. The second is the constructor function that'll be called when encounter ng-controller.angular.module('myApp',[]).controller('GreetingController', function($scope){ $scope.now=new Date(); //set the model 'now' on scope $scope.greeting='Hello'; //set the name model on scope });
- Controller is dependent on
scope . - When AngularJS finds ng-controller='GreetingController'in HTML, it creates a
new scope for your controller and passes that as an argument to the constructor function while instantiating the controller. This is calledDependency Injection - Controller should not do:
- No DOM manipulation. This should be done in directives.
- Don't format model values in controllers. Filters exist for this purpose.
- Don't write repeatable code in controllers. Rather encapsulate them in services.
- a
scope object is useful for holdingmodel values - Every AngularJS application has at least one scope called
$rootScope . This is created as a result of attaching theng-app directive to any HTML element - when you attach
ng-controller to any element, it creates anew child scope , which prototypallyinherits from the $rootScope . - Example(includes $watch):
<!DOCTYPE html> <html ng-app="myApp"> <head> <meta charset="utf-8" /> <title ng-bind="title"></title> <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0/angular.min.js"></script> </head> <body ng-controller="SiteController"> <span>{{publisher}} excels in {{type}} books</span> <div ng-controller="BookController"> <h3>Some of the popular books from {{publisher}}</h3> <ul> <li ng-repeat="book in books"> {{book}} <a href="#" ng-click="addToWishList(book)">Add to Wish List</a> </li> </ul> </div> </body> <script> angular.module('myApp', ['myApp.controllers']); angular.module('myApp').run(function($rootScope){ $rootScope.title = 'Famous Books'; $rootScope.name = "Root Scope"; }); angular.module('myApp.controllers',[]).controller('SiteController', function($scope){ $scope.publisher = 'SitePoint'; $scope.type = "Web Development"; $scope.name = "Scope for SiteController"; }); angular.module('myApp.controllers').controller('BookController', function($scope){ $scope.books = ['Jump Start HTML5','Jump Start CSS','Jump Start Responsive Web Design']; $scope.name = "Scope for BookController"; $scope.addToWishList = function(book){ $scope.wishListCount++; }; $scope.wishListCount = 2; $scope.$watch('wishListCount',function(newValue,oldValue){ if(newValue == 4){ alert('Great! You have 2 items in your wish list. Time to buy what you love. '); } }); }); </script> </html>
- Digest Cycle
- This cycle can be considered as a
loop , during which AngularJS checks if there are any changes to all the variables watched by all the$scopes . So if you have$scope.myVar defined in your controller and this variable was marked forbeing watched , then you are implicitly telling AngularJS to monitor the changes on myVar in each iteration of the loop. - Is everything attached to $scope being watched?
- No. Because of performance issues.
- There are two ways of declaring a $scope variable as being watched.
- Case 1: By using it in your template via the expression
<span>{{myVar}}</span> - Case 2: By adding it manually via the
$watch service
- Case 1: By using it in your template via the expression
Case 1 : suppose we write:<input type="text" ng-model="name"/>
- What happen behind the scene:
- The directive
ng-model registers akeydown listener with the input field. text changes --(fire)--> keydown event --(call)--> corresponding listener - Inside the
keydown listener the directive assigns the new value of input(in this case, name) field to the scope model specified in ng-model. This code is wrapped inside$apply() call. - After $apply() ends the
digest cycle starts in which the watchers are called. If you have an expression{{name}} in the view it has already set up awatcher on scope model name. So, in digest cycle this watcher gets called and the listener function executes with thenewValue andoldValue as arguments.
- The directive
- This cycle can be considered as a
- $watch
$watch() is used to register a watcher.- Basic function structure:
function(watchExp, listener, objectEquality, prettyPrintExpression)
watchExp : The expression being watched. It can be a function or a string, it is evaluated at every digest cycle.listener : A callback, fired when the watch is first set, and then each time that during the digest cycle that a change for watchExp‘s value is detected.- objectEquality: this is true the watcher will perform a deep comparison.
- Useful resources about
$watch
- $watchCollection
- $apply
- The $scope.$apply() function is used to execute some code, and then call $scope.$digest() after that, so all watches are checked and the corresponding watch listener functions are called. The $apply() function is useful when integrating AngularJS with other code.
$apply lets you start the digestion cycle explicitly.- Calling
$apply() willautomatically trigger a$digest on$rootScope which subsequently visitsall the child scopes calling the watchers.
- $digest
- This function iterates through
all watches and checks if any of the watched variables havechanged . If a watched variable has changed, a correspondinglistener function is called. The listener function does whatever work it needs to do, for instance changing an HTML text to reflect the new value of the watched variable. Thus, the $digest() function is what triggers the data binding to update.
- This function iterates through
- AngularJS supports two types of event
propagation :Emitting the eventupwards in the scope hierarchy.Broadcasting the eventdownwards in the scope hierarchy.
- $emit()
$scope.$emit(name,args)
- name: name of the event that is being emitted.
- args: arguments needed to pass to event listeners. Typically you pass the data which should be
shared with the listeners here.
- $broadcast()
$scope.$broadcast(name,args)
- Same as the &emit(), except the event propagates downwards in the scope hierarchy to all the child scopes.
- $on()
- It listen on events of a given type. It can catch the event dispatched by $broadcast and $emit.