博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Angular快速学习笔记(3) -- 组件与模板
阅读量:5100 次
发布时间:2019-06-13

本文共 12356 字,大约阅读时间需要 41 分钟。

1. 显示数据

在 Angular 中最典型的数据显示方式,就是把 HTML 模板中的控件绑定到 Angular 组件的属性。

使用插值表达式显示组件属性

要显示组件的属性,最简单的方式就是通过插值表达式 (interpolation) 来绑定属性名。 要使用插值表达式,就把属性名包裹在双花括号里放进视图模板,如 {

{myHero}}。

import { Component } from '@angular/core'; @Component({  selector: 'app-root',  template: `    

{
{title}}

My favorite hero is: {
{myHero}}

`})export class AppComponent { title = 'Tour of Heroes'; myHero = 'Windstorm';}

注意

  • 这里直接使用的 template,而不是templateUrl,template可以直接书写html代码,简单的html推荐这种方式
  • @Component 装饰器中指定的 CSS 选择器 selector,它指定了一个叫 my-app 的元素。 该元素是 index.html 的 body 里的占位符。
#src/index.html (body)  

内联 (inline) 模板还是模板文件?

angular提供两种地方存放组件模板

  • 你可以使用 template 属性把它定义为内联的,
  • 或者把模板定义在一个独立的 HTML 文件中, 再通过 @Component 装饰器中的 templateUrl 属性, 在组件元数据中把它链接到组件

到底选择内联 HTML 还是独立 HTML 取决于个人喜好、具体状况和组织级策略。 上面的应用选择内联 HTML ,是因为模板很小,而且没有额外的 HTML 文件显得这个演示简单些。

为数据创建一个类

使用angular提供的cli:

ng generate class hero

修改src/app/hero.ts

export class Hero {  constructor(    public id: number,    public name: string) { }}

使用这个类:

src/app/app.component.ts (heroes):

heroes = [  new Hero(1, 'Windstorm'),  new Hero(13, 'Bombasto'),  new Hero(15, 'Magneta'),  new Hero(20, 'Tornado')];myHero = this.heroes[0];template: `  

{
{title}}

My favorite hero is: {
{myHero.name}}

Heroes:

  • {
    { hero.name }}
`

在模板里可以自己使用.语法,访问对象属性

使用 ngFor 显示数组属性

*ngFor 是 Angular 的“迭代”指令。 它将 <li> 元素及其子级标记为“迭代模板”.,同vue.js里的v-for

export class AppComponent {  title = 'Tour of Heroes';  heroes = ['Windstorm', 'Bombasto', 'Magneta', 'Tornado'];  myHero = this.heroes[0];}template: `  

{
{title}}

My favorite hero is: {
{myHero}}

Heroes:

  • {
    { hero }}
·

不要忘记 *ngFor 中的前导星号 (*)。它是语法中不可或缺的一部分

通过 NgIf 进行条件显示

有时,应用需要只在特定情况下显示视图或视图的一部分,这个时候使用ngif,同vue.js里的v-if

There are many heroes!

小结

  • 带有双花括号的插值表达式 (interpolation) 来显示一个组件属性
  • 用 ngFor 显示数组
  • 用一个 TypeScript 类来为你的组件描述模型数据并显示模型的属性
  • 用 ngIf 根据一个布尔表达式有条件地显示一段 HTML。

2. 模板语法

Angular 应用管理着用户之所见和所为,并通过 Component 类的实例(组件)和面向用户的模板来与用户交互。

在 Angular 中,组件扮演着控制器视图模型的角色,模板则扮演视图的角色。

### 模板中的 HTML

HTML 是 Angular 模板的语言。几乎所有的 HTML 语法都是有效的模板语法。 但值得注意的例外是 <script>元素,它被禁用了,以阻止脚本注入攻击的风险。(实际上,<script> 只是被忽略了。)

### 插值表达式 ( {

{...}})

插值表达式{

{...}}可以把计算后的字符串插入到 HTML 元素标签内的文本或对标签的属性进行赋值。

{
{title}}

一般来说,括号间的素材是一个模板表达式,Angular 先对它求值,再把它转换成字符串。 下列插值表达式通过把括号中的两个数字相加说明了这一点:

The sum of 1 + 1 is {

{1 + 1}}

表达式还可以调用宿主组件的方法,就像下面用的 getVal():

The sum of 1 + 1 is not {

{1 + 1 + getVal()}}

模板表达式

模板表达式产生一个值。 Angular 执行这个表达式,并把它赋值给绑定目标的属性,这个绑定目标可能是 HTML 元素、组件或指令。

典型的表达式上下文就是这个组件实例,它是各种绑定值的来源。 在下面的代码片段中,双花括号中的 title 和引号中的 isUnchanged 所引用的都是 AppComponent 中的属性。

{
{title}}changed

表达式中的上下文变量是由模板变量、指令的上下文变量(如果有)和组件的成员叠加而成的。 如果你要引用的变量名存在于一个以上的命名空间中,那么,模板变量是最优先的,其次是指令的上下文变量,最后是组件的成员。

{
{hero.name}}
{
{heroInput.value}}

模板语句

模板语句用来响应由绑定目标(如 HTML 元素、组件或指令)触发的事件。 它出现在 = 号右侧的引号中,就像这样:(event)="statement"。

和模板表达式一样,模板语句使用的语言也像 JavaScript。 模板语句解析器和模板表达式解析器有所不同,特别之处在于它支持基本赋值 (=) 和表达式链 (; 和 ,)

某些 JavaScript 语法仍然是不允许的:

  • new 运算符
  • 自增和自减运算符:++ 和 --
  • 操作并赋值,例如 += 和 -=
  • 位操作符 | 和 &
  • 模板表达式运算符

和表达式中一样,语句只能引用语句上下文中 —— 通常是正在绑定事件的那个组件实例。

典型的语句上下文就是当前组件的实例。 (click)="deleteHero()" 中的 deleteHero 就是这个数据绑定组件上的一个方法。

模板语句不能引用全局命名空间的任何东西。比如不能引用 window 或 document,也不能调用 console.log 或 Math.max。

绑定语法

数据绑定是一种机制,用来协调用户所见和应用数据。绑定的类型可以根据数据流的方向分成三类: 从数据源到视图、从视图到数据源以及双向的从视图到数据源再到视图。

数据方向 语法 绑定类型
单向从数据源到视图 {
{expression}} [target]="expression" bind-target="expression"
插值表达式\属性\Attribute\CSS 类\样式
从视图到数据源的单向绑定 (target)="statement"、on-target="statement" 事件
双向 [(target)]="expression" 、bindon-target="expression" 双向

新的思维模型

数据绑定的威力和允许用自定义标记扩展 HTML 词汇的能力,会让你把模板 HTML 当成 HTML+。

在正常的 HTML 开发过程中,你使用 HTML 元素来创建视觉结构, 通过把字符串常量设置到元素的 attribute 来修改那些元素。

Mental Model

在 Angular 模板中,你仍使用同样的方式创建结构和初始化 attribute 值。然后,用封装了 HTML 的组件创建新元素,并把它们当作原生 HTML 元素在模板中使用。

Mental Model

改变定式思维;

直觉告诉你,你正在绑定按钮的 disabled attribute。 并把它设置为组件的 isUnchanged 属性的当前值,但你的直觉是错的!isUnchanged为true时,button增加disabled属性。

模板绑定是通过 property 和事件来工作的,而不是 attribute.

数据绑定的目标是 DOM 中的某些东西。 这个目标可能是(元素 | 组件 | 指令的)property、(元素 | 组件 | 指令的)事件,或(极少数情况下) attribute 名。

属性绑定

注意需要加[]方括号。

下面的代码是等价的:

is the interpolated image.

is the property bound image.

在多数情况下,插值表达式是更方便的备选项。 实际上,在渲染视图之前,Angular 把这些插值表达式翻译成相应的属性绑定。

事件绑定

可以通过 Angular 事件绑定来声明对哪些用户动作感兴趣

圆括号中的名称 —— 比如 (click) —— 标记出目标事件。在下面例子中,目标是按钮的 click 事件。

click me

也可以使用on-前缀方式:

$event 对象

在事件绑定中,Angular 会为目标事件设置事件处理器。绑定会通过名叫 $event 的事件对象传递关于此事件的信息。

使用 EventEmitter 实现自定义事件

通常,指令使用 Angular EventEmitter 来触发自定义事件。 指令创建一个 EventEmitter 实例,并且把它作为属性暴露出来。 指令调用 EventEmitter.emit(payload) 来触发事件,可以传入任何东西作为消息载荷。 父指令通过绑定到这个属性来监听事件,并通过 $event 对象来访问载荷。

template: `
{
{prefix}} {
{hero?.name}}
`deleteRequest = new EventEmitter
();delete() { this.deleteRequest.emit(this.hero);}

使用自定义事件:

双向绑定

对于需要显示数据属性,并在用户作出更改时更新该属性如何处理呢?

在元素层面上,既要设置元素属性,又要监听元素事件变化。Angular 为此提供一种特殊的双向数据绑定语法:[(x)][(x)] 语法结合了属性绑定的方括号[x] 和事件绑定的圆括号(x)

Attribute

attribute 绑定的语法与属性绑定类似。 但方括号中的部分不是元素的属性名,而是由attr前缀,一个点 (.) 和 attribute 的名字组成。

CSS 类

借助 CSS 类绑定,可以从元素的 class attribute 上添加和移除 CSS 类名。

Special
样式

样式绑定的语法与属性绑定类似。 但方括号中的部分不是元素的属性名,而由style前缀,一个点 (.)和 CSS 样式的属性名组成。 形如:[style.style-property]。

模板引用变量 ( #var )

模板引用变量通常用来引用模板中的某个 DOM 元素,它还可以引用 Angular 组件或指令或Web Component。

使用井号 (#) 来声明引用变量。#phone 的意思就是声明一个名叫 phone 的变量来引用<input> 元素。

输入和输出属性

输入属性是一个带有 @Input装饰器的可设置属性。当它通过属性绑定的形式被绑定时,值会“流入”这个属性。

输出属性是一个带有 @Output 装饰器的可观察对象型的属性。 这个属性几乎总是返回 Angular 的EventEmitter。 当它通过事件绑定的形式被绑定时,值会“流出”这个属性。

你只能通过它的输入和输出属性将其绑定到其它组件。

模板表达式操作符

模板表达式语言使用了 JavaScript 语法的子集,并补充了几个用于特定场景的特殊操作符。 下面介绍其中的两个:管道和安全导航操作符

管道操作符 ( | )

在绑定之前,表达式的结果可能需要一些转换。例如,可能希望把数字显示成金额、强制文本变成大写,或者过滤列表以及进行排序。

Angular 管道对像这样的小型转换来说是个明智的选择。 管道是一个简单的函数,它接受一个输入值,并返回转换结果。 它们很容易用于模板表达式中,只要使用管道操作符 (|) 就行了。

Title through uppercase pipe: {
{title | uppercase}}

安全导航操作符 ( ?. ) 和空属性路径

Angular 的安全导航操作符 (?.) 是一种流畅而便利的方式,用来保护出现在属性路径中 null 和 undefined 值。 下例中,当 currentHero 为空时,保护视图渲染器,让它免于失败。

The current hero's name is {
{currentHero?.name}}

当绑定中 title 属性为空,仍然会继续渲染

非空断言操作符(!)

在 TypeScript 2.0 中,你可以使用 --strictNullChecks 标志强制开启严格空值检查。TypeScript 就会确保不存在意料之外的 null 或 undefined。

在这种模式下,有类型的变量默认是不允许 null 或 undefined 值的,如果有未赋值的变量,或者试图把 null 或 undefined 赋值给不允许为空的变量,类型检查器就会抛出一个错误

Angular 模板中的**非空断言操作符(!)也是同样的用途。

The hero's name is {
{hero!.name}}

与安全导航操作符不同的是,非空断言操作符不会防止出现 null 或 undefined。 它只是告诉 TypeScript 的类型检查器对特定的属性表达式,不做 "严格空值检测"。

3. angular 声明周期钩子

每个组件都有一个被 Angular 管理的生命周期,Angular 提供了生命周期钩子,把这些关键生命时刻暴露出来,你可以做一些自定义操作。

钩子 用途及时机
ngOnChanges() 当 Angular(重新)设置数据绑定输入属性时响应。 该方法接受当前和上一属性值的 [SimpleChanges](https://angular.cn/api/core/SimpleChanges) 对象当被绑定的输入属性的值发生变化时调用,首次调用一定会发生在 ngOnInit() 之前。
ngOnInit() 在 Angular 第一次显示数据绑定和设置指令/组件的输入属性之后,初始化指令/组件。Initialize the directive/component after Angular first displays the data-bound properties and sets the directive/component's input properties.在第一轮 ngOnChanges() 完成之后调用,只调用一次
ngDoCheck() 检测,并在发生 Angular 无法或不愿意自己检测的变化时作出反应。在每个 Angular 变更检测周期中调用,ngOnChanges() 和 ngOnInit() 之后。
ngAfterContentInit() 当把内容投影进组件之后调用。第一次 ngDoCheck() 之后调用,只调用一次。
ngAfterContentChecked() 每次完成被投影组件内容的变更检测之后调用。ngAfterContentInit() 和每次 ngDoCheck() 之后调用
ngAfterViewInit() 初始化完组件视图及其子视图之后调用。第一次 ngAfterContentChecked() 之后调用,只调用一次。
ngAfterViewChecked() 每次做完组件视图和子视图的变更检测之后调用。ngAfterViewInit() 和每次 ngAfterContentChecked() 之后调用。
ngOnDestroy() 当 Angular 每次销毁指令/组件之前调用并清扫。 在这儿反订阅可观察对象和分离事件处理器,以防内存泄漏。在 Angular 销毁指令/组件之前调用

OnInit钩子

使用 ngOnInit() 有两个原因:

  • 在构造函数之后马上执行复杂的初始化逻辑
  • 在 Angular 设置完输入属性之后,对该组件进行准备。

OnDestroy()钩子

一些清理逻辑必须在 Angular 销毁指令之前运行,把它们放在 ngOnDestroy() 中。

OnChanges() 钩子

一旦检测到该组件(或指令)的输入属性发生了变化,Angular 就会调用它的 ngOnChanges() 方法

ngOnChanges(changes: SimpleChanges) {  for (let propName in changes) {    let chng = changes[propName];    let cur  = JSON.stringify(chng.currentValue);    let prev = JSON.stringify(chng.previousValue);    this.changeLog.push(`${propName}: currentValue = ${cur}, previousValue = ${prev}`);  }}

4.组件之间的交互

通过输入型绑定把数据从父组件传到子组件

HeroChildComponent 有两个输入型属性,它们通常带@Input 装饰器。

import { Component, Input } from '@angular/core'; import { Hero } from './hero'; @Component({  selector: 'app-hero-child',  template: `    

{
{hero.name}} says:

I, {

{hero.name}}, am at your service, {
{masterName}}.

`})export class HeroChildComponent { @Input() hero: Hero; @Input('master') masterName: string;}

父组件 HeroParentComponent。

import { Component } from '@angular/core'; import { HEROES } from './hero'; @Component({  selector: 'app-hero-parent',  template: `    

{
{master}} controls {
{heroes.length}} heroes

`})export class HeroParentComponent { heroes = HEROES; master = 'Master';}

通过 setter 截听输入属性值的变化

如果要对父组件赋值做修改,可以使用setter

import { Component, Input } from '@angular/core'; @Component({  selector: 'app-name-child',  template: '

"{
{name}}"

'})export class NameChildComponent { private _name = ''; @Input() set name(name: string) { this._name = (name && name.trim()) || '
'; } get name(): string { return this._name; }}

通过ngOnChanges()来截听输入属性值的变化

使用 OnChanges 生命周期钩子接口的 ngOnChanges() 方法来监测输入属性值的变化并做出回应

下面的 VersionChildComponent 会监测输入属性 major 和 minor 的变化,并把这些变化编写成日志以报告这些变化

import { Component, Input, OnChanges, SimpleChange } from '@angular/core'; @Component({  selector: 'app-version-child',  template: `    

Version {
{major}}.{
{minor}}

Change log:

  • {
    {change}}
`})export class VersionChildComponent implements OnChanges { @Input() major: number; @Input() minor: number; changeLog: string[] = []; ngOnChanges(changes: {[propKey: string]: SimpleChange}) { let log: string[] = []; for (let propName in changes) { let changedProp = changes[propName]; let to = JSON.stringify(changedProp.currentValue); if (changedProp.isFirstChange()) { log.push(`Initial value of ${propName} set to ${to}`); } else { let from = JSON.stringify(changedProp.previousValue); log.push(`${propName} changed from ${from} to ${to}`); } } this.changeLog.push(log.join(', ')); }}

父组件监听子组件的事件

子组件暴露一个 EventEmitter 属性,当事件发生时,子组件利用该属性 emits(向上弹射)事件。

子组件的 EventEmitter 属性是一个输出属性,通常带有@Output 装饰器

import { Component, EventEmitter, Input, Output } from '@angular/core'; @Component({  selector: 'app-voter',  template: `    

{
{name}}

`})export class VoterComponent { @Input() name: string; @Output() onVoted = new EventEmitter
(); voted = false; vote(agreed: boolean) { this.onVoted.emit(agreed); this.voted = true; }}

父组件,可以绑定onVoted事件,框架(Angular)把事件参数(用 $event 表示)传给事件处理方法:

import { Component }      from '@angular/core'; @Component({  selector: 'app-vote-taker',  template: `    

Should mankind colonize the Universe?

Agree: {
{agreed}}, Disagree: {
{disagreed}}

`})export class VoteTakerComponent { agreed = 0; disagreed = 0; voters = ['Mr. IQ', 'Ms. Universe', 'Bombasto']; onVoted(agreed: boolean) { agreed ? this.agreed++ : this.disagreed++; }}

父组件和子组件通过服务来通讯

在之前讲服务时就提过,同一个module下的组件间,可以通过服务进行通讯。父组件和它的子组件共享同一个服务,利用该服务在家庭内部实现双向通讯。

5.组件样式

Angular 应用使用标准的 CSS 来设置样式。这意味着你可以把关于 CSS 的那些知识和技能直接用于 Angular 程序中,例如:样式表、选择器、规则以及媒体查询等。

有几种方式把样式加入组件:

  • 设置 styles 或 styleUrls 元数据
  • 内联在模板的 HTML 中
  • 通过 CSS 文件导入

预编译css

如果使用 CLI 进行构建,那么你可以用 sass、less 或 stylus 来编写样式,并使用相应的扩展名(.scss、.less、.styl)把它们指定到 @Component.styleUrls 元数据中。例子如下:

@Component({  selector: 'app-root',  templateUrl: './app.component.html',  styleUrls: ['./app.component.scss']})

6.属性指令

在 Angular 中有三种类型的指令:

  1. 组件 — 拥有模板的指令
  2. 结构型指令 — 通过添加和移除 DOM 元素改变 DOM 布局的指令
  3. 属性型指令 — 改变元素、组件或其它指令的外观和行为的指令。

创建属性指令:

ng generate directive highlight

编写代码,这个指令实现高亮元素:

import { Directive, ElementRef } from '@angular/core';@Directive({  selector: '[appHighlight]'})export class HighlightDirective {    constructor(el: ElementRef) {       el.nativeElement.style.backgroundColor = 'yellow';    }}

通过ElementRef访问目标元素。

使用属性型指令

Highlight me!

使用 @Input 数据绑定向指令传递值

@Input() highlightColor: string;

Highlighted in yellow

Highlighted in orange

绑定到 @Input 别名

可以使用Input的别名

@Input('appHighlight') highlightColor: string;

Highlight me!


作者:Jadepeng

出处:jqpeng的技术记事本--
您的支持是对博主最大的鼓励,感谢您的认真阅读。
本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

转载于:https://www.cnblogs.com/xiaoqi/p/angular-quick-study-part3.html

你可能感兴趣的文章
设计模式(十一)外观模式(Facade Pattern)
查看>>
JS里charCodeAt()和fromCharCode()方法拓展应用:加密与解密
查看>>
查看SQLServer的QUOTED_IDENTIFIER等配置
查看>>
[转]构建基于WCF Restful Service的服务
查看>>
数据访问
查看>>
php基础知识测试总结
查看>>
Apache Cordova
查看>>
java随笔一(关于定时任务)
查看>>
Codeforces 975D Ghosts 【math】
查看>>
Oracle expdp/impdp导出导入命令及数据库备份
查看>>
HDU 1074 Doing Homework (dp+状态压缩)
查看>>
JavaScript经典代码【二】【javascript判断用户点了鼠标左键还是右键】
查看>>
Cocos2dx使用wxsqlite开源加密SQLite3数据库
查看>>
linux —— shell 编程(编程语法)
查看>>
2011计算机二级c语言考点:二维数组
查看>>
T-SQL:毕业生出门需知系列(六)
查看>>
[C#] 获取计算机内部信息 - ComputerInfoHelper
查看>>
概率算法_二项分布和泊松分布
查看>>
crontab命令使用文档.txt
查看>>
Luogu2986 [USACO10MAR]伟大的奶牛聚集 (树形DP)
查看>>