子组件通过Output给父组件传值
1、news.component.html,
<app-footer (outter)="run($event)"></app-footer> <br> <hr> <br> <h2>我是news组件</h2> 复制代码
2、news.component.ts
import {Component, OnInit} from '@angular/core'; @Component({ selector: 'app-news', templateUrl: './news.component.html', styleUrls: ['./news.component.css'] }) export class NewsComponent implements OnInit { constructor() { } ngOnInit(): void { } run(e) { console.log(e); alert('我是news组件的方法'); } } 复制代码
3、footer.component.html
<h2>我是footer组件</h2> <br> <button (click)="sendMsg()">子组件广播事务</button> 复制代码
4、footer.component.ts
import {Component, OnInit, Output, EventEmitter} from '@angular/core'; @Component({ selector: 'app-footer', templateUrl: './footer.component.html', styleUrls: ['./footer.component.css'] }) export class FooterComponent implements OnInit { @Output() private outter = new EventEmitter(); constructor() { } ngOnInit(): void { } sendMsg() { this.outter.emit('我是footer组件的数据'); } } 复制代码
5、网页运行效果如下:
6、小结
子组件给父组件传值,即父组件接收子组件的属性,大致分为以下几步:
- 子组件引入 Output、EventEmitter 模块;
- 子组件将要传输的数据封装在 outter 中;
- 父组件页面嵌套子组件页面,子组件声明时需要将 outter 指向父组件中的某个方法,注意接收数据;
父子组件相互通信
子组件 sizer.component.ts:
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; @Component({ selector: 'app-sizer', templateUrl: './sizer.component.html', styleUrls: ['./sizer.component.css'] }) export class SizerComponent implements OnInit { @Input() size: number | string; @Output() sizeChange = new EventEmitter<number>(); constructor() { } ngOnInit(): void { } inc() { this.resize(+1); } dec() { this.resize(-1); } resize(delta: number) { this.size = Math.min(40, Math.max(8, +this.size + delta)); this.sizeChange.emit(this.size); } } 复制代码
sizer.component.html:
<h2>子组件</h2> <div> <button (click)="dec()" title="smaller">-</button> <button (click)="inc()" title="bigger">+</button> <br> <br> <label [style.font-size.px]="size">FontSize: {{size}}px</label> </div> 复制代码
size
的初始值来自属性绑定的输入值。单击按钮可在最小值/最大值范围内增大或减小 size
,然后带上调整后的大小发出 sizeChange
事件。
Home2Component.fontSize
首先传递给 SizerComponent
, $event
变量包含了 SizerComponent.sizeChange
事件的荷载。 当用户点击按钮时,Angular 将 $event
赋值给 Home2Component.fontSizePx
。 父组件 home2.component.html:
<app-sizer [size]="fontSizePx" (sizeChange)="fontSizePx=$event"></app-sizer> <h2>父组件</h2> <div [style.font-size.px]="fontSizePx">Resizable Text</div> 复制代码
Home2Component.fontSizePx
建立初始 SizerComponent.size
值。
home2.component.ts:
fontSizePx = 16; 复制代码
页面测试:
上述父子组件之间通信的关键在于:
<app-sizer [size]="fontSizePx" (sizeChange)="fontSizePx=$event"></app-sizer> 复制代码
此处还可以通过双向绑定来实现, 双向绑定语法实际上是属性绑定和事件绑定的语法糖,所以还可以改为:
<app-sizer [(size)]="fontSizePx"></app-sizer> 复制代码
Home2Component.fontSize
被双向绑定到 SizerComponent
Rxjs异步数据流编程
介绍
RxJS 是 ReactiveX 编程理念的 JavaScript 版本。ReactiveX 来自微软,它是一种针对异步数据流的编程。
简单来说,它将一切数据,包括 HTTP 请求,DOM 事件或者普通数据等包装成流
的形式,然后用强大丰富的操作符对流进行处理,使你能以同步编程的方式处理异步数据,并组合不同的操作符来轻松优雅的实现你所需要的功能。
RxJS 是一种针对异步数据流编程工具,或者叫响应式扩展编程;可不管如何解释 RxJS 其目标就是异步编程,Angular 引入 RxJS 为了就是让异步可控、更简单。
RxJS 里面提供了很多模块。这里我们主要给大家讲 RxJS 里面最常用的 Observable 和
fromEvent。
参考手册:https://www.npmjs.com/package/rxjs
关于异步编程方法的讲解,可以参考 Javascript异步编程的4种方法。
目前常见的异步编程的几种方法:
- 回调函数
- 事件监听/发布订阅
- Promise
- Rxjs
本次测试需要新建一个 home2 组件,和 storage 服务,异步编程方法封装在 storage 服务中,供组件调用。
首先我们定义一个同步方法,来测试其效果。
1、storage.service.ts
getData() { // console.log('this is service data'); return 'this is service data'; } 复制代码
2、home2.component.ts
import {StorageService} from '../../services/storage.service'; constructor(public storage: StorageService) { } /*//1、同步方法*/ getData() { console.log(this.storage.getData()); } 复制代码
3、页面测试
从结果可以看出,每点击一下按钮,同步方法就会立刻获取到数据。但是在实际生产中,同步操作容易造成堵塞问题,异步操作的出现很好的解决该问题。
回调函数
这是异步编程最基本的方法。
1、storage.service.ts
// 一秒后获取数据 getCallbackData(cb) { setTimeout(() => { // tslint:disable-next-line:prefer-const let username = 'hresh'; cb(username); }, 1000); } 复制代码
2、home2.component.ts
import {StorageService} from '../../services/storage.service'; constructor(public storage: StorageService) { } getCallbackData() { /*2、通过Callback获取异步数据*/ this.storage.getCallbackData((data) => { console.log(data); });//a操作 console.log('延时获取数据');//b操作 } 复制代码
3、页面测试
从页面结果来看,a操作不会阻塞程序运行,会被推迟执行,不会影响b操作的执行。
回调函数的优点是简单、容易理解和部署,缺点是不利于代码的阅读和维护,各个部分之间高度耦合(Coupling),流程会很混乱,而且每个任务只能指定一个回调函数。
事件监听
另一种思路是采用事件驱动模式。任务的执行不取决于代码的顺序,而取决于某个事件是否发生。
演示代码如下:
import { Component, OnInit } from '@angular/core'; import * as $ from 'jquery'; @Component({ selector: 'app-home2', templateUrl: ' <h2>事件监听</h2> <input type="button" value="点击" id="btn"> <input type="button" value="移除" id="del"> ' }) export class Home2Component implements OnInit { fn1() { alert('first'); } fn1() { alert('first'); } fn2() { alert('second'); } ngOnInit(): void { $('h2').css('color', 'blue'); let $body = $('body'); $body.delegate('#btn', 'click', this.fn1); $body.delegate('#btn', 'click', this.fn2); $('#del').on('done', () => alert('解除事件绑定')); setTimeout(() => { $('#del').trigger('done'); }, 2000); } } 复制代码
页面测试效果:
#btn
标签可以绑定多个事件,$('#del').trigger('done')
表示页面初始化后,2s 之后会触发 done 事件,从而执行 alert 事件。
这种方法的优点是比较容易理解,可以绑定多个事件,每个事件可以指定多个回调函数,而且可以"去耦合"(Decoupling),有利于实现模块化。缺点是整个程序都要变成事件驱动型,运行流程会变得很不清晰。
Promise
Promise 本意是承诺,在程序中的意思就是承诺我过一段时间后会给你一个结果。 什么时候会用到过一段时间?答案是异步操作,异步是指可能比较长时间才有结果的才做,例如网络请求、读取本地文件等。
它的思想是,每一个异步任务返回一个 Promise 对象,该对象有一个 then 方法,允许指定回调函数。比如,f1的回调函数f2,可以写成:
f1().then(f2); 复制代码
在 Angular 项目中可以使用如下测试案例:
1、storage.service.ts
getPromiseData() { return new Promise((resolve, reject) => { setTimeout(() => { // tslint:disable-next-line:prefer-const let username = 'hresh----Promise'; resolve(username); }, 2000); }); } 复制代码
2、home2.component.ts
import {StorageService} from '../../services/storage.service'; constructor(public storage: StorageService) { } getPromiseData(){ /*3、通过Promise获取异步数据*/ let promiseObj = this.storage.getPromiseData(); promiseObj.then((data) => { console.log(data); }); } 复制代码
3、页面测试
另外 Promise 的 then 方法返回的是一个新的 Promise 实例,因此 then 可用链式调用。比如,指定多个回调函数:
f1().then(f2).then(f3); 复制代码
再比如,指定发生错误时的回调函数:
f1().then(f2).fail(f3); 复制代码
而且,它还有一个前面三种方法都没有的好处:如果一个任务已经完成,再添加回调函数,该回调函数会立即执行。所以,你不用担心是否错过了某个事件或信号。这种方法的缺点就是编写和理解,都相对比较难。
Rxjs
Rxjs 处理异步:
1、storage.service.ts
import { Observable } from 'rxjs'; getRxjsData() { return new Observable((observer) => { setTimeout(() => { let uname = 'hresh----Rxjs'; observer.next(uname); }, 3000); }); } 复制代码
2、home2.component.ts
import {StorageService} from '../../services/storage.service'; constructor(public storage: StorageService) { } getRxjsData(){ /*4、通过Rxjs获取异步数据*/ let observer = this.storage.getRxjsData(); let oData = observer.subscribe((data) => { console.log(data); }); } 复制代码
页面测试效果同 Promise 一样。从上面列子可以看到 RxJS 和 Promise 的基本用法非常类似, 除了一些关键词不同。 Promise 里面用的是 then() 和 resolve(),而 RxJS 里面用的是 next() 和 subscribe()。
其实Rxjs相比Promise 要强大很多。比如 Rxjs 中可以中途撤回、Rxjs 可以发射多个值、Rxjs 提供了多种工具函数等等。
Rxjs 可以通过 unsubscribe() 可以撤回 subscribe 的动作。
我们修改 home2.component.ts
文件内容如下:
import {StorageService} from '../../services/storage.service'; constructor(public storage: StorageService) { } getRxjsData(){ /*4、通过Rxjs获取异步数据*/ let observer = this.storage.getRxjsData(); let oData = observer.subscribe((data) => { console.log(data); }); /*5、Rxjs允许取消订阅操作*/ setTimeout(() => { oData.unsubscribe(); }, 2000); } 复制代码
数据交互(Get,Post,jsonp)
Angular5.x 以后 Get、Post 和 jsonp 通过 HttpClientModule 模块来和服务器进行数据交互。
Get获取数据
1、 在 app.module.ts 中引入 HttpClientModule
import {HttpClientModule} from '@angular/common/http'; imports: [ BrowserModule, HttpClientModule, HttpClientJsonpModule ] 复制代码
2、html 文件内容:
<p>new3 works!</p> <br> <hr> <br> <button (click)="getData()">get请求数据</button> <br> <hr> <br> <ul> <li *ngFor="let item of dataList">{{item.name}} --- {{item.age}}</li> </ul> <br> 复制代码
3、new3.component.ts 文件
import {Component, OnInit} from '@angular/core'; import {HttpClient, HttpHeaders} from '@angular/common/http'; @Component({ selector: 'app-new3', templateUrl: './new3.component.html', styleUrls: ['./new3.component.css'] }) export class New3Component implements OnInit { dataList: any[] = []; constructor(public client: HttpClient) { } ngOnInit(): void { } getData() { // var api = 'http://a.itying.com/api/productlist'; var api = 'https://mdn.github.io/learning-area/javascript/oojs/json/superheroes.json'; this.client.get(api).subscribe((data: any) => { console.log(data); this.dataList = data.members; }); } } 复制代码
4、网页运行效果如下:
Post获取数据
在 SpringMVC 项目构建后台服务方法,一个是用 RequestBody 接受参数,另一个是用 RequestParam 接受参数。
@RestController @CrossOrigin(origins = "http://localhost:4200") public class EncodingController { @RequestMapping(value = "/testPostPara2") public Map testPostPara2(@RequestParam String uname,@RequestParam int age){ Map<String,Object> map = new HashMap<String, Object>(); map.put("name",uname); map.put("age",age); return map; } @RequestMapping("/testPostBody") public Map testPostBody(@RequestBody Map<String,Object> objectMap){ System.out.println(objectMap.toString()); return objectMap; } } 复制代码
出于安全原因,浏览器禁止 Ajax 调用驻留在当前原点之外的资源。例如,当你在一个标签中检查你的银行账户时,你可以在另一个选项卡上拥有 EVILL 网站。来自 EVILL 的脚本不能够对你的银行 API 做出 Ajax 请求(从你的帐户中取出钱!)使用您的凭据。 跨源资源共享(CORS)是由大多数浏览器实现的W3C规范,允许您灵活地指定什么样的跨域请求被授权,而不是使用一些不太安全和不太强大的策略,如IFRAME或JSONP。
关于跨域的更多内容可以参考注解@CrossOrigin解决跨域问题
注意上述方法都返回的是 Map 类型,不需要转换为 String 类型返回。但是如果直接访问 http://localhost:8080/testPostPara2?uname=hresh22&age=24 是会报错的,必须转换为 String 类型。
1、同样需要在 ap.module.ts 中引入 HttpClientModule 模块并注入。
2、html
<br> <button (click)="doPost2()">post提交数据:body中传参</button> <br> <hr> <br> <button (click)="doPost()">post提交数据:url后携带参数</button> 复制代码
3、new3.component.ts 文件
import {Component, OnInit} from '@angular/core'; import {HttpClient, HttpHeaders} from '@angular/common/http'; @Component({ selector: 'app-new3', templateUrl: './new3.component.html', styleUrls: ['./new3.component.css'] }) export class New3Component implements OnInit { dataList: any[] = []; constructor(public client: HttpClient) { } ngOnInit(): void { } doPost() { const header = { headers: new HttpHeaders({ 'Content-Type': 'application/json' }) }; let url = 'http://localhost:8080/testPostPara2?uname=hresh22&age=24'; this.client.post(url, {}, header).subscribe((response: any) => { console.log(response); }); } doPost2() { const header = { headers: new HttpHeaders({ 'Content-Type': 'application/json' }) }; let url = 'http://localhost:8080/testPostBody'; this.client.post(url, { 'uname': 'hresh', 'age': 24 }, header).subscribe((response: any) => { console.log(response); }); } } 复制代码
4、页面测试
需要先启动 web 项目,然后再启动 Angular 项目,点击页面上的按钮,查看效果。
jsonp获取数据
同 Get 请求获取数据一样,然后主要是添加 html 文件和 ts 中的方法。
1、 在 app.module.ts 中引入 HttpClientJsonpModule
import {HttpClientJsonpModule} from '@angular/common/http'; imports: [ BrowserModule, HttpClientJsonpModule ] 复制代码
2、html
<button (click)="getJsonpData()">Jsonp获取数据</button> 复制代码
3、new3.component.ts 文件
getJsonpData() { var api = 'http://a.itying.com/api/productlist'; this.client.jsonp(api, 'callback').subscribe((data) => { console.log(data); }) } 复制代码
4、页面测试
第三方模块axios获取数据
1、安装 axios
npm install axios 复制代码
2、配置服务文件 httpservice.service.ts
import {Injectable} from '@angular/core'; import axios from 'axios'; import {Observable} from 'rxjs'; @Injectable({ providedIn: 'root' }) export class HttpserviceService { constructor() { } //可以返回Promise对象,也可以使用Rxjs,根据个人习惯 axiosGet(api) { return new Promise((resolve, reject) => { axios.get(api).then(function(res) { resolve(res); }); }); } axiosRxjs(api) { return new Observable(function(observer) { axios.get(api).then(function(res) { observer.next(res); }); }); } } 复制代码
3、html 文件
<button (click)="getAxiosData()">Axios获取数据方式一</button> <hr> <br> <button (click)="getAxiosData2()">Axios获取数据方式二</button> 复制代码
4、在用到的地方引入 httpservice 并在构造函数声明
import { HttpserviceService } from '../../services/httpservice.service'; constructor(public client: HttpClient, public httpservice: HttpserviceService) { } //通过第三方模块获取服务器数据 getAxiosData() { var api = 'http://a.itying.com/api/productlist'; let axiosObj = this.httpservice.axiosGet(api); axiosObj.then((data) => { console.log(data); }) } //通过Rxjs获取 getAxiosData2() { var api = 'http://a.itying.com/api/productlist'; let axiosObj = this.httpservice.axiosRxjs(api); axiosObj.subscribe(function (data) { console.log(data); }) } 复制代码
5、页面测试