Brief analysis of life cycle and delay processing in AngularJS

  • 2020-06-15 07:49:43
  • OfStack

Here, we discuss a few more commonly used advanced inversion of control containers (Inversion of Control containers) : lazy loading (ES5en-ES6en), lifecycle management (lifetime management), and lazy creation/handling (deferred creation/resolution).

Lazy loading (Lazy-Loading)

Lazy loading is when you instantiate an object when you need it. Many dependency injection systems create components at the beginning of 1 as their dependencies. But sometimes you don't want to instantiate these components until you use them in your application. A good example of this in Angular is when you set a behavior at configuration time that references a component that has not yet been created.

Suppose you want to block the system's built-in $log service, so you save it in $rootScope. I don't recommend this, but it's a simple and effective example. To intercept, you use $provide in the configuration and then call the modifier. If you want to refer to $rootScope directly, you will get an exception because of the circular reference. The solution was to lazily load $rootScope via $injector.


The following code will only load $rootScope the first time it is used.



$provide.decorator(, [, ,
   ($delegate, $injector) {
     log = $delegate.log.bind($delegate);
    $delegate.log = (msg) {
       rs = $injector.get();
       (rs.logs === undefined) {
        rs.logs = [];
      }
      rs.logs.push(msg);
      log(msg);
    };
     $delegate;
}]);

Subsequent calls will receive a sample singleton of $rootScope. Here's an example you can use. I think I've heard a (incorrect) statement before (Angular only supports singletons)... Of course not. The method in $injector is for you to manage the lifecycle of your component.

Lifecycle management

The lifecycle is concerned with how you manage instances of components. By default, when you inject an Angular dependency, dependency injection will help you create a copy of it and reuse it in your application. For the most part, that's exactly what we expect. In some cases, multiple instances of the same 1 component are required. Assume the following counting service:



Counter($log) {
  $log.log();
}
 
angular.extend(Counter.prototype, {
  count: 0,
  increment: () {
    .count += 1;
     .count;
  }
});
 
Counter.$inject = [];
 
app.service(, Counter);

Your application is tracking different counters. When you inject the service, you always get one counter. Is that the limit for Angular?

Of course not. Repeat once. With the $injector service you can create a new copy at any time. The following code USES two separate counters:


app.run([, , ,
   (rs, c, i) {
    rs.count = c.count;
    rs.update = c.increment;
    rs.update2 = () {
       c = i.instantiate(Counter);
      rs.count2 = c.count;
      rs.update2 = () {
        c.increment();
        rs.count2 = c.count;
      };
    };
  }]);

As you can see, counters are tracked by individual instances. Here is an example that can be used. If you need to generate new instances frequently, you can sign up for the service like this:


app.factory(, [,
   (i) {
     {
      getCounter: () {
         i.instantiate(Counter);
      }
    };
  }]);

Generating 1 required instance is that simple, and you can replace $injector with your factory component:


app.run([, ,
   (rs, cf) {
     c1 = cf.getCounter(),
      c2 = cf.getCounter();
    rs.count = c1.count;
    rs.update = c1.increment;
    rs.count2 = c2.count;
    rs.update2 = () {
      rs.count2 = c2.increment();
    };
  }]);

You can see the full version of the available examples. As you can see, it is entirely possible to manage the life cycle of your components with Angular's built-in dependency injection. What about deferred processing (deferred resolution)? For example, some components you need to introduce after Angular has been configured and need to wrap them with their dependencies.

Delayed processing (Deferred Resolution)

We have described one way in Angular to delay processing dependencies. When you want to wrap something, you can call instantiate of the $injector service, which can then resolve dependencies by parameter sniffing, which looks like $inject's static property 1, or by checking the array you pass it. In other words, the following is perfectly valid:


$injector.instantiate(['dependency', Constructor]);

You can also call methods with decorative arrays. Suppose you have a method that relies on the $log service, which you can invoke at runtime by deferring processing, as follows:



 myFunc = [, ($log) {
  $log.log();
}];
$injector.invoke(myFunc);

You can look at this working example (open your console and see what happens when you press the button).

conclusion

To sum up, THE dependency injection of Angular provides many advanced features that you will want and often use in the line of business applications. The convenience of factories, services, and providers often leads Angular developers to believe that there is only one option available. The magic is the $injector service, which you can use to generate the required singleton, create new components, or dynamically reference dependent methods.

Finally, note that the injection in your client code is available even outside of Angular. Let's look at an example of invoking the $log service via injection outside of Angular. Click here. Why pass 'ng' into the array of methods? It is the core module of Angular and is added implicitly when you wrap your module, but you must add explicitly if your instructions are to generate their own injection instances.


Related articles: