Summary of angular problems

Time:2021-8-24

1. Bidirectional data binding

At present, various MVs are popular in the industry*Framework, related frameworks are emerging, and angular is one of them (MVVM). MV*In fact, the core problem of the framework is to separate the view layer from the model, reduce the coupling of code, and achieve the separation of data and performance. MVC, MVP and MVVM have the same goals, and the difference between them is how to associate the model layer with the view.

How the data flows in the model and view layers has become the key to the problem. Angular realizes the two-way binding of data through dirty check. The so-called two-way binding means that the changes of view can be reflected in the model layer, and the changes of model data can be reflected in the view. So how does angular do two-way binding? Why become dirty check? Let’s start with an original question from the front end:

html:<input type=”button” value=”increase 1″ id=”J-increase” />

<span id=”J-count”></span>

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 () {
            //Initialization
            this.appy();
            //
            this.increase();
        }
    };
    bindDate.initialize();
</script>

In the above example, there are two processes:

  • The view layer affects the model layer: clicking button on the page increases the number of data counts by 1

  • The model layer reflects the view layer: after the count changes, it is reflected on the view layer through the apply function

This is the data processing previously implemented using jQuery, YUI and other class libraries. The problems are obvious:

  • A lot of DOM operations are involved

  • Cumbersome process

  • Code coupling is too high to write unit tests. Let’s take a look at how angular processes data:

Step 1. Add a watcher: when the data changes, which objects need to be detected and registered first

//Add watcher
//The source code in angular is simplified  
$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 scope changes, you need to traverse the $$watchers = […] registered for detection

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

In this way, the two-way binding of data is realized. Is the above implementation very similar to the user-defined event? You can see that the observer design pattern or (publisher subscriber) is used.

2. Dependency injection

Students who have used the spring framework know that IOC and AOP are the two most important concepts in spring, and IOC can be injection dependency (DI). Obviously, angular has a very strong back-end color.

Similarly, let’s first look at how to solve object interdependence without Di:

//Traditional object dependency solutions
function Car() {
    ...
}
Car.prototype = {
    run: function () {...}
}
 
function Benz() {
    var cat = new Car();
}
Benz.prototype = {
    ...
}

In the above example, class Benz depends on class car, and this dependency is solved directly through internal new. The disadvantages of this are very obvious. The code coupling becomes higher and is not conducive to maintenance. The back-end framework was aware of this problem for a long time. In the early days, spring registered the dependencies between objects in the XML file. Later, it was more convenient to solve the di problem through anotation. The cos students can see the back-end code.

JS language itself does not have an annotation mechanism. How is angular implemented?

1. Simulation notes

//Annotation simulation
//Annotation simulation
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;

2. Creation of injection object

//Creation of injection objects
function createInjector(modulesToLoad, strictDi) {
    //Creating objects through singleton mode
    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;
}

3. Get the injection object

//Object acquisition, annotation object actual injection
function invoke(fn, self, locals, serviceName) {
    var args = [],
        $inject = annotate(fn, strictDi, serviceName);
 
    for (...) {
        key = $inject[i];
        //Replace with dependent object
        args.push(
          locals && locals.hasOwnProperty(key)
          ? locals[key]
          : getService(key, serviceName)
        );
    }
    if (isArray(fn)) {
        fn = fn[length];
    }      
    return fn.apply(self, args);
}

Here, do you see many ideas of back-end framework design and simulate one without anotation? No wonder PPK says that angular is “a front-end framework by non front ends for non front ends”

3. Controller communication

In actual development, the application system will be very large. An application app cannot have only one controller, so there is a possibility of communication between different controllers. There are two main methods to solve this common problem:

1. Event mechanism:The problem with registering events on $rootscope is that too large events will be registered on $rootscope, which will cause some subsequent problems

//Event resolution controller communication
//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. Through service:Make full use of the di feature of angular and the feature that service is a single instance to serve as a bridge between different controllers

//Service to solve controller communication
//Register service
app.service('Message', function () {
    return {
        count: void(0);
    }
})
 
//Controller1, modify the count value of the service
app.controller('controller1', function ($scope, Message) {
    $scope.count = 1;
    Message.count = $scope.count;
});
 
//Controller2, get the count value of the service
app.controller('controller2', function ($scope, Message) {
    $scope.num = Message.count;
});

4. Features of service

1. Singleton:In angular, only service can perform di. For example, controller and directive do not have these functions. Service literally provides some basic services and is not associated with specific business, while controller and directive are closely associated with specific business, so it is necessary to ensure the uniqueness of service.

2.lazy new:Angular first generates a service provider, but does not immediately generate the corresponding service. Instantiation is only performed when these services are needed.

3. Classification of providers:Provider (), factory, service, value and constant. Provider is the lowest level implementation. Other methods are based on the syntax sugar. It should be noted that these services finally need to add the $GET method, because the specific service is generated by executing the $GET method.

5. Implementation of directive

The compiler of directive includes two stages: compile and link. In short, the compile stage mainly deals with the template dom. At this time, the scope problem is not involved, that is, no data rendering is performed. For example, the ngrepeat instruction modifies the template through compile. After executing compile, the link function will be returned, overwriting the link function defined later; Link is mainly used for data rendering, which is divided into pre link and post link. The parsing order of these two links is opposite. Post link parses the internal first and then the external. In this way, the parsing of directive is safe, because directive can also include directive. At the same time, link is the processing of real DOM, which will involve the performance of DOM operation.

Summary of angular problems

reference resources

https://angularjs.org/

https://github.com/xufei/blog/issues/10

http://teropa.info/blog/2013/11/03/make-your-own-angular-part-1-scopes-and-digest.html

http://www.quirksmode.org/blog/archives/2015/01/the_problem_wit.html

http://www.jvandemo.com/the-nitty-gritty-of-compile-and-link-functions-inside-angularjs-directives/