Angular2 作为MVVM 模型-视图-视图模型框架,组件扮演着控制器或者视图模型的角色,模板则扮演视图的角色,熟练灵活的使用模板语法,可以最快速便捷的构建出复杂应用
本篇文章作为 Angular 模板语法总结的深入篇,涵盖了最通用的模板范例和原理,可以灵活的根据产品需求,搭建简洁有效的自定义模板
在 Angular 模版中,<script>
标签被禁用了,以防止脚本注入攻击的风险
通过自定义的组件和指令,可以扩展或者创建 html 标签,实现需求的视图功能
插值表达式
<h1>{{title}}</h1>
双花括号+模板表达式 构成了插值表达式的语法,可以把表达式的值赋予标签的属性或文本
<img src="{{imageSrc}}" />
模板表达式
模板表达式最终会计算出一个值,在插值表达式中使用的也是这个值,类似于 JavaScript 中的表达式,但是模版表达式禁用一些运算符和关键字
赋值 =,+=,-= …
new 运算符
;或,
++ \ – 自增、自减
模板表达式的上下文就是引用这个模版的组件实例,不能引用 window、document,不能调用 console.log、Math.max
最佳实践
模版表达式除了目标属性外,不因该改变应用的其它状态
不要执行复杂的运算,因为模版表达式执行频繁
不要耦合业务逻辑,简单易理解的绑定
幂等性,在前置条件不变的条件下,最终结果唯一可确定
模版语句
用来响应绑定目标触发的事件
<button (click)="onSave($event)">Save</button>
语句上下文同样为组件实例
$event 代表触发事件的消息,对应 JS 中 event 对象
最佳实践
避免复杂的模版语句,通常止于组件实例方法调用或者属性赋值
绑定语法
数据绑定帮助我们简化从 DOM 元素中获取值和赋值的,通过使用 angular 定义的绑定语法,可以轻松实现 model 和 view 之间的映射关系,在模版中管理我们的数据流向
模版最为数据的消费者和生产者,可以帮助我们梳理和理解数据实际的含义,在模版中绑定属性和方法完全不必理会背后的处理和逻辑
数据方向 | 语法 | 场景 |
---|---|---|
单向(组件 -> 模版) | {{expression}}, [target]=”expression”, bind-target=”expression” | 插值表达式 属性 attr 类 class 样式 style |
单向(模版 -> 组件) | (target)=”statement” on-target=”statement” | 事件 |
双向 | [(target)]=”expression” bindon-target=”expression” | 双向 |
深入理解 HTML 和 DOM 关系
html 是一种标记语言,当我们使用 html 这种语言保存的 .html 文件在浏览器中打开时,浏览器根据我们在 html 中的描述,在浏览器中构建 DOM 树
总结:html 是一种描述,DOM 是 html 定义的,由浏览器生成的对象
所以,在 html 中定义的 attribute 属性会映射到浏览器生成的 DOM 对象的 property 上,但这种映射是单向的,只有在第一次渲染的时候,写在 html 标签中的 attribute 才会影响到 property,之后我们所有的操作都是基于 DOM 的 property 来实现的
<input type="text" value="Bob" />
此时,DOM value property = HTML value attribute
当用户在这个输入框中输入,”Sally” 时
DOM value property == “Sally”
HTML value attribute == “Bob”
所以,模板绑定是通过 property 和事件来生效的,attribute 只负责初始化,生成了 DOM 后,HTML 就可以靠边站了
绑定目标
数据绑定的目标可以是 (元素|组件|指令) 的 (property|事件)(绑定等式左侧的为目标名)
绑定场景 | 目标 | 例子 |
---|---|---|
property | 元素|指令|组件 属性 | <img [src]="imageSrc" /> <hero-detail [hero]="currentHero"></hero-detail> <div [ngClass]="{selected: isSelected}"></div> |
事件 | 元素|组件|指令 事件 | <button (click)="onClick($event)">Save</button> <hero-detail [hero]="currentHero"></hero-detail> <div (myClick)="clicked=$event">click me</div> |
双向 | 事件与property | <input [(ngModel)]="heroName" /> |
class | class property | <div [class.special]="isSpecial"></div> |
style | style property | <div [style.color]="isSpecial? 'red': 'green'"></div> |
property 绑定的注意事项
属性绑定的数据是单向传输的,属性绑定无法从目标元素获取值
元素属性的绑定最常见,但 angular 会优先匹配某个已知的指令的属性名(@Input)
<div [ngClass]="classes">[ngClass] binding to the classes property</div>
这里 ngClass 作为某个指令的属性来绑定
指令属性冲突会发生什么?
两个指令都会获取到绑定到值,而且都会被执行
bind-propertyName 规范形式
保证单一功能原则,消除副作用
如果没有加[],表达式会被当作字符串常量赋值给属性
基于可读性考虑,推荐使用插值表达式来绑定属性(习惯问题,统一就好)
attribute 绑定
<tr><td colspan="{{1+1}}"></td></tr>
// 模版解析错误:colspan 不是已知的原生属性
解决方法:[attr.colspan]=”1+1” 特殊的语法支持
CLASS 绑定
可以添加和移除元素的 class 类名
<div [class]="classStr"></div>
<div [class.special]="isSpecial"></div>
// boolean
不如 ngClass 好用
STYLE 绑定
<div [style.color]="isSpecial?'red':'green'"></div>
不如 ngStyle 好用
事件绑定
响应用户和浏览器的动作,从元素到组件的数据传递
<button on-click="onClick($event)"></button>
同样,事件绑定也会优先匹配指令中输出的事件属性
- $event 作为事件对象传递给事件的处理方法
$event 这里就是 DOM 原生的点击事件对象
EventEmitter 自定义事件
deleteRequest = new EventEmitter<Hero>();
delete() {
this.deleteRequest.emit(this.hero);
}
这里定义了一个 EventEmitter 实例,即一个事件实例,当 delete() 指令时,触发了这个自定义的 deleteRequest 事件,同时
<hero-detail (deleteRequest)="deleteHero($event)"></hero-detail>
父级元素绑定了这个自定义事件,感知到事件触发并获取到了 this.hero
作为 $event
,传递到自己的处理器中,完成了删除的动作
双向数据绑定
[(x)] 实际上是属性绑定和事件绑定的语法糖,使用的前提是绑定的目标同时具有 x 属性和 xChange 方法(名字必须一致)
<my-sizer [(size)]="fontSizePx"></my-sizer>
<div [style.font-size.px]="fontSizePx"></div>
等同于
<my-sizer [size]="fontSizePx" (sizeChange)="fontSizePx = $event"></my-sizer>
自定义事件参考上文
在这里,my-sizer 组件可以理解为一个黑箱,我们将 fontSizePx 绑定到这个黑箱的双向绑定接口上,在初始化的之后还可以不断获取到黑箱的输出,同时更新页面
表单的双向绑定
// 依赖 import { FormsModule } from '@angular/forms';
---
<input [(ngModel)]="firstName" />
// 等同于如下
<input [value]="firstName"
(input)="firstName = $event.target.value" />
// 添加点佐料
<input [ngModel]="firstName"
(ngModelChange)="setUpperCase(firstName)" />
**
前行的路上,感谢您的鼓励!!
**