目录
1.2 Angular 中的 dom 操作(ViewChild)
1.3 父子组件中通过 ViewChild 调用子组件 的方法
4.5 Angualr6.x 之前 —— Rxjs 的工具函数 map、filter
4.6 Angualr6.x 以后 —— Rxjs6.x 的变化及使用
5.4 Angular 中使用第三方模块 axios 请求数据
1.@ViewChild、DOM 操作、css3动画
1.1 Angular 中的 dom 操作(原生 js)
- ngOnInit():组件和指令初始化完成以后触发的方法,并不是真正的 DOM加载完成
- 所以,在 ngOnInit() 中执行下面的操作,可能获取不到 DOM节点:
- ngAfterViewInit():视图加载完成以后触发的方法,DOM 加载完成,建议 DOM操作都放这里
1.2 Angular 中的 dom 操作(ViewChild)
- 引入 ViewChild 模块:import { Component ,ViewChild,ElementRef } from '@angular/core';
- 给要获取的 DOM元素 取名:<div #mybox>
- viewchild关联组件:@ViewChild('myBox') myBox:any;
- 获取组件节点:ngAfterViewInit() { let attrEl = this.mybox.nativeElement; }
1.3 父子组件中通过 ViewChild 调用子组件 的方法
- 父组件调用子组件,给子组件定义名称:<app-footer #footerChild></app-footer>
- 将 viewchild 和子组件关联起来:@ViewChild('footerChild') footer;
- 在父组件中调用子组件中的方法:run() { this.footer.footerRun(); }
- 总结一下:
- 给dom元素起名字 直接在标签里书写 #name 即可
- viewChild 获取子组件实例并调用其内部方法
- 组件间不可以相互调用方法,组件可以调用服务的方法,服务间可以相互调用方法,服务不可以调用组件的方法
- @viewChild 获取节点并赋值给一个变量: @viewchild('mybox') myboxBar:any;
- 获取真正的节点:【this.myboxBar.nativeElement】 .style.color = red;
1.4 css3侧边栏动画
<div class="content"> 内容区域 <button (click)="showAside()">弹出侧边栏</button> <button (click)="hideAside()">隐藏侧边栏</button> </div> <aside id="aside">这是一个侧边栏</aside> —————————————————————————————————————————————————————————————————————————————— #aside{ width: 200px; height: 100%; position: absolute; right: 0px; top:0px; transform: translate(100%,0); transition: all 2s; } —————————————————————————————————————————————————————————————————————————————— export class TransitionComponent implements OnInit { constructor() {} ngOnInit() {} showAside() { var asideDom: any = document.getElementById("aside"); // 原生js获取dom节点 asideDom.style.transform = "translate(0,0)"; } hideAside() { var asideDom: any = document.getElementById("aside"); // 原生js获取dom节点 asideDom.style.transform = "translate(100%,0)"; } }
2.Angular 组件通讯
- 父组件给子组件传值:在父组件内给子组件绑定属性,在子组件内引入 input模块,用@input类装饰器,接收对应的父组件传来的值
- 父组件获取子组件的值: 在父组件内引用子组件的标签中,给子组件添加#id,然后再ts中通过 viewchild获取子组件实例,然后调用对应的属性及方法
- 【了解】子组件通过 @output 触发父组件方法,在子组件内引入 output / EventEmitter 模块,子组件中实例化EventEmitter: @output() private outer = new EventEmitter<String>(),子组件中this.outer.emit('给父组件广播数据'),父组件中定义子组件中的事件 <app-header (outer)="定义接受事件($event)">
- 非父子组件的通信:localstorage / 服务
2.1 父组件给子组件传值 @input
- 父组件调用子组件的时候(属性绑定)传入数据:<app-header [msg]="msg"></app-header>
- 子组件引入 Input 模块:import { Component, OnInit ,Input } from xxx
- 子组件中 @Input 接收父组件传过来的数据:export class H implements OnInit { @Input() msg:string ... }
- 子组件中使用父组件的数据:这是在子组件中使用头部组件的数据 —— {{msg}}
———————————————————————————————————————————————————————————————————— 父组件:父组件html文件中调用子组件标签,将父组件本身的内容 作为属性 绑定给子组件 ———————————————————————————————————————————————————————————————————— <app-header [title]="title" [msg]="msg" [run]='run' [home]='this'></app-header> <div>我是首页组件</div> ——————— ————————— ——————— —————— ——————— —————— ————— —————— —————— export class HomeComponent implements OnInit { public title:string="首页组件的标题"; public msg:string='我是父组件的msg'; constructor() { } ngOnInit() { } run() { alert('我是父组件的run方法'); }} ———————————————————————————————————————————————————————————————————— 子组件:利用 @Input() 接受父组件传来的值 ———————————————————————————————————————————————————————————————————— <header> {{title}} </header> <button (click)="getParentRun()">子组件里面执行父组件的run方法</button> ——————— ————————— ——————— —————— ——————— —————— ————— —————— —————— export class HeaderComponent implements OnInit { @Input() title: any; // 接受父组件传来的数据 @Input() run: any; @Input() home: any; constructor() {} ngOnInit() {} getParentMsg() { alert(this.msg); // 获取父组件的数据 } getParentRun() { this.run(); // 执行父组件的 run 方法1 this.home.run(); // 执行父组件的 run 方法2 } }
2.2 子组件通过 @Output 触发父组件方法
- 子组件引入 Output 和 EventEmitter:import { Component, OnInit, Input, Output, EventEmitter } from xxx
- 子组件中实例化 EventEmitter:@Output() private outer = new EventEmitter();
- 子组件通过 EventEmitter 对象 outer 实例广播数据:this.outer.emit('msg from child')
- 父组件调用子组件的时候,定义接收事件:<app-header (outer)="runParent($event)"></app-header>
- 父组件接收到数据,调用自己的 runParent 方法,拿到子组件的数据:runParent(msg:string) { ...
———————————————————————————————————————————————————————————————————— 子组件:通过 @Output 给父组件广播数据 ———————————————————————————————————————————————————————————————————— <h2>我是footer子组件</h2> <button (click)="sendParent()">通过 @Output 给父组件广播数据</button> ——————— ————————— ——————— —————— ——————— —————— ————— —————— —————— export class FooterComponent implements OnInit { @Output() private outer = new EventEmitter(); public msg = "我是子组件footer的一个msg"; constructor() {} ngOnInit() {} run() { alert("我是子组件的 run 方法"); } sendParent() { this.outer.emit("我是子组件的数据"); } } ———————————————————————————————————————————————————————————————————— 父组件:定义子组件传过来的 @output事件,接受里面的参数 ———————————————————————————————————————————————————————————————————— <app-footer (outer)="run($event)"></app-footer> ——————— ————————— ——————— —————— ——————— —————— ————— —————— —————— run(e) { console.log(e); //子组件给父组件广播的数据 alert('aa'); }
2.3 父组件通过 @ViewChild 获取子组件的值
- 调用子组件,给子组件定义一个名称:<app-footer #footerChild></app-footer>
- 引入 ViewChild:import { Component, OnInit, ViewChild } from xxx
- ViewChild 和刚才的子组件关联起来:@ViewChild('footerChild') footer;
- 调用子组件:run() { this.footer.footerRun(); }
———————————————————————————————————————————————————————————————————— 父组件:在父组件中引用子组件标签,并给子组件命名 ———————————————————————————————————————————————————————————————————— <app-footer #footer></app-footer> // 给子组件标签重命名 <div>我是一个新闻组件</div> <button (click)="getChildMsg()">获取子组件的msg</button> <button (click)="getChildRun()">执行子组件的run方法</button> ——————— ————————— ——————— —————— ——————— —————— ————— —————— —————— export class NewsComponent implements OnInit { @ViewChild("footer") footer: any; // 父组件引入 viewchild,用于获取子组件的值 constructor() {} ngOnInit() {} getChildMsg() { alert(this.footer.msg); // 获取footer子组件的数据 } getChildRun() { this.footer.run(); // 获取footer子组件的数据 } } ———————————————————————————————————————————————————————————————————— 子组件 ———————————————————————————————————————————————————————————————————— <h2>我是footer子组件</h2> ——————— ————————— ——————— —————— ——————— —————— ————— —————— —————— export class FooterComponent implements OnInit { public msg = "我是子组件footer的一个msg"; constructor() {} ngOnInit() {} run() { alert("我是子组件的run方法"); } }
2.4 非父子组件通讯
- 公共的服务
- Localstorage (推荐)
- Cookie
3.Angular 生命周期
- 生命周期:组件创建、组件更新、组件销毁时触发的一系列的方法
- constructor:构造函数中除了使用简单的值对局部变量进行初始化 之外,什么都不应该做(非生命周期函数)
- ngOnChanges():当被绑定的输入属性的值发生变化时调用(父子组件传值 / 父组件改数据 的时候会触发)
- 【重要】ngOnInit():初始化指令 / 组件,此时 DOM没加载完,请求数据一般放在这里
- ngDoCheck():检测,做一些自定义的操作,比如看看数据有没有改变,如果改变了做啥,没改变做啥
- ngAfterContentInit():当把内容投影进组件之后调用,组件渲染完成后触发的函数
- ngAfterContentChecked():检测
- 【重要】ngAfterViewInit():视图加载完成,此时一般进行 DOM 操作
- ngAfterViewChecked():检测
- 【重要】ngOnDestroy():销毁 指令 / 组件之前调用,比如用户忘记保存,组件销毁之前,保存用户信息
4.Rxjs 异步数据流编程
4.1 Rxjs 介绍
- Rxjs 将一切数据,包括 HTTP请求、DOM事件、普通数据等,包装成 流 的形式,进行处理
- Rxjs 以同步编程的方式处理异步数据,Angular 引入 RxJS 为了就是让异步可控、更简单
- 常见的异步编程:
- 回调函数
- 事件监听/发布订阅
- Promise
- Rxjs
4.2 Promise 和 RxJS 处理异步对比
- Promise 里面用的是 then() 获取结果 和 resolve() 处理正常事件:
- Rxjs 里面用的是 subscribe() 获取结果 和 observer.next() 处理正常事件:
- 在 observer.next() 同级,还可以添加:observer.error('数据')
- Promise 和 RxJS 的用法基本相似,但 Rxjs 相比 Promise 强大很多,比如:
- Rxjs 中可以中途撤回、Rxjs 可以发射多个值、Rxjs 提供了多种工具函数等
4.3 Rxjs unsubscribe 取消订阅
- Promise 创建之后,动作是无法撤回的
- Observable 创建之后,动作可以通过 unsbscribe() 方法撤回:
- ← 一秒后撤回刚刚的操作
4.4 Rxjs 订阅后多次执行
- Promise 无法让异步里面的方法多次执行
- Promise 最终结果要么 resole(兑现)、要么 reject (拒绝),都只能触发一次,多次调用 resolve 会抛异常
- Observable 可以不断地触发下一个值:
4.5 Angualr6.x 之前 —— Rxjs 的工具函数 map、filter
- Angular6.x 以后 使用以前的 Rxjs 方法(map、filter),必须安装 rxjs-compat 模块才可以
- npm install rxjs-compat
import {Observable} from 'rxjs'; import 'rxjs/Rx'; let stream = new Observable<any>(observer => { let count = 0; setInterval(() => { // 每隔1s自动调用一次的定时器 observer.next(count++); }, 1000); }); // 过滤出符合条件的结果,链式调用 .subscribe() stream.filter(val => val%2 == 0).subscribe(value => console.log("filter>"+value)); // 对结果进行处理,链式调用 .subscribe() stream.map(value => { return value * value }).subscribe(value => console.log("map>"+value));
4.6 Angualr6.x 以后 —— Rxjs6.x 的变化及使用
- Rxjs6.x 主要变化在:import 方式(rxjs/Rx → rxjs/operators)、pipe()
import {Observable} from 'rxjs'; import {map,filter} from 'rxjs/operators'; // 注意引入的文件改了 rxjs/Rx → rxjs/operators let stream = new Observable<any>(observer => { let count = 0; setInterval(() => { observer.next(count++); }, 1000); }); // 使用管道 pipe() stream.pipe( filter(val=>val%2==0) ) .subscribe(value => console.log("filter>"+value)); // 管道 pipe() 内同时放两个处理 stream.pipe( filter(val=>val%2==0), map(value => {return value * value}) ) .subscribe(value => console.log("map>"+value));
4.7 Rxjs 延迟执行
import { Observable, fromEvent } from 'rxjs'; import { map, filter, throttleTime } from 'rxjs/operators'; var button = document.querySelector('button'); fromEvent(button, 'click').pipe(throttleTime(1000)) .subscribe(() => console.log(`Clicked`));
5.Angular 中的数据交互
- Angular5.x 以后,get、post、jsonp 和服务器交互使用的是 HttpClientModule 模块
- 在 app.module.ts 引入并声明 HttpClientModule:
- 在用到 get/post/jsonp 的 组件.ts 引入 HttpClient,并在 构造函数 中声明 HttpClient:
import { Component, OnInit } from "@angular/core"; // HttpClient 当做一个服务,要在构造函数中声明,而 HttpHeaders 不需要在构造函数中声明 import { HttpClient, HttpHeaders } from "@angular/common/http"; // 使用服务里面的axios获取数据 import { HttpserviceService } from "../../services/httpservice.service"; @Component({ selector: "app-news", templateUrl: "./news.component.html", styleUrls: ["./news.component.scss"], }) export class NewsComponent implements OnInit { public list: any[] = []; constructor( public http: HttpClient, //注意这里的两个声明哦 public httpService: HttpserviceService ) {}
5.1 Angular get 请求数据
- 注意跨域
getData() { // 服务器必须允许跨域 let api = "http://a.com/api/productlist"; this.http.get(api).subscribe((response: any) => { console.log(response); this.list = response.result; }); }
5.2 Angular post 提交数据
- 使用 post 还需要在 组件.ts 引入一个模块:HttpHeaders,不需要 在构造函数中声明
- import { HttpClient, HttpHeaders } from "@angular/common/http";
- 注意跨域,注意手动设置请求类型
doLogin() { const httpOptions = { // 手动设置请求类型 headers: new HttpHeaders({ "Content-Type": "application/json" }), }; let api = "http://127.0.0.1:3000/dologin"; // 存在跨域 this.http .post(api, { username: "张三", age: 20 }, httpOptions) .subscribe((response) => { console.log(response); }); }
5.3 Angular Jsonp 请求数据
- 使用 jsonp 还需要在 app.module.ts 引入并声明一个模块:HttpClientJsonpModule
- jsonp请求:服务器必须得支持 jsonp
getJsonpData() { // jsonp请求 服务器必须得支持jsonp // http://a.com/api/productlist?callback=xxx // http://a.com/api/productlist?cb=xxx let api = "http://a.com/api/productlist"; // 注意这里的后缀和前面的区别 this.http.jsonp(api, "callback") // 这里的 callback 取决于上面地址是 cb,还是 callback .subscribe((response) => { console.log(response); }); }
5.4 Angular 中使用第三方模块 axios 请求数据
- 安装 axios:cnpm install axios --save
- 用到的地方引入 axios,比如创建单独的服务:import axios from 'axios';
———————————————————————————————————————————————————————————————— 服务: ———————————————————————————————————————————————————————————————— import { Injectable } from "@angular/core"; import axios from "axios"; // 注意引入 axios @Injectable({ providedIn: "root", }) export class HttpserviceService { constructor() {} axiosGet(api) { // 封装 axios/promise return new Promise((resolve, reject) => { axios.get(api).then(function (response) { resolve(response); // handle success });}); }} ———————————————————————————————————————————————————————————————— 组件内: ———————————————————————————————————————————————————————————————— <button (click)="getAxiosData()">通过第三方模块获取服务器的数据</button> getAxiosData() { console.log("axios获取数据"); let api = "http://a.com/api/productlist"; this.httpService.axiosGet(api).then((data) => { console.log(data); });} }