angualr - 模块与组件
模块
@NgModule 的设计意图
静态的元数据 (declarations)
运行时的元数据 (providers)
组合与分组 (imports 和 exports)
《angular从零到一》说的简单些:
NgModule是一个装饰器函数,它接收一个用来描述模块属性的元数据对象。其中最重要的属性是:
❑declarations:声明本模块中拥有的视图类。Angular有三种视图类:组件、指令和管道。
❑exports:declarations
的子集,可用于其他模块的组件模板。
❑imports:本模块声明的组件模板需要的类所在的其他模块。
❑providers:服务的创建者,并加入到全局服务列表中,可用于应用任何部分。
❑bootstrap:指定应用的主视图(称为根组件),它是所有其他视图的宿主。只有根模块才能设置bootstrap属性
插值表达式
插值是指将表达式嵌入到被标记的文本中。默认情况下,插值使用双花括号 {{
和 }}
作为定界符。
<p>footer works! {{year}} - {{year + 10}}</p>
组件
组件是Angular应用中最基本的U构造块
组件必须从属于某个 NgModule
才能被其它组件或应用使用
组件在 @NgModule 元数据的 declarations
字段中引用
@Component 元数据
@Component 装饰器会指出紧随其后的那个类是个组件类,并为其指定元数据。
在下面的范例代码中,你可以看到 HeroListComponent
只是一个普通类,完全没有 Angular 特有的标记或语法。 直到给它加上了 @Component 装饰器,它才变成了组件。
组件的元数据告诉 Angular 到哪里获取它需要的主要构造块,以创建和展示这个组件及其视图。具体来说,它把一个模板(无论是直接内联在代码中还是引用的外部文件)和该组件关联起来。该组件及其模板,共同描述了一个视图。
除了包含或指向模板之外,@Component 的元数据还会配置要如何在 HTML 中引用该组件,以及该组件需要哪些服务等等。
下面的例子中就是 HeroListComponent
的基础元数据:
@Component({
selector: 'app-hero-list',
templateUrl: './hero-list.component.html',
providers: [ HeroService ]
})
export class HeroListComponent implements OnInit {
/* . . . */
}
配置选项 | 详情 |
---|---|
selector |
一个 CSS 选择器,它会告诉 Angular,一旦在模板 HTML 中找到了这个选择器对应的标签,就创建并插入该组件的一个实例。 比如,如果应用的 HTML 中包含 <app-hero-list></app-hero-list> ,``Angular 就会在这些标签中插入一个 HeroListComponent 实例的视图。 |
templateUrl |
该组件的 HTML 模板文件相对于这个组件文件的地址。``另外,你还可以用 template 属性的值来提供内联的 HTML 模板。这个模板定义了该组件的宿主视图。 |
providers |
当前组件所需的服务提供者的一个数组。``在这个例子中,它告诉 Angular 该如何提供一个 HeroService 实例,以获取要显示的英雄列表。 |
在组件的 @Component
装饰器中,添加一个名叫 animations:
的元数据属性。你可以把用来定义动画的触发器放进 animations
元数据属性中。
src/app/app.component.ts
@Component({
selector: 'app-root',
templateUrl: 'app.component.html',
styleUrls: ['app.component.css'],
animations: [
// animation triggers go here
]
})
组件生命周期
ngOnChanges 组件输入属性改变
子组件
scroll-lable-tab.component.html
<ul [ngStyle]="{'background-color': backColor}">
export class ScrollLableTabComponent implements OnInit,OnChanges{
selectIndex = -1;
@Input() menus: TopMenu[] = [];
@Input() backColor = "#fff";
@Output() tabSelected = new EventEmitter<TopMenu>();
handleSelected(index: number){
this.selectIndex = index;
this.tabSelected.emit(this.menus[this.selectIndex]);
}
ngOnInit(): void {
console.log("组件初始化");
}
ngOnChanges(changes: SimpleChanges): void {
console.log('组件输入属性改变',changes);
}
}
父级
app.component.html
<app-scroll-lable-tab [menus]="topMenus" [backColor]="backColor" (tabSelected)="handleSelectedTab($event)"></app-scroll-lable-tab>
app.component.ts
backColor = "red";
handleSelectedTab(topmenu: TopMenu){
const colors =['red','blue','black'];
const idx = Math.floor(Math.random()*3);
this.backColor = colors[idx];
console.log(topmenu);
}
方法1:@Output()
装饰器和EventEmitter
来通知父组件
方法2:父组件和子组件之间服务通信
在Angular中,除了使用@Output()
装饰器和EventEmitter
来通知父组件外,还有其他几种方式可以实现子组件与父组件之间的通信。
ngAfterContentInit 组件内容投影完成时
内容投影
内容投影 是从组件外部导入 HTML 内容,并把它插入在组件模板中指定位置上的一种途径。可以在目标中通过查找下列结构来认出内容投影。
- 元素标签中间的 HTML
- 组件模板中的
<ng-content>
标签
<ng-content>
标签是外来内容的占位符。它告诉 Angular 在哪里插入这些外来内容。在这里,被投影进去的内容就是来自父组件的<app-child>
标签。
Name Description select="selector"
Only select elements from the projected content that match the given CSS selector
.``仅从投影内容中选择与给定CSS “selector” 匹配的元素。Angular supportsselectors for any combination of tag name, attribute, CSS class, and the :not
pseudo-class.``Angular支持对标签名、属性、CSS类和“:not”伪类的任意组合使用[selectors]。<ng-content select="样式类/HTML标签/指令"></ng-content> //仅从投影内容中选择与给定 CSS 匹配的元素selector
例子,在 app-scoll-lable-tab
里面加入 div ,这里是不显示的
必须在这个组件加上 <ng-content>
生命周期钩子
1 挂载:
ngOnInit() 、ngAfterContentInit()、 ngAfterViewInit()
2 更新:
ngOnChanges()、ngDoCheck()
(不推荐)、ngAfterContentChecked()、ngAfterViewChecked()
3 卸载:
ngOnDestroy()
钩子方法 | 用途 | 时机 |
---|---|---|
ngOnChanges() 多次 | 当Angular设置或重设数据绑定的输入属性时做出响应。该方法接收一个包含当前和之前属性值的SimpleChanges 对象。 NOTE: 这种情况发生得非常频繁,所以你在这里进行的任何操作都会对性能产生重大影响。 See details in Using change detection hooks in this document.当 Angular 设置或重新设置数据绑定的输入属性时响应。``该方法接受当前和上一属性值的 SimpleChanges 对象 注意: 这发生的非常频繁,所以你在这里执行的任何操作都会显著影响性能。 引用类型属性值的改变,检测不到!!!!如果要更改引用类型,但可以给引用类型重新赋值(例如给对象赋值) 欲知详情,参阅本文档的使用变更检测钩子。 |
在 ngOnInit() 之前调用(如果组件有绑定的输入),并且在一个或多个数据绑定的输入属性发生变化时调用。NOTE: 如果你的组件没有输入或者你在使用它时没有提供任何输入,框架将不会调用 ngOnChanges() .如果组件绑定过输入属性,那么在 ngOnInit() 之前以及所绑定``的一个或多个输入属性的值发生变化时都会调用。注意: 如果你的组件没有输入属性,或者你使用它时没有提供任何输入属性,那么框架就不会调用 ngOnChanges() 。 |
ngOnInit() | 在 Angular 第一次显示数据绑定和设置指令/组件的输入属性之后,初始化指令/组件。欲知详情,参阅本文档中的初始化组件或指令。 | 在第一轮 ngOnChanges() 完成之后调用,只调用一次。而且即使没有调用过 ngOnChanges() ,也仍然会调用 ngOnInit() (比如当模板中没有绑定任何输入属性时)。 |
ngDoCheck()``多次,慎用 | 检测,并在发生 Angular 无法或不愿意自己检测的变化时作出反应。``欲知详情和范例,参阅本文档中的自定义变更检测。 | 紧跟在每次执行变更检测时的 ngOnChanges() 和首次执行变更检测时的 ngOnInit() 后调用。 |
ngAfterContentInit() | 当 Angular 把外部内容投影进组件视图或指令所在的视图之后调用。<br> 欲知详情和范例,参阅本文档中的响应内容中的变更。 |
第一次 ngDoCheck() 之后调用,只调用一次。 |
ngAfterContentChecked()``多次 | 每当 Angular 检查完被投影到组件或指令中的内容之后调用。<br> 欲知详情和范例,参阅本文档中的响应被投影内容的变更。 |
ngAfterContentInit() 和每次 ngDoCheck() 之后调用。 |
ngAfterViewInit() | 当 Angular 初始化完组件视图及其子视图或包含该指令的视图之后调用。<br> 欲知详情和范例,参阅本文档中的响应视图变更。 |
第一次 ngAfterContentChecked() 之后调用,只调用一次。 |
ngAfterViewChecked()``多次 | 每当 Angular 做完组件视图和子视图或包含该指令的视图的变更检测之后调用。 | ngAfterViewInit() 和每次 ngAfterContentChecked() 之后调用。 |
组件的输入输出
把数据发送到子组件
父组件 @Input
属性绑定
把数据发送到父组件
@Output() 必须是 EventEmitter 类型,它是 @angular/core
中用来发出自定义事件的类。
装饰器部件 | 详情 |
---|---|
@Output() |
一个装饰器函数,它把该属性标记为数据从子组件进入父组件的一种途径。 |
newItemEvent |
这个 @Output() 的名字。 |
EventEmitter<string> |
这个 @Output() 的类型。 |
new EventEmitter<string>() |
要求 Angular 创建一个新的事件发射器,它发出的数据是 string 类型的。 |
关于
EventEmitter
的详细信息,请参阅 EventEmitter API 文档。
emit()
: 发出包含给定值的事件。
subscribe()
: 注册此实例发出的事件的处理器。
事件绑定 (tabSelected)='handleSelectedTab($event)'
会把子组件中的 tabSelected
事件连接到父组件的 handleSelectedTab()
方法。
$event
中包含用户在子组件模板上的 <input>
中键入的数据。
获取DOM对象(组件模板):1.模板变量
模板变量可以帮助你在模板的另一部分使用这个部分的数据。使用模板变量,你可以执行某些任务,比如响应用户输入或微调应用的表单。
模板变量可以引用这些东西:
- 模板中的 DOM 元素
- 指令或组件
- 来自 ng-template 的 TemplateRef
- Web 组件
语法
在模板中,要使用井号 #
来声明一个模板变量。下列模板变量 #phone
声明了一个名为 phone
的变量,其值为此 <input>
元素
<input #phone placeholder="phone number" />
可以在组件模板中的任何地方引用某个模板变量。这里的 <button>
就引用了 phone
变量。
<input #phone placeholder="phone number" />
<!-- lots of other elements -->
<!-- phone refers to the input element; pass its `value` to an event handler -->
<button type="button" (click)="callPhone(phone.value)">Call</button>
获取DOM对象(组件类):2.ViewChild
模板在组件类中的引用 @ViewChild
属性装饰器,用于配置一个视图查询。 变更检测器会在视图的 DOM 中查找能匹配上该选择器的第一个元素或指令。 如果视图的 DOM 发生了变化,出现了匹配该选择器的新的子节点,该属性就会被更新
在调用 NgAfterViewInit
回调函数之前就会设置这些视图查询。
元数据属性:
- selector - 用于查询的指令类型或名字。
- read - 从查询到的元素中读取另一个令牌。
- static - 如果为 true,则在变更检测运行之前解析查询结果,如果为 false,则在变更检测之后解析。默认为 false。
1 HTML 中引用(更改dom)
<!-- #后面是给模板或者dom元素起的引用名字,以便可以在组件类或模板中进行引用 -->
<div class="container" #imageSlider>
<div class="image-slider">
<img *ngFor="let slider of sliders" [src]="slider.imgUrl" [alt]="slider.caption">
</div>
<div class="nav-selection">
<span *ngFor="let _ of sliders; let idx=index" class="slide-button"></span>
</div>
</div>
ts
export class ImageSliderComponent implements OnInit{
/**
*@ViewChild 是一个选择器,用来查找要引用的 dom元素或者组件
* ElementRef 是 dom 元素的一个包装类,因为 dom元素不是angular中的类,所以需要一个
* 包装类以便在 angular中使用和标识其类型
*/
// @ViewChild('container') containerRef: ElementRef;
@ViewChild('imageSlider', { static: true }) imgSlider!: ElementRef;
@Input() sliders: ImageSlider[] = [];
ngOnInit(): void {
console.log(this.imgSlider.nativeElement.innerHTML = '<span>aaa</span>')
}
}
2 angular组件中引用
效果如下
获取DOM对象(一组组件类):3.ViewChildren
用于配置视图查询的参数装饰器
用于从视图 DOM 中获取元素或指令的 QueryList。每当添加、删除或移动子元素时,此查询列表都将更新,并且其可观察对象 changes 将发出新值。
在调用 ngAfterViewInit
前设置的视图查询。
元数据属性:
-
selector - 要查询的指令类型或名称。
-
read - 用于从查询元素中读取不同的标记。
-
emitDistinctChangesOnly -仅当 QueryList 结果发生更改时, QueryList#changes observable 才会发出新值。当
false
时,即使 QueryList 没有changes
,observable 也可能会发出更改。注意:此配置选项已弃用*,它将被永久设置为true
并在未来版本的 Angular 中删除。支持以下选择器。
- 任何带有
@Component
或@Directive
装饰器的类 - 作为字符串的模板引用变量(例如,使用
@ViewChildren('cmp')
查询<my-component #cmp></my-component>
) - 在当前组件的子组件树中定义的任何提供者(例如
@ViewChildren(SomeService) someService!: SomeService
) - 通过字符串标记定义的任何提供程序(例如
@ViewChildren('someToken') someTokenVal!: any
) TemplateRef
(例如使用@ViewChildren(TemplateRef) template;
查询<ng-template></ng-template>
;)
- 任何带有
此外,多个字符串选择器可以用逗号分隔(例如 @ViewChildren('cmp1,cmp2')
)
read
支持以下值:
- 任何带有
@Component
或@Directive
装饰器的类 - 在此查询的
selector
匹配的组件注入器上定义的任何提供程序 - 通过字符串标记定义的任何提供程序(例如
{provide: 'token', useValue: 'val'}
) TemplateRef
、ElementRef
和ViewContainerRef
Renderer2
扩展此基类以实现自定义渲染器。默认情况下,Angular 会把模板渲染成 DOM。 你可以使用自定义渲染器来拦截渲染类调用,或用于渲染一些非 DOM 的东西。
说明:
使用 RendererFactory2
创建你的自定义渲染器。
使用自定义渲染器可以绕过 Angular 的模板机制,并进行无法以声明式语法表达的自定义 UI 变更。 比如,如果你要设置无法静态得知名称的 Property 或 Attribute,可以使用 setProperty()
或 setAttribute()
方法。
使用高级样式特性
1 设置视图封装
ViewEncapsulation 枚举值
Emulated | 默认值。angular 通过改写内容和样式添加属性。从而模拟影子DOM |
---|---|
ShadowDom | 使用浏览器的影子 DOM 特性 |
None | 未修改的 CSS 样式添加到HTML 文档的 head 节中。并且让浏览器设法使用正常的 CSS 优先级规则来应用样式 |
@Component({
selector: 'app-product',
templateUrl: 'template.component.html',
styles: ['div {background-color: lightgreen;}'],
encapsulation: ViewEncapsulation.Emulated,
})
2 使用影子 DOM CSS 选择器
Name | Description |
---|---|
:host | 匹配组件的宿主元素 |
:host-context(classSelector) | 特定CSS类成员的宿主元素的祖先 |
/deep/或>>> ::ng-deep |
父组件使用此类选择器来定义影响子组件模板中元素的样式。只有当 @Component 装饰器的 encapsulation 属性设置为 Emulated 时,才使用这个选择器/deep/ 或 >>> 作废;::ng-deep ,15后标记作废,但17还可使用 |