Angular术语概念的理解
装饰器(Decorator)
装饰器是一种设计模式,其主要应用场景是动态的将多个责任附加到一个对象上
,这是一种比继承更有弹性的面对对象设计方法,缺点是代码中会出现大量的小类。
Angular中定义了许多装饰器,他们是用于修饰 JavaScript 类的函数,用来将各种功能零件装饰在一个个类上
。具体的实现方式是把一些特定用途的组件以预定义的方式附加到类上,组合成一个拥有完整功能的模块,以便于 Angular 框架了解这些这些类的含义并知道如何调用它们。
模块(NgModule)
Angular中的模块本质上是存放一些内聚代码块的容器
,一个模块相当于一个功能单元,它们分别专注于某个功能或者工作流程,比如登录注册功能可以作为一个模块。Angular项目中所有模块会被组装成一棵树
,树的根节点是AppModule
。Angular的模块和 JavaScript中的模块不同,但是有一定的互补性。
Angular中定义一个模块的方法是通过使用@NgModule
装饰器装饰一个类,@NgModule
装饰器为一个模块声明了编译的环境,即其作用域由@NgModule
定义。例如,我们可以导入一些由其它模块中导出的功能,并导出一些指定的功能供其它模块使用。
// 一个用户模块的例子,模块使用@NgModule装饰
@NgModule({
declarations: [ UserMainComponent, WritePostComponent ],
exports: [UserMainComponent],
imports:[CommonModule,ReactiveFormsModule,SharedModule,PostSharedModule,RouterModule.forChild(userRoutes)],
providers: [PostTableService]
})
export class UserModule {}
组件(Component)
Angular中的视图指的是最终显示在浏览器上的整个页面或页面的一部分。一块视图由组件类和模板构成,即一个组件类+一个模板=一块视图
。一个组件通常是指组件类+模板
。一个组件可以作为标签来嵌入到其他模板中,以此来形成一个有复杂层次的视图结构。
Angular中定义一个组件类的方式是通过@Component
装饰器修饰一个类,这个类包含了与模板交互所需的数据和逻辑。模板是一个通过@Component
装饰器与模板类绑定的html文件。
当Angular创建、更新、销毁一些组件时。我们可以通过重写生命周期钩子方法的方式,来在每个特定的时机执行特定的业务逻辑。
@Component({
selector: 'app-hero-list',
templateUrl: './hero-list.component.html',
providers: [ HeroService ]
})
export class HeroListComponent implements OnInit {
}
模板语法
模板语法主要包括了:组件标签、管道、指令、数据绑定
数据绑定
模板中可以使用数据绑定来实现组件类和模板之间数据的交互
,Angular可以在组件类和模板DOM之间双向的进行数据传输。
<!-- 以下是数据绑定的四种形式 -->
<li>{{hero.name}}</li> <!-- 将组件类中的hero变量的name属性的值,从组件传入到模板中 -->
<!-- 下面这个是组件标签,它在模板中嵌入其他组件,并调用了其他组件的selectedHero()方法获得返回值传给hero -->
<app-hero-detail [hero]="selectedHero"></app-hero-detail>
<li (click)="selectHero(hero)"></li> <!-- 通过click点击事件,将hero属性的值从模板传到组件类的selectHero()方法中,并调用方法 -->
<input [(ngModel)]="hero.name"> <!-- 这个是双向的数据绑定,数据可以在模板和组件类中双向的流动 -->
管道
Angular里的管道可以将 我们在类中定义的转换逻辑用在模板中。带有@Pipe 装饰器并实现PipeTransform接口的类中会定义一个转换函数,用来把输入值转换成供视图显示用的输出值。
Angular 自带了很多管道,比如 date 管道和 currency 管道,也可以定义一些新管道。在HTML 模板中使用管道时使用操作符 (|)。管道也可以串联起来,把一个管道的输出送给另一个管道进行转换。 并且能接收一些参数,来控制它该如何进行转换。
<!-- 把要使用的日期格式传给 date 管道 -->
<!-- Default format: output 'Jun 15, 2015'-->
<p>Today is {{today | date}}</p>
<!-- fullDate format: output 'Monday, June 15, 2015'-->
<p>The date is {{today | date:'fullDate'}}</p>
<!-- shortTime format: output '9:43 AM'-->
<p>The time is {{today | date:'shortTime'}}</p>
指令(Directives)
Angular 的模板是动态的。当 Angular 渲染它们的时候,会根据指令对 DOM进行转换。指令就是一个带有 @Directive 装饰器的类。
组件本质上是一种特殊的指令。除组件外,还有两种指令:结构型指令和属性型指令。和组件一样,指令的元数据把指令类和一个 selector 关联起来,selector 用来把该指令插入到 HTML 中。 在模板中,指令通常作为属性出现在元素标签上,可能仅仅作为名字出现,也可能作为赋值目标或绑定目标出现。
结构型指令
结构型指令通过添加、移除或替换 DOM 元素来修改布局。 这个范例模板使用了两个内置的结构型指令来为要渲染的视图添加程序逻辑:
<!-- *ngFor 是一个迭代器,它要求 Angular 为 list 列表中的每个 item 渲染出一个 `<li>`标签 -->
<li *ngFor="let item of list"></li>
<!-- *ngIf 是个条件语句,只有当选中的英雄存在时,它才会包含 HeroDetail 组件。 -->
<app-hero-detail *ngIf="selectedHero"></app-hero-detail>
属性型指令
属性型指令会修改现有元素的外观或行为。 在模板中,它们看起来就像普通的 HTML 属性一样,因此得名“属性型指令”。
ngModel 指令就是属性型指令的一个例子,它实现了双向数据绑定。 ngModel 修改现有元素(一般是 )的行为:设置其显示属性值,并响应 change 事件。
<input [(ngModel)]="hero.name">
Angular 还有很多预定义指令,它们或者修改布局结构(比如 ngSwitch),或者修改 DOM 元素和组件的某些方面(比如 ngStyle 和 ngClass)。
服务与依赖注入
服务
Angular为了把组件和服务区分开,以提高模块性和复用性。对于与特定视图无关并希望跨组件共享的数据或逻辑,可以创建服务类。使用@Injectable
装饰器可以定义一个服务类。该装饰器提供的元数据可以让服务作为依赖被自动注入到需要的组件中。
其中组件一般只用于提供数据绑定的属性和方法,作为视图和业务逻辑的中介者。组件不应该包含任何诸如从服务器获取数据、验证用户输入或直接往控制台中写日志等工作。而要把这些任务委托给各种服务。通过把业务逻辑定义在可注入的服务类中的方式,可以让任何组件使用某一种服务。
// 这是一个服务类的例子,用于把日志记录到浏览器的控制台
@Injectable()
export class Logger {
log(msg: any) { console.log(msg); }
error(msg: any) { console.error(msg); }
warn(msg: any) { console.warn(msg); }
}
// 服务也可以依赖其它服务。
// 比如,下面的HeroService 就依赖于 Logger 服务,它还用 BackendService 来获取英雄数据。
// BackendService 还可能再转而依赖 HttpClient 服务来从服务器异步获取英雄列表。
@Injectable()
export class HeroService {
private heroes: Hero[] = [];
constructor(
private backend: BackendService,
private logger: Logger) { }
getHeroes() {
this.backend.getAll(Hero).then( (heroes: Hero[]) => {
this.logger.log(`Fetched ${heroes.length} heroes.`);
this.heroes.push(...heroes); // fill cache
});
return this.heroes;
}
}
依赖注入(DI)
依赖注入是面向对象编程中的一种设计原则,是面对对象编程的一种解耦方式。它将创建对象实例的权限交给了框架(控制反转)。当 Angular 创建组件类的新实例时,它会通过查看该组件类的构造函数,来决定该组件依赖哪些服务或其它依赖项。
// 比如 HeroListComponent 的构造函数中需要 HeroService
constructor(private service: HeroService) { }
当 Angular 发现某个组件依赖某个服务时,它会首先检查应用中是否已经有了那个服务的实例。如果该服务实例还不存在就会创建该服务的实例,并把该服务实例的传给构造方法。
// 注册服务有三种方式
// 在服务类中注册到应用全局中,此时该服务是全局单例的
@Injectable({
providedIn: 'root',
})
// 在 @NgModule 中注册时,该服务的同一个实例将会对该 NgModule 中的所有组件可用
@NgModule({
providers: [
BackendService,
Logger
],
...
})
// 在 @Component 中注册时,会为该组件的每一个新实例提供该服务的一个新实例
@Component({
selector: 'app-hero-list',
templateUrl: './hero-list.component.html',
providers: [ HeroService ]
})
路由
Angular 的 Router 模块可以让我们定义在应用的各个视图及其状态之间导航时要使用的路径。定义导航规则是通过URL关联组件。Angular应用可以根据URL决定要显示或隐藏哪些视图或者对用户的输入做出响应。
其中,路由器会把类似 URL 的路径映射到组件对应的视图而不是页面。当用户执行一个动作时(比如点击链接),本应该在浏览器中加载一个新页面,但是路由器拦截了浏览器的这个行为,并显示或隐藏一个视图层次结构。如果路由器需要的模块尚未加载,路由器可以根据配置按需惰性加载。