Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

AngularJS use summary

0.00/5 (No votes)
28 Jan 2016 1  
My understanding about AngularJS

Introduction

Introduce some bacis and important knowledge of AngularJs.

Background

I have used AngularJS for a while, I use it because our customer demanded us to use that technology in our project. From totally blank to have some understanding about AngularJs, I think it's necessary to write something about my understanding to AngularJS. 

1.Two-way data binding

There are many different kinds of MV** frameworks that are very popular, and AngularJS is one of them (MVVM). 

The most important thing for MV** framework is that they can separate View layer and Model layer, decouple your code. MVC, MVP, MVVM all have the same objective and the difference between them is how to associate the model layer with the view layer.

How the data flow between model layer and view layer is the key problem. The two-way data binding is that the change on view can reflect on the model layer and vice versa. So how the AngularJS does the two-way binding and how it becomes dirty-check ? let's start from a front-end question:

html:
<input type="button" value="increase 1" id="J-increase" />
<span id="J-count"></span>

js:
<script>
    var bindDate = {
        count: 1,
        appy: function () {
            document.querySelector('#J-count').innerHTML = this.count;
        },
        increase: function () {
            var _this = this;
            document.querySelector('#J-increase').addEventListener('click', function () {
                _this.count++;
                appy();
            }, true);
        },
        initialize: function () {            
            this.appy();            
            this.increase();
        }
    };
    bindDate.initialize();
</script>

As above example, there are two processes:
a. view layer affects model layer: click button on page would increase the count number.
b. model layer reflects view layer: after the 'count' changed, it would represent on view via 'apply' function.

This is the way when we use jQuery or other libraries, there are three obvious defects:
a. It involves too many DOM operations.
b. The process is complicated.
c. Code coupling is too high, not easy to write unit tests.

Let's see how AngularJS deal with data:

Step 1, add Watcher:  when the data changes definition which object needs to be check, need to register first. 

$watch: function(watchExp, listener, objectEquality) {
    var scope = this,
        array = scope.$$watchers,
        watcher = {
            fn: listener,
            last: initWatchVal,
            get: get,
            exp: watchExp,
            eq: !!objectEquality
        };
    if (!array) {
        array = scope.$$watchers = [];
    }
    array.unshift(watcher);
}

Step 2, dirty-check: when the data under a specified scope changed, need to loop the registered $$watchers = [...]

 $digest: function() {
     while (length--) {
         watch = watchers[length];
         watch.fn(value, lastValue, scope);
     }
 }

this implemented the two-way data binding, you can see that similar to a customize event and that used observer design pattern or publisher-subscriber.

2.Dependency Injection

Let's see if we don't use DI (Dependency Injection) how to solve the interdependence between objects.

function Car() {
    ...
}
Car.prototype = {
    run: function () {...}
}
 
function Benz() {
    var cat = new Car();
}
Benz.prototype = {
    ...
}

As above, class Benz dependents on class Car, it solves the dependence relationship by using an internal New. It's obviously not good to do like that, the code coupling is high and not beneficial for maintenance.
We know javascript language doesn't have annotation mechanism, let's see how AngularJS achieve it.

Step 1, Analog comment

function annotate(fn, strictDi, name) {
    var $inject;
    if (!($inject = fn.$inject)) {
        $inject = [];
        $inject.push(name);
    }else if (isArray(fn)) {
        $inject = fn.slice(0, last);
    }
    return $inject;
}
createInjector.$$annotate = annotate;

Step 2, Create injection object

function createInjector(modulesToLoad, strictDi) {
    var providerCache = {
        $provide: {
            provider: supportObject(provider),
            factory: supportObject(factory),
            service: supportObject(service),
            value: supportObject(value),
            constant: supportObject(constant),
            decorator: decorator
          }
      },
    instanceCache = {},
    instanceInjector = (instanceCache.$injector =
        createInternalInjector(instanceCache, function(serviceName, caller) {
            var provider = providerInjector.get(serviceName + providerSuffix, caller);
            return instanceInjector.invoke(provider.$get, provider, undefined, serviceName);
        }));
    return instanceInjector;
}

Step 3, Get inject object

function invoke(fn, self, locals, serviceName) {
    var args = [],
        $inject = annotate(fn, strictDi, serviceName);
 
    for (...) {
        key = $inject[i];        
        args.push(
          locals && locals.hasOwnProperty(key)
          ? locals[key]
          : getService(key, serviceName)
        );
    }
    if (isArray(fn)) {
        fn = fn[length];
    }      
    return fn.apply(self, args);
}

so if there is no anotation then simulate one, that why PPK said angular is  "a front-end framework by non-front-enders for non-front-enders".

3.Controller communication

In the real development work, the app system will become very big, one app could not have only one controller, how to deal with the communication between different controllers ? There are two main ways:

1.Event mechanism: register event on $rootScope, in this way it will register too many events on the $rootScope and that will cause a lot of problems.

//controller1
app.controller('controller1', function ($rootScope) {
    $rootScope.$on('eventType', function (arg) {
        ......
    })
})
 
// controller2
app.controller('controller2', function ($rootScope) {
    $rootScope.$emit('eventType',arg);
    or
    $rootScope.$broadcast('eventType',arg);
})

2. Use service: make full use of angular characteristics of DI, use angular service is a singleton feature, it will play a role as a bridge between different controller.

// register service
app.service('Message', function () {
    return {
        count: void(0);
    }
})
 
// controller1
app.controller('controller1', function ($scope, Message) {
    $scope.count = 1;
    Message.count = $scope.count;
});
 
// controller2
app.controller('controller2', function ($scope, Message) {
    $scope.num = Message.count;
});

4.Service features

1. Singleton: In AngularJS only service can do dependency injection, something like controller, directive etc don't have this feature, angular service only provide some basic services, it wouldn't associated with specific business, but controller, directive all closely associated with the specific business, so we need to keep the service unique.

2. Lazy new: angular will generate the provider of service first, but it did not generate the corresponding service instantly, it will be instantiated only when we need to use it. 

3. The category of provider: provider(), factory, service, value, constant. Which provider is the underlying implementation, other ways are base on it. Note that all these services all need to add $get method eventually, because all the specific  service is generated by executing the $get method.

5.The implementation of directive

Directive's compiler has two phases: compile, link. In short, the compiler phase mainly handle template DOM, In this phase it does not involve scope problems, i.e. no data rendering. For example, ng-Repeat directive just using compile to modify template, after executed compile it will return link function override the following link functions; in the other hand, the link is mainly responsible for data rendering, it divides into two steps.  The compile order of these two steps is reversed, post-link will compile the internal then external. This is safe for directie compile, because directive can embed directive, in the meanwhile link will handle the real DOM, that will involve DOM operation performance issue.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here