Angular2 之 依赖注入

依赖注入这部分分为两部分来学习。第一部分自然是官网上的文档,另外一部分,是自己的“血泪时间史”。

之所以称之为“血泪时间史”,是因为在这部分上花费的时间实在是有点多,也就是前面提到过的“时间的教训”,所以在这里要记录下来,避免下次再犯这样的错误。

依赖注入

依赖注入是一个用来管理代码依赖的强大模式。

应用程序全局依赖

在这里主要说的是,在应用程序根组件AppComponent中注册那些被应用程序全局使用的依赖提供商。

import { LoggerService }      from './logger.service';
import { UserContextService } from './user-context.service';
import { UserService }        from './user.service';

@Component({
  moduleId: module.id,
  selector: 'my-app',
  templateUrl: 'app.component.html',
  providers: [ LoggerService, UserContextService, UserService ]
})
export class AppComponent {
/* . . . */
}

在@Component元数据的providers数组导入和注册了几个服务,这些服务都是使用类来实现的,所以服务类能充当自己的提供商。==> 泪在providers数组中就算是注册成功了。

我们在根组件中注册了这些服务,所以我们可以在应用程序的任何地方,把它们注入到任何组件和服务的构造函数中去!

@Injectable()

  • @Injectable装饰器只在一个服务类有自己的依赖的时候,才是必不可少的。
@Injectable()
export class UserContextService {
  constructor(private userService: UserService, private loggerService: LoggerService) {
  }
}

这个@Injectable()就是必不可少的。

  • AppComponent类有两个依赖,但它没有@Injectable()。 它不需要@Injectable(),这是因为组件类有@Component装饰器。 在用TypeScript的Angular应用里,有一个单独的装饰器 — 任何装饰器 — 来标识依赖的类型就够了。

把服务作用域限制到一个组件支树

一个组件中注入的服务依赖,会在该组件的所有子组件中可见,而且Angular会把同样的服务实例注入到需要该服务的子组件中。

这刚好在实际工作有遇到这样的例子。

我在sino-base-data-service.component组件中注入了BaseDataService,那么它的所有子组件sino-list.component就都能访问到这个service。

多个服务实例

主要讲的是,在组件级别注入服务,每个组件就能拥有自己独立的service实例。这样就能保证每个组件都有自己的服务。每个组件都有自己的工作状态,与其他组件的服务与状态相隔离,这种我们成为沙盒化。每个服务和组件都在自己的沙盒里运行。

@Optional 和 @Host

当组件申请一个依赖时,Angular从该组件本身的注入器开始,沿着依赖注入器的树往上找,直到找到第一个符合要求的提供商。如果罩杯多就会抛出一个错误。

@Optional

当Angular找不到依赖时,@Optional装饰器会告诉Angular继续执行,Angualr会把此注入参数设置为null(而不是默认的抛出错误的行为)。

@Host

该装饰器将把往上搜索的行为截止在宿主组件

使用提供商来定义依赖

  • useValue - 值提供商
  • useClass
  • useExisting
  • useFactory

useValue

通常在单元测试中使用。useValue的值必须是立即定义的。

e.g.

 { provide: Hero,          useValue:    someHero },
 { provide: TITLE,         useValue:   'Hero of the Month' },

useClass

useClasst提供商创建并返回一个指令类的新实例。
使用该技术来为公共或默认类提供备选实现。该替代品能实现一个不同的策略,比如拓展默认类或者在测试的时候假冒真实类。

{ provide: HeroService,   useClass:    HeroService },
{ provide: LoggerService, useClass:    DateLoggerService },

useExisting - 别名-提供商

使用useExisting,提供商可以把一个令牌映射到另一个令牌上。实际上,第一个令牌是第二个令牌所对应的服务的一个别名,创造了访问同一个服务对象的两种方法

{ provide: MinimalLogger, useExisting: LoggerService },

通过使用别名接口来把一个API变窄,是一个很重要的该技巧的使用例子.

useFactory - 工厂-提供商

useFactory提供商通过调用工厂函数来新建一个依赖对象。

 { provide: RUNNERS_UP,    useFactory:  runnersUpFactory(2), deps: [Hero, HeroService] }

知识点

  • 令牌 - 使用提供商来定义依赖

我们通常在构造函数里面,为参数指定类型,让Angular来处理依赖注入。该参数类型就是依赖注入器所需的令牌。 Angular把该令牌传给注入器,然后把得到的结果赋给参数。

使用angular的DI系统来获取参数时,必须将该令牌需要依赖的其他服务一并注入。
e.g.
要模拟BaseDataService必须有ModuleConfig

 export class CrudModule {
  static forRoot(config: any, routeConfig?: any): ModuleWithProviders {
    return {
      ngModule: CrudModule,
      providers: [
        BaseDataService,
        {
          provide: ModuleConfig,
          useValue: config,
        }, {
          provide: RouteConfig,
          useValue: routeConfig ? routeConfig : DEFAULT_ROUTE_CONFIG,
        },
      ],
    };
  }
}
@Injectable()
export class SinoListComponent implements OnInit {
  constructor(
    private config: ModuleConfig,
    private baseDataService: BaseDataService,) {}
}
  • ng-content

    把对应组件中的内容投影进组件的视图中。

<ion-content>
  <sino-loading-hint [state]="state" loadingTitle="loading..." needRefresh=true (onVoted)="onVoted($event)">
    <h2 class="sino-list-title">{{title}}</h2>
    <div class="sino-list" *ngFor="let item of datas; let i = index" (click)="toDetail(item, i)">
      <h3>{{item.title}}</h3>
      <p>
        来自:{{item.ngDepartmentName}}的发文申请<br/>
        <span>发送时间:{{item.signDate}}</span>
      </p>
    </div>
  </sino-loading-hint>
</ion-content>

“`

写在后面

GitHub上集大家之力搞了一个前端面试题的项目,里面都是大家面试时所遇到的题以及一些学习资料,有兴趣的话可以关注一下。如果你也有兴趣加入我们的话,请在项目中留言。项目同时也可以在gitbook上查看。

InterviewLibrary-GitHub
InterviewLibrary-gitbook

©️2020 CSDN 皮肤主题: 大白 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值