Here, we will discuss some commonly used advanced Inversion of Control containers: lazy-loading, lifetime management, and deferred creation/resolution.
Lazy-Loading
The so-called lazy loading is to instantiate the object when you need to use it. Many dependency injection systems create components from the beginning as their dependable projects. But sometimes, you don't want to instantiate these components until you use them in your application. A good example in Angular is when you configure a behavior that references some components that have not been created yet.
Suppose you want to intercept the $log service built into the system, so you have it in $rootScope. Of course I don't recommend this, but it's easier and more effective to give examples. In order to intercept, you use $provide when configuring and then call the modification method. If you want to directly reference $rootScope at this time, you will get an exception due to the circular reference. And the solution is to delay loading $rootScope via $injector.
The following code will only load $rootScope when it is first used.
$(, [, , ($delegate, $injector) { log = $($delegate); $ = (msg) { rs = $(); ( === undefined) { = []; } (msg); log(msg); }; $delegate; }]);
The subsequent calls will get the same singleton $rootScope. There is one hereAvailable Examples. I seem to have heard a (incorrect) statement before (Angular only supports singletons) ... Of course not true. The method in $injector is used to manage the life cycle of your component for you.
Lifecycle Management
The life cycle involves 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. This is indeed what we expect in most cases. In some cases, multiple instances of the same component are required. Assume the following counting service:
Counter($log) { $(); } (, { count: 0, increment: () { .count += 1; .count; } }); Counter.$inject = []; (, Counter);
Your application wants to track different counters. And after you inject the service, you will always get the same counter. Is this Angular limitation?
Of course not. Repeat once, through the $injector service you can create a new copy at any time. The following code uses two independent counters:
([, , , (rs, c, i) { = ; = ; rs.update2 = () { c = (Counter); rs.count2 = ; rs.update2 = () { (); rs.count2 = ; }; }; }]);
You can see that the counters are tracked by independent instances, hereAvailable Examples. If you need to generate new instances frequently, you can register the service like this:
(, [, (i) { { getCounter: () { (Counter); } }; }]);
It is so simple to generate an instance you need, and you can use your factory components instead of $injector:
([, , (rs, cf) { c1 = (), c2 = (); = ; = ; rs.count2 = ; rs.update2 = () { rs.count2 = (); }; }]);
You can check out thisAvailable examples for the full version. As you can see, it is entirely possible to manage the life cycle of your component with Angular's built-in dependency injection. So what about deferred resolution - for example, some components you need to introduce after Angular is configured and need to be wrapped with their dependencies.
Deferred Resolution
We have already introduced one way to delay processing of dependencies in Angular. When you want to wrap something, you can call instantiate of the $injector service, and then it can resolve the dependency by sniffing the parameter, which looks like it is with the static property of $inject, or it can also be done by checking the array you pass it to it. In other words, the following is a completely effective way to write:
$(['dependency', Constructor]);
You can also call methods with decorative arrays. Suppose you have a method that depends on the $log service, which you can call via delayed processing at runtime, like this:
myFunc = [, ($log) { $(); }]; $(myFunc);
You can take a look at this available example (open your console and see what happens after you press the button).
Summarize
To sum up, Angular's dependency injection provides many advanced features that you will want and often use on the commercial application production line. The convenience of factories, services, and providers often gives Angular developers the illusion that only the only option is available here. The magic is that the $injector service, you can use it to generate the required singletons, create new components, or dynamically refer to methods with dependencies.
Finally, it is important to note that the injections in your client code are available even outside of Angular. Let's look at an example wrapped outside Angular, calling $log service by injecting, clickhere. Why pass 'ng' into an array of methods? It is the core module of Angular, which will be added implicitly when you wrap your module, but if your directive wants to generate its own injection instance, you must add it explicitly.