angular js依赖注入是通过 方式实现的,依赖注入是angularjs框架的其中一个关键特性
本篇文章给大家介绍一下依赖注入在有角的中的应用,希望对大家有所帮助!
本文通过实际案例,带大家了解依赖注入在有角的中的应用和部分实现原理,其中包括
使用工厂、使用类、使用值和使用现有的不同提供商的应用场景
模块注入器和元素注入器不同层次注入器的意义
@可注射()和@NgModule()中定义供应者的区别
@Optional()、@Self()、@SkipSelf()、@Host()修饰符的使用
穆蒂(多提供商)的应用场景
【相关教程推荐: 《angular教程》 】
如果你还不清楚什么是依赖注入,可以先看下这篇文章详解依赖注入
useFactory、useClass、useValue 和 useExisting 不同类型provider的应用场景
下面,我们通过实际例子,来对几个提供商的使用场景进行说明。
useFactory 工厂提供商
某天,咱们接到一个需求:实现一个本地存储的功能,并将其注入到有角的应用中,使其可以在系统中全局使用
首先编写服务类存储.服务. ts实现其存储功能
//storage.service.ts
导出类存储服务{
get(key: string) {
返回JSON。解析(本地存储。getitem(key) { } ) { };
}
set(key: string,value:ITokenModel null):boolean {
localStorage.setItem(key,JSON。stringify(value));
返回真实的
}
移除(键:字符串){
本地存储。移除项目(键);
}
}如果你马上在用户。组件。分时(同timesharing)中尝试使用
//user.component.ts
@组件({
选择器:"应用程序用户",
templateUrl:“”./user.component.html ,
样式URL:[ ./user.component.css]
})
导出类CourseCardComponent {
构造函数(私有存储服务:存储服务){
.
}
.
}你应该会看到这样的一个错误:
NullInjectorError:没有存储服务的提供程序!显而易见,我们并没有将存储服务添加到有角的的依赖注入系统中角形的。无法获取存储服务依赖项的提供商,也就无法实例化这个类,更没法调用类中的方法。
接下来,我们本着缺撒补撒的理念,手动添加一个提供商。修改存储。服务。分时(同timesharing)文件如下
//storage.service.ts
导出类存储服务{
get(key: string) {
返回JSON。解析(本地存储。getitem(key) { } ) { };
}
set(key: string,value: any) {
localStorage.setItem(key,JSON。stringify(value));
}
移除(键:字符串){
本地存储。移除项目(键);
}
}
//添加工厂函数,实例化存储服务
exportstorageserviceproviderfactory():存储服务{
返回新的存储服务();
}通过上述代码,我们已经有了提供商。那么接下来的问题,就是如果让有角的每次扫描到存储服务这个依赖项的时候,让其去执行storageServiceProviderFactory方法,来创建实例
这就引出来了下一个知识点注射代币
在一个服务类中,我们常常需要添加多个依赖项,来保证服务的可用。而注射代币是各个依赖项的唯一标识,它让有角的的依赖注入系统能准确的找到各个依赖项的提供商。
接下来,我们手动添加一个注射代币
//storage.service.ts
从" @角度/核心"导入{注入令牌};
导出类存储服务{
get(key: string) {
返回JSON。解析(本地存储。getitem(key) { } ) { };
}
set(key: string,value: any) {
localStorage.setItem(key,JSON。stringify(value));
}
移除(键:字符串){
本地存储。移除项目(键);
}
}
exportstorageserviceproviderfactory():存储服务{
返回新的存储服务();
}
//添加存储服务的注射代币
export const STORAGE _ SERVICE _ TOKEN=new InjectionTokenStorageService( AUTH _ STORE _ TOKEN );好吧,我们已经有了存储服务的供应者和注射开始。
接下来,我们需要一个配置,以便Angular的依赖注入系统可以识别它。扫描到StorageService(依赖项)时,我们会根据storage _ service _ token(注入令牌)找到对应的StorageServiceProvider工厂(提供者),然后创建这个依赖项的实例。如下,我们在模块的@NgModule()装饰器中配置它:
//user.module.ts
@NgModule({
进口:[
.
],
声明:[
.
],
提供商:[
{
Provide: storage _ service _ token,//依赖关系关联的InjectionToken用于控制工厂函数的调用。
use factory:storageserviceproviderfactory,//需要创建和注入依赖关系时调用这个工厂函数。
Deps: [] //如果StorageService有其他依赖项,请在此添加。
}
]
})
Classusermodule {}这里,我们已经完成了依赖的实现。最后,Angular需要知道在哪里注射。Angular提供了@Inject装饰器来识别
//user.component.ts
@组件({
选择器:“应用程序用户”,
templateUrl:“”。/user.component.html ,
样式URL:[。/user.component.css]
})
导出类CourseCardComponent {
构造函数(@ Inject(STORAGE _ SERVICE _ TOKEN)private STORAGE SERVICE:STORAGE SERVICE){
.
}
.
}此时,我们可以在user.component.ts中调用StorageService中的方法
useClass 类提供商
emm.是不是觉得上面的写法太复杂了,而在实际开发中,我们的大部分场景都不需要手动创建Provider和InjectionToken?如下所示:
//user.component.ts
@组件({
选择器:“应用程序用户”,
templateUrl:“”。/user.component.html ,
样式URL:[。/user.component.css]
})
导出类CourseCardComponent {
构造函数(私有存储服务:存储服务){
.
}
.
}
//storage.service.ts
@ injectible({ provided in: root })
导出类存储服务{}
//user.module.ts
@NgModule({
进口:[
.
],
声明:[
.
],
提供商:[存储服务]
})
类用户模块{}接下来我们来分析一下上面的速记模式。
在user.component.ts,我们抛弃了@Inject decorator,直接加入了依赖私有StorageService: StorageService,这要感谢Angular对InjectionToken的设计。
//user.module.ts
@NgModule({
进口:[
.
],
声明:[
.
],
提供商:[{
Provide: StorageService,//使用构造函数名称作为InjectionToken
use factory:storageServiceProviderFactory,
deps: []
}]
})
Classusermodule {}注意:因为Angular的依赖注入系统是根据运行时环境中的InjectionToken来识别依赖的,所以进行依赖注入。所以这里不能用接口名作为InjectionToken,因为它只存在于Typescript语言的编译时,而不存在于运行时。至于类名,在运行时环境中体现为构造函数名,可以使用。
接下来我们可以用useClass代替useFactory,这样实际上可以达到创建实例的效果,如下:
.
提供商:[{
提供:存储服务,
useClass: StorageService,
deps: []
}]
.在使用useClass的时候,Angular会把后一个值作为构造函数,在运行时环境下,会直接执行新的指令来实例化它,这也省去了我们手动创建提供者的需要。
当然,基于Angular的依赖注入设计,我们可以写得更简单。
.
提供商:[存储服务]
.直接将类名写入providers数组,Angular会将其识别为构造函数,然后检查函数内部,创建一个工厂函数,找到其构造函数中的依赖项,最后实例化。
useClass的另一个特性是Angular会根据Typescript中依赖的类型定义自动找到提供者作为其运行时InjectionToken。因此,我们不需要使用@Inject decorator来告诉Angular在哪里注入它。
您可以缩写如下
.
//不需要手动注入:构造函数(@ inject(storage service)Private storage service:storage service)
构造函数(私有存储服务:存储服务){
.
}
.这也就是我们平常开发中,最常见的一种写法。
useValue 值提供商
完成本地存储服务的实现后,我们又收到了一个新需求,研发老大希望提供一个配置文件,来存储存储服务的一些默认行为
我们先创建一个配置
常量存储配置={
后缀: app_ //添加一个存储键的前缀
过期时间:24 * 3600 * 100 //过期时间,毫秒戳
}而使用价值则可以涉及住这种场景。其可以是一个普通变量,也可以是一个对象形式。
配置方法如下:
//config.ts
导出接口存储配置={
后缀:字符串;
过期时间:数字;
}
export const STORAGE _ CONFIG _ TOKEN=new InjectionTokenSTORAGE _ CONFIG( STORAGE-CONFIG );
导出常量存储配置={
后缀: app_ //添加一个存储键的前缀
过期时间:24 * 3600 * 100 //过期时间,毫秒戳
}
//user.module.ts
@NgModule({
.
提供商:[
存储服务,
{
提供:存储配置令牌,
使用值:存储配置
}
],
.
})
导出类用户模块{}在用户。组件。分时(同timesharing)组件中,直接使用配置对象:
//user.component.ts
@组件({
选择器:"应用程序用户",
templateUrl:“”./user.component.html ,
样式URL:[ ./user.component.css]
})
导出类CourseCardComponent {
构造函数(专用存储服务:存储服务,@ Inject(STORAGE _ CONFIG _ TOKEN)专用存储配置:存储配置){
.
}
getKey(): void {
const {后缀}=this。存储配置;
控制台。日志(这个。存储服务。获取(后缀demo’));
}
}
useExisting 别名提供商
如果我们需要基于一个已存在的供应者来创建一个新的提供商,或需要重命名一个已存在的供应者时,可以用使用现有的属性来处理。比如:创建一个有角的的表单控件,其在一个表单中会存在多个,每个表单控件存储不同的值。我们可以基于已有的表单控件供应者来创建
//新输入组件分时(同timesharing)
从" @angular/forms "导入{ ControlValueAccessor,NG _ VALUE _ ACCESSOR };
@组件({
选择器:"新建-输入",
导出为:新输入,
提供商:[
{
提供:NG_VALUE_ACCESSOR
使用现有的:forward ref(()=新输入组件),//这里的新输入组件已经声明了,但还没有被定义。无法直接使用,使用forwardRef可以创建一个间接引用,有角度在后续在解析该引用
多:真的
}
]
})
导出类新输入组件实现ControlValueAccessor {
.
}
ModuleInjector 和 ElementInjector 层级注入器的意义
在有角的中有两个注入器层次结构
模块注入器——使用@NgModule()或@可注射()的方式在模块中注入
元素喷射器——在@指令()或@组件()的提供者属性中进行配置
我们通过一个实际例子来解释两种注入器的应用场景,比如:设计一个展示用户信息的卡片组件
ModuleInjector 模块注入器
我们使用用户卡。组件。分时(同timesharing)来显示组件,用户服务来存取该用户的信息
//user-card.component.ts
@组件({
选择器:“user-card.component.ts”,
templateUrl:“”./user-card.component.html ,
样式URL:[ ./user-card.component.less]
})
导出类UserCardComponent {
.
}
//user.service.ts
@可注射({
提供了:"根"
})
导出类用户服务{
.
}上述代码是通过@可注射添加到根模块中,根即根模块的别名。其等价于下面的代码
//user.service.ts
导出类用户服务{
.
}
//app.module.ts
@NgModule({
.
提供者:[用户服务],//通过提供者添加
})
导出类应用模块{}当然,如果你觉得户服务只会在用户模块模块下使用的话,你大可不必将其添加到根模块中,添加到所在模块即可
//user.service.ts
@可注射({
提供者:用户模块
})
导出类用户服务{
.
}如果你足够细心的话,你会发现在上面的例子中,我们既可以在当前服务文件中通过@ injective({ provide in:XXX })定义提供者,也可以在其模块中通过@NgModule({ providers: [xxx]})定义提供者。那么,它们有什么区别呢?
除了使用@ Injectable()和@NgModule()的方式不同,还有一个很大的区别:
让我们用一个例子来解释上面的概述。随着业务的增长,我们拓展了两个服务,UserService1和UserService2,但是因为一些原因,UserService2一直没有使用。
如果依赖项是通过@NgModule()的提供者引入的,我们需要在user.module.ts文件中引入对应的user1.service.ts和user2.service.ts文件,然后在providers数组中添加UserService1和UserService2引用。而且由于模块文件中引用了UserService2的文件,Angular中的摇树器误以为这个UserService2已经被使用了。无法优化树。代码示例如下:
//user.module.ts
从导入UserService1。/user 1 . service . ts ;
从导入UserService2。/user 2 . service . ts ;
@NgModule({
.
提供程序:[用户服务1,用户服务2],//通过提供程序添加
})
ClassUsermodule {}那么,如果我们使用@ injective({在:usermodule}中提供),我们实际上是在服务类自己的文件中引用use.module.ts,并为其定义一个提供者。不需要在UserModule中重复定义,所以不需要导入user2.service.ts文件。因此,当UserService2不依赖时,可以对其进行优化。代码示例如下:
//user2.service.ts
从“”导入用户模块。/user . module . ts ;
@可注射({
提供者:用户模块
})
导出类UserService2 {
.
}
ElementInjector 组件注入器
了解了ModuleInjector之后,通过刚才的例子继续讲ElementInjector。
起初,我们的系统中只有一个用户,我们只需要一个组件和一个UserService来访问这个用户的信息。
//user-card.component.ts
@组件({
选择器:“user-card.component.ts”,
templateUrl:“”。/user-card.component.html ,
样式URL:[。/user-card.component.less]
})
导出类UserCardComponent {
.
}
//user.service.ts
@可注射({
providedIn:“根”
})
导出类用户服务{
.
}注意:上面的代码将UserService添加到根模块中,它只会被实例化一次。
此时,如果系统中有多个用户,每个用户卡组件中的UserService需要访问相应用户的信息。如果还是按照上面的方法,UserService只会生成一个实例。那么可能发生的情况是,张三保存数据后,李四去取数据,得到了张三的结果。
那么,我们有什么办法可以实例化多个UserService,让每个用户的数据访问操作都可以隔离开来呢?
答案是肯定的。我们需要在user.component.ts文件中使用ElementInjector来添加UserService的提供者。如下所示:
//user-card.component.ts
@组件({
选择器:“user-card.component.ts”,
templateUrl:“”。/user-card.component.html ,
样式URL:[。/user-card.component.less],
提供者:[用户服务]
})
导出类UserCardComponent {
.
}通过上面的代码,每个用户卡组件都会实例化一个UserService来访问自己的用户信息。
为了解释上述现象,我们需要谈谈Angular的组件和模块层次依赖注入。
@Optional()、@Self()、@SkipSelf()、@Host() 修饰符的使用
在角度应用中,当依赖项寻找提供者时,我们可以对对搜索结果进行容错处理或限制搜索的范围使用一些修饰符。
@Optional()
导出类UserCardComponent {
构造函数(@可选私有用户服务:用户服务){}
}
@Self()
如下,Angular只会在当前UserCardComponent的提供者中搜索匹配的提供者。如果不匹配,会直接报错。用户服务没有提供程序.
//user-card.component.ts
@组件({
选择器:“user-card.component.ts”,
templateUrl:“”。/user-card.component.html ,
样式URL:[。/user-card.component.less],
提供者:[用户服务],
})
导出类UserCardComponent {
构造函数(@Self() private userService?UserService) {}
}
@SkipSelf()
//子组件用户卡。组件。分时(同timesharing)
@组件({
选择器:“user-card.component.ts”,
templateUrl:“”./user-card.component.html ,
样式URL:[ ./user-card.component.less],
提供程序:[用户服务],//不起作用
})
导出类UserCardComponent {
构造函数(@SkipSelf() private userService?UserService) {}
}
//父组件父卡。组件。分时(同timesharing)
@组件({
选择器:“parent-card.component.ts”,
templateUrl:“”./parent-card.component.html ,
样式URL:[ ./parent-card.component.less],
提供商:[
{
提供:用户服务,
useClass: ParentUserService,//work
},
],
})
导出类ParentCardComponent {
构造函数(){}
}
@Host()
multi 多服务提供商
某些场景下,我们需要一个注射代币初始化多个提供商。比如:在使用拦截器的时候,我们希望在default.interceptor.ts之前添加一个用于代币校验的JWTInterceptor
.
const NET_PROVIDES=[
{ provide: HTTP_INTERCEPTORS,useClass: DefaultInterceptor,multi: true },
{ provide: HTTP_INTERCEPTORS,useClass: JWTInterceptor,multi: true }
];
.多:为错误的时,提供商的值会被覆盖;设置为没错,将生成多个供应者并与唯一InjectionToken HTTP _ INTERCEPTORS关联。最后可以通过HTTP _拦截器获取所有供应者的值
参考链接
角度依赖性注射:完整指南
有角的中的依赖注入
更多编程相关知识,请访问:编程教学!以上就是深入浅析有角的中怎么使用依赖注入的详细内容,更多请关注我们其它相关文章!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。