Angular 学习笔记 —— 组件通讯、生命周期、Rxjs、数据交互

 

目录

1.@ViewChild、DOM 操作、css3动画

1.1 Angular 中的 dom 操作(原生 js)

1.2 Angular 中的 dom 操作(ViewChild)

1.3 父子组件中通过 ViewChild 调用子组件 的方法

1.4 css3侧边栏动画

2.Angular 组件通讯

2.1 父组件给子组件传值 @input

2.2 子组件通过 @Output 触发父组件方法

2.3 父组件通过 @ViewChild 获取子组件的值

2.4 非父子组件通讯

3.Angular 生命周期

4.Rxjs 异步数据流编程

4.1 Rxjs 介绍

4.2 Promise 和 RxJS 处理异步对比

4.3 Rxjs unsubscribe 取消订阅

4.4 Rxjs 订阅后多次执行

4.5 Angualr6.x 之前 —— Rxjs 的工具函数 map、filter

4.6 Angualr6.x 以后 —— Rxjs6.x 的变化及使用

4.7 Rxjs 延迟执行

5.Angular 中的数据交互

5.1 Angular get 请求数据

5.2 Angular post 提交数据

5.3 Angular Jsonp 请求数据

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)

  1. 引入 ViewChild 模块:import { Component ,ViewChild,ElementRef } from '@angular/core';
  2. 给要获取的 DOM元素 取名:<div #mybox>
  3. viewchild关联组件:@ViewChild('myBox') myBox:any;
  4. 获取组件节点:ngAfterViewInit() { let attrEl = this.mybox.nativeElement; }

1.3 父子组件中通过 ViewChild 调用子组件 的方法

  1. 父组件调用子组件,给子组件定义名称:<app-footer #footerChild></app-footer>
  2. 将 viewchild 和子组件关联起来:@ViewChild('footerChild') footer;
  3. 在父组件中调用子组件中的方法:run() { this.footer.footerRun(); }
  • 总结一下:

 

  1. 给dom元素起名字 直接在标签里书写 #name 即可
  2. viewChild 获取子组件实例并调用其内部方法
  3. 组件间不可以相互调用方法,组件可以调用服务的方法,服务间可以相互调用方法,服务不可以调用组件的方法
  4. @viewChild 获取节点并赋值给一个变量: @viewchild('mybox') myboxBar:any;
  5. 获取真正的节点:【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

  1. 父组件调用子组件的时候(属性绑定)传入数据:<app-header [msg]="msg"></app-header>
  2. 子组件引入 Input 模块:import { Component, OnInit ,Input } from xxx
  3. 子组件中 @Input 接收父组件传过来的数据:export class H implements OnInit { @Input() msg:string ... }
  4. 子组件中使用父组件的数据:这是在子组件中使用头部组件的数据 —— {{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 触发父组件方法

  1. 子组件引入 Output 和 EventEmitter:import { Component, OnInit, Input, Output, EventEmitter } from xxx
  2. 子组件中实例化 EventEmitter:@Output() private outer = new EventEmitter();
  3. 子组件通过 EventEmitter 对象 outer 实例广播数据:this.outer.emit('msg from child')
  4. 父组件调用子组件的时候,定义接收事件:<app-header (outer)="runParent($event)"></app-header>
  5. 父组件接收到数据,调用自己的 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 获取子组件的值

  1. 调用子组件,给子组件定义一个名称:<app-footer #footerChild></app-footer>
  2. 引入 ViewChild:import { Component, OnInit, ViewChild } from xxx
  3. ViewChild 和刚才的子组件关联起来:@ViewChild('footerChild') footer;
  4. 调用子组件: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 非父子组件通讯

  1. 公共的服务
  2. Localstorage (推荐)
  3. 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 为了就是让异步可控、更简单
  • 常见的异步编程:
  1. 回调函数
  2. 事件监听/发布订阅
  3. Promise
  4. 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);
    });}
}

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Lyrelion

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值