我们往往把 `C层的数据自动向V层传输的机制`叫做`数据绑定`,把`V层数据自动C层传输的机制`也叫`数据绑定`。本节中讨论`C -> V`的绑定机制。在Angular中C层向V层的数据绑定是隐性的:C层中声明的属性均被自动绑定到V层。
>[info] C层:app.component.ts;V层:app.component.html。
# 数据绑定
app.component.ts
~~~
export class AppComponent {
constructor(private httpClient: HttpClient) {
// 向8080端口的helloWorld路径发起请求
httpClient.get('http://localhost:8080/helloWorld')
.subscribe(success, error);
}
title = 'hello-world'; ➊
}
~~~
➊ C层定义了title属性,则在V层中可以直接显示该属性的值。
# 数据显示
app.component.html
```html
<h1 style="text-align: center; margin-top: 100px;">
{{title}}!➊
</h1>
<router-outlet></router-outlet>
```
➊ V层中显示绑定的数据title。
## 测试
![](https://img.kancloud.cn/f5/25/f5256e1c81571a3f6cdf2f7d0dca310c_488x238.png)
# 在V层显示后台返回数据
想将后台返回的数据绑定给V层,只需要改变C层中的`title`变量赋值就可以了。`title`的值改变会将会自动的更新显示页面中。
## 匿名函数
在处理后台返回数据以前, 对原代码进行稍许整理使其更符合通用的JS编程风格。
在JS中,我们可以在使用某个函数时,直接将函数定义在参考中,就好比:
app.component.ts
~~~
export class AppComponent {
constructor(private httpClient: HttpClient) {
// 向8080端口的helloWorld路径发起请求
httpClient.get('http://localhost:8080/helloWorld')
.subscribe(
success, ➊
error);
}
title = 'hello-world';
}
/**
* 在控制台打印传入值
* @param data 任意数据
*/
function success(data) { ➋
console.log('请求成功');
console.log(data);
}
~~~
等价于
~~~
export class AppComponent {
constructor(private httpClient: HttpClient) {
// 向8080端口的helloWorld路径发起请求
httpClient.get('http://localhost:8080/helloWorld')
.subscribe(
function success(data) { ➌
console.log('请求成功');
console.log(data);
},
error);
}
title = 'hello-world';
}
~~~
➊等于➋,所以使用➋替换➊后,就变成了➌。
以上代码还等价于
~~~
export class AppComponent {
constructor(private httpClient: HttpClient) {
// 向8080端口的helloWorld路径发起请求
httpClient.get('http://localhost:8080/helloWorld')
.subscribe((data) => { ➍
console.log('请求成功');
console.log(data);
}, error);
}
title = 'hello-world';
}
~~~
由于➌只会被用到1次,所以我们可以省略它的名字,便简化为了➍。在后续的教程中,我们会越来越多的使用这种方式。
## 我的点滴
时间回到20年前自己还刚刚上高中的时候,有一天了解到原来住校生有晚自习。带着好奇的心情上了一晚上了,教室里无数展日光灯把环境照亮,那种感觉别提有多舒服了,突然感觉到晚上能有这么明亮的教室学习是一件非常幸福的事情。从那时候起,开始了走读生的晚自习生活。晚自习会上到9点钟,然后和同学骑自行车走到3公里的国道回家,那时候车比较少,到晚上的时候一般就是拉稻草的车或一些拉货的四轮车,少许会有几辆摩托车。
有一天自习完如常的和几个同学(只记得王乐超一个了)一起回家,半路上突然发现路边摔着一辆摩托车,预感到可能有事情发生后我们下车在摩托周围开始找人,果然不远处还躺着一个人。说实话那时候吓坏了,人碰到这种事情老往最坏的地方想,我们尝试着用手轻推了几下并喊了几下,最后发现人还活着于是问了电话号码,然后分工合作:有同学守着他,而我和另外的同学则去村里找有电话号码的人家赶快去给他家人打电话求助。只听接到电话的那头两口子已经开始不淡定了,过了半小时左右他家人到了,发现原来是自己的邻居家的小孩。。而恰好的是,这个有电话的人家前些天刚刚给自己的儿子买的摩托车。
把那人送上车后,我们几个心里踏实的各回各家了。当初的那个人并不知道我们的名字,我们亦然。可能在后面的人生路上,我们还有过几面之缘,也可能至此再也没有见过,或者他后来一直就在我身边。但这些都不重要,重要的事我们在帮助了他的时候,并不需要知道他的名字。
*****
在编程的世界里把这种情景的处理叫做**匿名函数**。
*****
在现实生活中,匿名函数太普遍了。比如今天我们坐地铁来上班,我们并不知道地铁司机的姓名,但这个妨碍他来驾驶地铁这个事实;出了地换,你买早点时我没有问题老板叫什么名字。或者正在学习教程的你,可能正处于河北工业大学的教室里,此时你可能都不知道授课教师的姓名。我们的生活中,有太多太多的才肩而过,一面之缘了。
> 我不知道你来自哪里,不知道你的过去,甚至于连你的姓名我都不知道,但这些都不重要,因为你活生生的就在这里! ---- 致`匿名函数`
# 赋值
app.component.ts
~~~
constructor(private httpClient: HttpClient) {
const self = this; ➊
/* 向8080端口的helloWorld路径发起请求 */
this.httpClient.get('http://localhost:8080/helloWorld')
.subscribe(
function success(data: { message: string }) { ➋
self➌.title = data.message;
console.log(data);
},
error);
}
~~~
* ➊ 常规写法,此处的this表示为:本对象。所以此行代码解释为:self = 本App组件对象。
* ➋ 声明`data`参数接收的类型。作用有两个:其一为调用`自己`(指这个匿名方法)的方法规定了数据类型,指调用此方法向参考`data`传值时,该值中必须有`message`字段且该字段的类型必须为`string`。其二是为`自己`在方法体内部使用`data`对象时提供类型参考。
* ➌ 设置**App组件对象**的title值为data.message
## 测试
![](https://img.kancloud.cn/0d/ff/0dff3eb3baa0c3fe5b42e84be562bf5a_490x405.png)
# 主要数据流如下
![](https://img.kancloud.cn/b0/a2/b0a28781019c343826bbb992ee512d32_800x600.gif)
# 本节小测
尝试抛弃`self`而直接使用`this`,请测试看会发生什么,那么为什么会这样呢?
app.component.ts
~~~
constructor(private httpClient: HttpClient) {
const self = this;
/* 向8080端口的helloWorld路径发起请求 */
this.httpClient.get('http://localhost:8080/helloWorld')
.subscribe(
function success(data: { message: string }) {
this.title = data.message; ➊
console.log(data);
},
error);
}
~~~
➊ 在这里直接使用this
## 上节答案
实现的方案有很多种,在此给出其中的两种:
方案一:
~~~
class Yunzhi {
private input: string;
public get(input: string) {
this.input = input;
return this; ➊
}
public subscribe(successFunction, errorFunction) {
if (this.input === 'a') {
successFunction('hello success');
} else {
errorFunction('hello error');
}
}
}
~~~
➊ 在get方法中,将`this 自己`做为了返回值。自己中存在➋subscribe方法。
➋ 接收两个参数,作者心里认为这两个参数的类型是函数类型,所以在内部代码执行了 `successFunction()`,`errorFunction()`。
方案二:
~~~
class Yunzhi {
public get(input: string) {
return new Observable(input); ➊
}
}
class Observable { ➋
private readonly input: string;
constructor(input: string) {
this.input = input;
}
public subscribe(successFunction, errorFunction) { ➌
if (this.input === 'a') {
successFunction('hello success');
} else {
errorFunction('hello error');
}
}
},
~~~
➊ 在get方法中,返回了实例化`Observable`的对象➋,该对象➋存在➌subscribe方法。
> 注意: 不是返回了`Observable`,而是返回了根据`Observable`实例化的对象。`Observable`是个图纸、是个协议、是个功能需求描述,它是看不见摸不着的(抽象的)。根据此楼房图纸盖的楼房是对象,是看的见摸的着的;根据USB协议生产的USB鼠标是对象,是看的见摸的着的;根据用车需求提供的汽车是个对象,也是看的见也摸的着。
- 序言
- 第一章:Hello World
- 第一节:Angular准备工作
- 1 Node.js
- 2 npm
- 3 WebStorm
- 第二节:Hello Angular
- 第三节:Spring Boot准备工作
- 1 JDK
- 2 MAVEN
- 3 IDEA
- 第四节:Hello Spring Boot
- 1 Spring Initializr
- 2 Hello Spring Boot!
- 3 maven国内源配置
- 4 package与import
- 第五节:Hello Spring Boot + Angular
- 1 依赖注入【前】
- 2 HttpClient获取数据【前】
- 3 数据绑定【前】
- 4 回调函数【选学】
- 第二章 教师管理
- 第一节 数据库初始化
- 第二节 CRUD之R查数据
- 1 原型初始化【前】
- 2 连接数据库【后】
- 3 使用JDBC读取数据【后】
- 4 前后台对接
- 5 ng-if【前】
- 6 日期管道【前】
- 第三节 CRUD之C增数据
- 1 新建组件并映射路由【前】
- 2 模板驱动表单【前】
- 3 httpClient post请求【前】
- 4 保存数据【后】
- 5 组件间调用【前】
- 第四节 CRUD之U改数据
- 1 路由参数【前】
- 2 请求映射【后】
- 3 前后台对接【前】
- 4 更新数据【前】
- 5 更新某个教师【后】
- 6 路由器链接【前】
- 7 观察者模式【前】
- 第五节 CRUD之D删数据
- 1 绑定到用户输入事件【前】
- 2 删除某个教师【后】
- 第六节 代码重构
- 1 文件夹化【前】
- 2 优化交互体验【前】
- 3 相对与绝对地址【前】
- 第三章 班级管理
- 第一节 JPA初始化数据表
- 第二节 班级列表
- 1 新建模块【前】
- 2 初识单元测试【前】
- 3 初始化原型【前】
- 4 面向对象【前】
- 5 测试HTTP请求【前】
- 6 测试INPUT【前】
- 7 测试BUTTON【前】
- 8 @RequestParam【后】
- 9 Repository【后】
- 10 前后台对接【前】
- 第三节 新增班级
- 1 初始化【前】
- 2 响应式表单【前】
- 3 测试POST请求【前】
- 4 JPA插入数据【后】
- 5 单元测试【后】
- 6 惰性加载【前】
- 7 对接【前】
- 第四节 编辑班级
- 1 FormGroup【前】
- 2 x、[x]、{{x}}与(x)【前】
- 3 模拟路由服务【前】
- 4 测试间谍spy【前】
- 5 使用JPA更新数据【后】
- 6 分层开发【后】
- 7 前后台对接
- 8 深入imports【前】
- 9 深入exports【前】
- 第五节 选择教师组件
- 1 初始化【前】
- 2 动态数据绑定【前】
- 3 初识泛型
- 4 @Output()【前】
- 5 @Input()【前】
- 6 再识单元测试【前】
- 7 其它问题
- 第六节 删除班级
- 1 TDD【前】
- 2 TDD【后】
- 3 前后台对接
- 第四章 学生管理
- 第一节 引入Bootstrap【前】
- 第二节 NAV导航组件【前】
- 1 初始化
- 2 Bootstrap格式化
- 3 RouterLinkActive
- 第三节 footer组件【前】
- 第四节 欢迎界面【前】
- 第五节 新增学生
- 1 初始化【前】
- 2 选择班级组件【前】
- 3 复用选择组件【前】
- 4 完善功能【前】
- 5 MVC【前】
- 6 非NULL校验【后】
- 7 唯一性校验【后】
- 8 @PrePersist【后】
- 9 CM层开发【后】
- 10 集成测试
- 第六节 学生列表
- 1 分页【后】
- 2 HashMap与LinkedHashMap
- 3 初识综合查询【后】
- 4 综合查询进阶【后】
- 5 小试综合查询【后】
- 6 初始化【前】
- 7 M层【前】
- 8 单元测试与分页【前】
- 9 单选与多选【前】
- 10 集成测试
- 第七节 编辑学生
- 1 初始化【前】
- 2 嵌套组件测试【前】
- 3 功能开发【前】
- 4 JsonPath【后】
- 5 spyOn【后】
- 6 集成测试
- 7 @Input 异步传值【前】
- 8 值传递与引入传递
- 9 @PreUpdate【后】
- 10 表单验证【前】
- 第八节 删除学生
- 1 CSS选择器【前】
- 2 confirm【前】
- 3 功能开发与测试【后】
- 4 集成测试
- 5 定制提示框【前】
- 6 引入图标库【前】
- 第九节 集成测试
- 第五章 登录与注销
- 第一节:普通登录
- 1 原型【前】
- 2 功能设计【前】
- 3 功能设计【后】
- 4 应用登录组件【前】
- 5 注销【前】
- 6 保留登录状态【前】
- 第二节:你是谁
- 1 过滤器【后】
- 2 令牌机制【后】
- 3 装饰器模式【后】
- 4 拦截器【前】
- 5 RxJS操作符【前】
- 6 用户登录与注销【后】
- 7 个人中心【前】
- 8 拦截器【后】
- 9 集成测试
- 10 单例模式
- 第六章 课程管理
- 第一节 新增课程
- 1 初始化【前】
- 2 嵌套组件测试【前】
- 3 async管道【前】
- 4 优雅的测试【前】
- 5 功能开发【前】
- 6 实体监听器【后】
- 7 @ManyToMany【后】
- 8 集成测试【前】
- 9 异步验证器【前】
- 10 详解CORS【前】
- 第二节 课程列表
- 第三节 果断
- 1 初始化【前】
- 2 分页组件【前】
- 2 分页组件【前】
- 3 综合查询【前】
- 4 综合查询【后】
- 4 综合查询【后】
- 第节 班级列表
- 第节 教师列表
- 第节 编辑课程
- TODO返回机制【前】
- 4 弹出框组件【前】
- 5 多路由出口【前】
- 第节 删除课程
- 第七章 权限管理
- 第一节 AOP
- 总结
- 开发规范
- 备用