Angular overview

Why MVC

Unobtrusive JavaScript

$('.main').text('Main paragraph');
$('.main').on('click', function() {console.log('I am clicked')});

HTML

JavaScript

<p class="main"></p>

HTML

<p class="main">Main paragraph</p>

Problem

JS and HTML are not really separated:

you can’t change a line of markup without checking every single line of JavaScript to assure you’re not breaking a selector.

Two-way data binding

$scope.content = "Main paragraph";
$scope.processClick = function() {console.log('I am clicked')};

HTML

JavaScript

<p class="main" ng-bind="content" ng-click="processClick()"></p>

When run, HTML becomes like this

<p class="main" ng-bind="content" ng-click="processClick()">Main paragraph</p>

Main user visible components

Main implementation components

  • $injector (dependency injection)
  • $scope (dirty checking)
  • $compile (data binding)

Module

Why we need modules?

  • is a container for directives, controllers, and factories/services and also for the config and run blocks
  • defines the loading order of the above mentioned components

How we define modules?

// define new dependent module `designer`

angular.module('designer', []);

// define new module `main` and its dependency

angular.module('main', ['designer']);

Factories/Services

Creating a service with module

angular.module('main', []).service('post', function($http) {
    var root = 'http://jsonplaceholder.typicode.com';
	
    this.getPosts = function() {
    	return $http.get(root + '/posts/1');
    };
});


angular.module('main', []).factory('post', function($http) {
    var root = 'http://jsonplaceholder.typicode.com';

    return {
	getPosts : function() {
            return $http.get(root + '/posts/1');
	}
    }
});

Defining service as a `service`

Defining service as a `factory`

$injector service

angular.module('main', []).service('post', function($injector) {
    function Post($http) {
    	var root = 'http://jsonplaceholder.typicode.com';
    	
    	this.getPosts = function() {
            return $http.get(root + '/posts/1');
    	};
    }
	
    return $injector.instantiate(Post);
})

Defining service as a `service`

Defining service as a `factory`

angular.module('main', []).service('post', function($injector) {
    function Post($http) {
    	var root = 'http://jsonplaceholder.typicode.com';
		
    	return {
            getPosts: function() {
		return $http.get(root + '/posts/1');
            }
	}
    }
	
    return $injector.invoke(Post);
})

Scope

Dirty checking

var scope = {
    itemsCount: 5,
    showInvalid: true
}
var watchers = [];

function addWatcher(property, object, callback) {
    var previous;
    watchers.push(function watcher() {
        if (object[property] !== previous) {
            callback();
            previous = object[property];
        }
    });
}

function digest() {
    console.log('calling digest');
    watchers.forEach(function(watcher) {
        watcher();
    });
}

var scope = {
    itemsCount: 5,
    showInvalid: true
}

addWatcher('itemsCount', scope, function() {console.log('itemsCount changed')});

digest(); // calling digest, itemsCount changed, showInvalid changed
digest(); // calling digest

scope.itemsCount = 10;
digest(); // calling digest, itemsCount changed

Dirty checking (native API)

angular.module('main', []).service('scopes', function($rootScope) {
    var scope = $rootScope.$new();
    
    setTimeout(function() {
        scope.$watch('itemsCount', function() {
            console.log('itemsCount changed');
        });
    
        scope.$digest(); // itemsCount changed
        scope.$digest(); //
    
        scope.itemsCount = 10;
        scope.$digest(); // itemsCount changed
    });
});

Scope hierarchy

angular.module('main', []).service('scopes', function($rootScope) {
    var scope = $rootScope.$new();
    Object.getPrototypeOf(scope) === $rootScope; // true
    scope.$parent === $rootScope; // true
});

Directives

Creating directives

angular.module('main').directive('showPostTitle', function(post) {
    return {
        compile: function($element) {
            var span = '<span>I am child span</span>';
            $element.append(span);
					
            return function($scope, $element) {
                post.getPosts().then(function(data) {
                    $element.find('span').text(data.data.title);
                });
            }
        }
    }
})
<p class="main" show-post-title></p>

Initial HTML

<p show-post-title=""><span>sunt aut facere</span></p>

Compiled and linked HTML

$compile service

angular.module('main').directive('addClickableChild', function($compile) {
    return function($scope, $element) {
    	var scope = $scope.$new();
	scope.content = 'I am span content';
	scope.processClick = function() {console.log('I am clicked')};
	
	var span = '<span class="clickable-child" ng-click="processClick()" ng-bind="content"></span>';
	var linked = $compile(span)(scope);
		
	$element.append(linked);
    }
})
<p add-clickable-child></p>

Initial HTML

<p add-clickable-child="">
    <span class="clickable-child ng-binding ng-scope" 
          ng-click="processClick()" 
          ng-bind="content">
          I am span content
    </span>
</p>

Compiled and linked HTML

Controller

Data preparation

angular.module('main').directive('showPostTitle', function() {
    return {
        controller: function($scope, post) {
            post.getPosts().then(function(data) {
                $scope.posts = data.data.title;
            });
        },
        compile: function($element) {
            var span = '<span>I am child span</span>';
            $element.append(span);
					
            return function($scope, $element) {
                $scope.$watch('posts', function(posts) {
                    if (posts) {
                        $element.find('span').text(posts); 
                    }
                });
            }
        }
    }
})

Angular overview

By maximk

Angular overview

  • 593