一、提供商注册到模块中还是组件中?
1.通常,优先把模块中具体特性的提供商注册到模块中(@NgModule.providers
),而不是组件中(@Component.providers
)。
应该把全应用级提供商添加到根模块AppModule
中,而不是还是根组件AppComponent
中
这是因为惰性加载模块及其组件可以注入AppModule
中的服务,却不能注入AppComponent
中的。
只有当该服务必须对AppComponent
组件树之外的组件不可见时,才应该把服务注册进AppComponent
的providers
中。 这是一个非常罕见的异常用法。
2.当你必须
把服务实例的范围限制到某个组件及其子组件树时,就把提供商注册到该组件中
指令的提供商也同样照此处理。
二、不要在共享模块中把应用级单例添加到providers
中:
虽然很多组件都共享着同一个服务实例,但它们是靠 Angular 的依赖注入体系实现的,而不是模块体系。
比如很多组件都注入了UserService,
在整个应用程序中,只应该有一个UserService
的实例,并且它只应该有一个提供商。换句话说,UserService
是全应用级单例, 我们不希望每个模块都各自有它的实例。 而如果由SharedModule
提供UserService
,就会导致铁板钉钉的危险, 否则如果一个惰性加载模块导入了此共享模块,就会导致它自己也生成一份此服务的实例。
三、我要如何知道一个模块或服务是否已经加载过了?
某些模块及其服务只能被根模块AppModule
加载一次。 在惰性加载模块中再次导入这个模块会导致错误的行为,这个错误可能非常难于检测和诊断。为了防范这种风险,我们可以写一个构造函数,它会尝试从应用的根注入器中注入该模块或服务。如果这种注入成功了,那就说明这个类是被第二次加载的,我们就可以抛出一个错误,或者采取其它挽救措施。
constructor (@Optional() @SkipSelf() parentModule: CoreModule) {
if (parentModule) {
throw new Error(
'CoreModule is already loaded. Import it in the AppModule only');
}
}
四、模板引用变量 ( #var )
模板引用变量通常用来引用模板中的某个DOM元素,它还可以引用Angular组件或指令或Web Component,模板引用变量的作用范围是整个模板。不要在同一个模板中多次定义同一个变量名,否则它在运行期间的值是无法确定的。
使用井号 (#) 来声明引用变量(我们也可以用ref-
前缀代替#
):
#phone
的意思就是声明一个名叫phone
的变量来引用<input>
元素。
<input #phone placeholder="phone number">
我们可以在模板中的任何地方引用模板引用变量,比如在<input>上的phone变量就是在模板另一侧的<button>上使用的:
<input #phone placeholder="phone number">
<!-- lots of other elements -->
<!-- phone是输入元素,将它的value值传递给事件处理程序 -->
<button (click)="callPhone(phone.value)"> Call </button>
大多数情况下,Angular会把模板引用变量的值设置为声明它的那个元素。 在上一个例子中,phone
引用的是表示电话号码的<input>
框。 "拨号"按钮的点击事件处理器把这个input值传给了组件的callPhone
方法。
不过,指令也可以修改这种行为,让这个值引用到别处,比如它自身。 NgForm
指令就是这么做的:
<form (ngSubmit)="onSubmit(heroForm)" #heroForm="ngForm">
<div class="form-group">
<label for="name">Name
<input class="form-control" name="name" required [(ngModel)]="hero.name">
<!-- name属性的用途是有效性验证和对表单元素的变更进行追踪 -->
</label>
</div>
<button type="submit" [disabled]="!heroForm.form.valid">Submit</button>
</form>
<div [hidden]="!heroForm.form.valid">{{submitMessage}}<div>
这里的heroForm
实际上是一个Angular NgForm 指令的引用, 因此具备了跟踪表单中的每个控件的值和有效性的能力。
应用实例:
<div class="container">
<div [hidden]="submitted">
<h1>Hero Form</h1>
<!-- 模版引用:heroForm是一个Angular NgForm 指令的引用 -->
<form (ngSubmit)="onSubmit()" #heroForm="ngForm">
<div class="form-group">
<label for="name">Name</label>
<!-- 模版引用 -->
<input type="text" class="form-control" id="name" required
[(ngModel)]="model.name" name="name" #name="ngModel">
<div [hidden]="name.valid || name.pristine"
class="alert alert-danger">
Name is required
</div>
</div>
<div class="form-group">
<label for="alterEgo">Alter Ego</label>
<input type="text" class="form-control" id="alterEgo"
[(ngModel)]="model.alterEgo" name="alterEgo">
</div>
<div class="form-group">
<label for="power">Hero Power</label>
<!-- 模版引用 -->
<select class="form-control" id="power" required
[(ngModel)]="model.power" name="power" #power="ngModel">
<option *ngFor="let pow of powers" [value]="pow">{{pow}}</option>
</select>
<div [hidden]="power.valid || power.pristine" class="alert alert-danger">
Power is required
</div>
</div>
<button type="submit" class="btn btn-success"
[disabled]="!heroForm.form.valid">Submit</button>
<button type="button" class="btn btn-default" (click)="newHero();
heroForm.reset()">New Hero</button>
</form>
</div>
<!-- 表单提交之后显示 -->
<div [hidden]="!submitted">
<h2>You submitted the following:</h2>
<div class="row">
<div class="col-xs-3">Name</div>
<div class="col-xs-9 pull-left">{{ model.name }}</div>
</div>
<div class="row">
<div class="col-xs-3">Alter Ego</div>
<div class="col-xs-9 pull-left">{{ model.alterEgo }}</div>
</div>
<div class="row">
<div class="col-xs-3">Power</div>
<div class="col-xs-9 pull-left">{{ model.power }}</div>
</div>
<br>
<!-- 点击Edit按钮之后,可编辑的表单不再隐藏,可以重新编辑提交 -->
<button class="btn btn-primary" (click)="submitted=false">Edit</button>
</div>
</div>
五、为什么建议每个文件只放一个类?
在同一个文件中有多个类容易造成混淆,最好避免。 开发人员期望每个文件只放一个类。如果我们蔑视这个建议,并且 —— 比如说 —— 把HeroService
和HeroesComponent
组合在同一个文件里, 就得把组件定义放在最后面! 如果把组件定义在了服务的前面, 在运行时抛出空指针错误。