1. Use di
Dependency injection is a very important programming pattern. Angular has its own dependency injection framework, leaving it, we can hardly build Angular applications. It is so widely used that almost everyone will simply call it DI.
Let's look at a simple example:
Export Class Animal { Dogs Constructor () { var dog = new Dog (); } } |
Our animal creates everything that is needed in the constructor manually. The problem is that our Animal
class is too fragile, inflexible and hard to test.
When our animal class needs a Dog and does not request a ready-made instance, it creates a new copy for itself in the constructor with the specific dog class.
What if the dog class is upgraded and its constructor requires a parameter to be passed in? Our animal class is destroyed, and it is bad until we rewrite the code that created the engine to dog= new Dog (Thenewparameter). But when the definition of the dog changes, we have to care, and the animal class has to change. This will make the animal class too fragile.
Now, every animal has its own unique dog. He could not be shared by other animal. Our animal lacks the necessary flexibility to share with other animal-class consumers.
How can we make animal stronger, more resilient and testable?
The answer is super simple. We have transformed the animal constructor into a version that uses DI:
Export Class Animal { Dogs Constructor (private Dog:dog) { } } |
What happened? We moved the dependency definition into the constructor. Our animal class no longer creates a dog, it simply "consumes" them. If someone expands the dog class, it is no longer the animal of the class.
2. Angular DI
Angular comes with its own dependency injection framework. This framework can also be used as a standalone module in other applications and frameworks.
2.1 Injector Tree
Angular has a multistage dependency injection system. In fact, there is an injection tree parallel to the component tree in the application, and we can reconfigure the injector at any level in the component tree. The usual number of injectors is in the following form:
When an underlying component requests a dependency, Angular first tries to satisfy it with the component's own injector. If the component's injector does not find the corresponding provider, it passes the application to the injector of its parent component for processing. If the injector does not satisfy the application, it continues to be transferred to its parent component's injector. The application continues to bubble up-until we find an injector that can handle the application or go beyond the ancestor location in the component tree. If the ancestors in the component tree were not found, Angular throws an error.
2.2 Implementation principle
The angular provides tokens for the dependency injector to get the service. Typically inside a constructor, you specify the type for the parameter and let Angular handle the dependency injection. The parameter type is the token required by the dependency injector. Angular passes the token to the injector and assigns the resulting result to the parameter.
Where does the injector get the dependency? It may already have a dependency in its own internal container. If it does not, you can also create a new one with the help of the provider. A provider is a recipe for delivering services that is associated with a token. Angular creates a service result from the vendor based on the token and stores it inside the injector for later invocation.
2.3 Tokens
When we register a provider for an injector, we actually associate this provider with a DI token. The injector maintains an internal token- provider Mapping table, which is referenced when a dependency is requested. The token is the key value in this mapping table.
2.3.1 Class Dependency
In general, a dependency value is a class instance , and the type of the class is its own lookup key value. In this case, we actually get an instance of the type as a token directly from the injector.
It is fortunate for us to write a constructor that requires class-based dependency injection. As long as we define a constructor parameter as a class type, Angular will know to inject the services associated with the class token, and most of the dependency values are provided in the form of classes.
For example, where animal relies on the dog class and provides the dog type in the constructor, it can rely on injecting the corresponding instance of the dog class.
Export Class Animal { Dogs Constructor (private Dog:dog) { } } |
Note that the TypeScript interface is not a valid token.
2.3.2 Non-class dependent
What if the dependency value is not a class? Sometimes what we want to inject is a string, a function or an object.
Applications often define configuration objects for a number of small factors, such as the application's title, or the address of a network API endpoint, but these configuration objects are not always instances of the class.
But what do we use as tokens in this situation? The solution is to define and use a opaquetoken. This is defined in a way similar to this:
Import {Opaquetoken} from ' @angular/core '; Export Let App_config = new Opaquetoken (' App. config '); |
We use this OpaqueToken
object to register dependent providers:
Providers: [{provide:app_config, usevalue:hero_di_config}] |
Now, with @Inject
the help of this, we can inject this configuration object into any constructor that needs it:
Constructor (@Inject (app_config) config:appconfig) { This.title = Config.title; } |
Although the ConfigAppConfig
interface has no effect on the dependency injection process, it provides strongly typed information for the configuration objects in the class.
2.4 Providers
Provider provides a runtime version of dependency injection. The injector relies on the provider to create an instance of the service, which is injected into the component or other service by the injector.
Using the provide object as a provider in angular, the provide object requires a token and a definition object, which is usually a class, but not necessarily.
2.4.1 uservalue-Value Providers
The definition object has a primary attribute (that is, Uservalue), which identifies how the provider creates and returns dependencies.
Assigns a fixed value, that is, the provider can assign it as the value returned by the dependent object, to the Uservalue property.
This technique is often used to set runtime constants, such as the base address and function flags of a website. We have seen an example in opaquetoken that we have provided a constant as a definition object for App_config.
{provide:app_config, usevalue:hero_di_config} |
The value of a value provider must be defined immediately. The value of it cannot be defined afterwards. It is clear that the title string is immediately available.
2.4.2 Useclass-Class provider
The UserClass provider creates and returns a new instance of the specified class. Use this technique to provide an alternative implementation for a public or default class. In general, the newly created class is also an injection token for that provider, such as a provider that provides log services
{Provide:loggerservice, Useclass:loggerservice} |
When we rely on injection loggerservice, we create a default example based on class Loggerservice to return as a result.
When a dependency injection class has other types of dependencies, such as Loggerservice relies on user information, we also use the constructor injection pattern to add a constructor with the Loggerservice parameter. In this case, injectable annotations are required.
@Injectable () indicates that a class can be instantiated by an injector. When our Loggerservice service has an injected dependency, we need to use @injectable () to identify the loggerservice so that angular can inject one using the metadata of the constructor parameters 用户服务
.
2.4.3 Useexisting-alias Provider
Useexisting, a provider can map a token to another token. In fact, the first token is an alias for the service that corresponds to the second token, creating two ways to access the same service object.
{Provide:minimallogger, Useclass:loggerservice} |
Narrowing an API by using an alias interface is an important example of the use of this technique. We are here to use aliases for this purpose. Imagine if Loggerservice has a very large API interface (although it actually has only three methods, one attribute), by using the Minimallogger class-interface alias, you can successfully reduce the API interface to only expose two members:
Export abstract class Minimallogger { Loginfo: (msg:string) = void; Logs:string[]; } |
2.4.4 Usefactory Factory Supplier
The Usefactory provider creates a new dependent object by calling the factory function, which is used primarily to create an object with parameters as the provider.
Using this technique, you can build a dependent object with a factory function that contains some dependent services and local state input, which is not necessarily a class instance, it can be anything. For example
Export function Factory (level) { return new Logger (level) } {provide:runners_up, usefactory:factory, Deps: [2]} |
2.5 Configure the injector.
In general, there are two types of input, one is injected into the ngmodule, and one is injected into the component. Both types are injected into the providers array in the metadata, and the difference is different in the scope of the entry into effect, and the injection in component takes effect only in the current component and in the subassembly. For example
providers:[ UserService, {Provide:logger, Useclass:evenbetterlogger} ] |
Where UserService is the shorthand for the class provider
{Provide:userservice, Useclass:userservice} |
2.6 Using Di
We know how to configure a service provider, so let's take a look at how to use it.
2.6.1 constructor function
Typically, we use constructor parameters to inject the corresponding service. Generally speaking, there are two main situations.
First, it is a dependency injection of a type as a token, in which case a dependency injection can be made directly using the parameter types in the constructor, such as using the dog type in animal
Export Class Animal { Dogs Constructor (private Dog:dog) { } } |
Second, you can use the form of @inject (' token ') to inject an object or service of the type of token, such as the configuration object for which we inject value types
Constructor (@Inject (app_config) config:appconfig) { This.title = Config.title; } |
2.6.2 getting the parent component
In general, getting a parent component is getting a component type that already exists, and the parent component must be implemented by providing an alias provider, such as
Providers: [{provide:parent, Useexisting:forwardref (() = parentcomponent)}] |
The parent is the provider's token, and parentcomponent is the type of the alias provider that injects the type provider into the parent component's injector, and the child component can use the parent token as the constructor parameter type to inject the service. Gets the parentcomponent. Parentcomponent refers to itself, causing a circular reference, and must use a forward reference ForwardRef break the loop to find the current or parent provider.
2.6.3 Skip itself with optional
When we don't want to get dependencies from the current element, we can use @skipself () so that the injector starts searching for a dependency from a component at its top level Parent
. At the same time, you can use the @optional () annotation when there is no way to ensure that a dependency exists, and in order to avoid a dependency error condition being thrown, so that the dependency is optional, such as the constructor that introduces the parent component, which can be written in the following format:
Constructor (@SkipSelf () @Optional () public parent:parent) {} |
ANGULAR2 Dependency Injection