Angular 中的可观察对象

简介: Angular 中的可观察对象

Angular 使用可观察对象作为处理各种常用异步操作的接口。比如:


  • EventEmitter 类派生自 Observable
  • HTTP 模块使用可观察对象来处理 AJAX 请求和响应。
  • 路由器和表单模块使用可观察对象来监听对用户输入事件的响应。


EventEmitter


Angular 提供了一个 EventEmitter 类,它用来通过组件的 @Output() 装饰器 发送一些值。EventEmitter 扩展了 RxJS Subject,并添加了一个 emit() 方法,这样它就可以发送任意值了。当你调用 emit() 时,就会把所发送的值传给订阅上来的观察者的 next() 方法。 我们来查看一下该类的定义:


export declare class EventEmitter<T extends any> extends Subject<T> {
    /**
     * Creates an instance of this class that can
     * deliver events synchronously or asynchronously.
     *
     * @param isAsync When true, deliver events asynchronously.
     *
     */
    constructor(isAsync?: boolean);
    /**
     * 发出包含给定值的事件。
     */
    emit(value?: T): void;
    /**
     *注册此实例发出的事件的处理程序。
     */
    subscribe(generatorOrNext?: any, error?: any, complete?: any): Subscription;
}
复制代码


EventEmitter 与指令@Output 一起在组件中使用以同步或异步方式发送自定义事件,并通过订阅实例来注册这些事件的处理程序。


接下来我们演示一个案例,在组件之间传递数据。


子组件 zippy.component.ts


import { Component, OnInit, Output, EventEmitter } from '@angular/core';
@Component({
  selector: 'app-zippy',
  templateUrl: '
    <div class="zippy">
      <div (click)="toggle()">点击该文本</div>
      <div [hidden]="visible">
        <ng-content></ng-content>
      </div>
    </div>
  '
})
export class ZippyComponent implements OnInit {
  visible = true;
  // tslint:disable-next-line: no-output-native
  @Output() open = new EventEmitter<any>();
  // tslint:disable-next-line: no-output-native
  @Output() close = new EventEmitter<any>();
  constructor() { }
  ngOnInit(): void {
  }
  toggle() {
    this.visible = !this.visible;
    if (this.visible) {
      this.close.emit('关闭');
    } else {
      this.open.emit('打开');
      this.open.subscribe((data) => {
        console.log('open subscribe:' + data);
      });
    }
  }
}
复制代码


父组件 home4.component.ts


import { Component, OnInit } from '@angular/core';
@Component({
  selector: 'app-home4',
  templateUrl: '
    <app-zippy (open)="onOpen($event)" (close)="onClose($event)">我是home组件的内容</app-zippy>
  '
})
export class Home4Component implements OnInit {
  constructor() { }
  ngOnInit(): void {
  }
  onOpen(e) {
    console.log(e);
  }
  onClose(e) {
    console.log(e);
  }
}
复制代码


运行项目,在页面上点击文本内容,注意观察控制台输出的内容。如下图所示:


1.jpg


上文中关于 ng-content 标签的使用,感兴趣的朋友可以去阅读一下:Angular开发实践(八): 使用ng-content进行组件内容投射


HTTP


Angular 的 HttpClient 从 HTTP 方法调用中返回了可观察对象。例如,http.get(‘/api’) 就会返回可观察对象。相对于基于承诺(Promise)的 HTTP API,它有一系列优点:


  • 可观察对象不会修改服务器的响应(和在承诺上串联起来的 .then() 调用一样)。反之,你可以使用一系列操作符来按需转换这些值。
  • HTTP 请求是可以通过 unsubscribe() 方法来取消的。
  • 请求可以进行配置,以获取进度事件的变化。
  • 失败的请求很容易重试。


关于讲解数据交互那一部分内容时,对于 get、post 请求都有测试案例,现在回头看当时比较疑惑的内容,现在就豁然开朗了。


Async 管道


AsyncPipe 会订阅一个可观察对象或承诺,并返回其发出的最后一个值。当发出新值时,该管道就会把这个组件标记为需要进行变更检查的(译注:因此可能导致刷新界面)。


下面的例子把 time 这个可观察对象绑定到了组件的视图中。这个可观察对象会不断使用当前时间更新组件的视图。


import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
@Component({
  selector: 'app-asyncpipe',
  templateUrl: '
    <div><code>observable|async</code>:
      Time: {{ time | async }}</div>
  '
})
export class AsyncpipeComponent implements OnInit {
  time: Observable<any>;
  constructor() { }
  ngOnInit(): void {
    this.time = new Observable(observer => {
      setInterval(() => {
        observer.next(new Date().toString());
      }, 1000);
    });
  }
}
复制代码


这样写就相当于订阅了 time,会实时接收 next 过来的值,Observable 定义如上,用来逐秒打印时间,页面接收的值类型为 Observable。


页面测试效果:


observable|async: Time: Tue Apr 14 2020 09:39:46 GMT+0800 (中国标准时间)
复制代码


若是要接收 object 对象,需要这样取值。


import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
@Component({
  selector: 'app-asyncpipe',
  templateUrl: '
    <ng-container *ngIf="time2 |async as tim">
      <div>
        {{tim.date}}---{{tim.time}}
      </div>
    </ng-container>
  '
})
export class AsyncpipeComponent implements OnInit {
  time2: Observable<any>;
  constructor() { }
  ngOnInit(): void {
      this.time2 = new Observable(observer => {
      setInterval(() => {
        const dd = new Date();
        observer.next({ date: dd.toString(), time: dd.toTimeString() });
      }, 1000);
    });
  }
}
复制代码


页面测试结果:


Tue Apr 14 2020 09:49:38 GMT+0800 (中国标准时间)---09:49:38 GMT+0800 (中国标准时间)
复制代码


路由器 (router)


Router.events 以可观察对象的形式提供了其事件。 你可以使用 RxJS 中的 filter() 操作符来找到感兴趣的事件,并且订阅它们,以便根据浏览过程中产生的事件序列作出决定。 例子如下:


import { Router, NavigationStart } from '@angular/router';
import { filter } from 'rxjs/operators';
@Component({
  selector: 'app-routable',
  templateUrl: './routable.component.html',
  styleUrls: ['./routable.component.css']
})
export class Routable1Component implements OnInit {
  navStart: Observable<NavigationStart>;
  constructor(private router: Router) {
    // Create a new Observable that publishes only the NavigationStart event
    this.navStart = router.events.pipe(
      filter(evt => evt instanceof NavigationStart)
    ) as Observable<NavigationStart>;
  }
  ngOnInit() {
    this.navStart.subscribe(evt => console.log('Navigation Started!'));
  }
}
复制代码


ActivatedRoute 是一个可注入的路由器服务,它使用可观察对象来获取关于路由路径和路由参数的信息。比如,ActivatedRoute.url 包含一个用于汇报路由路径的可观察对象。结合 Angular基础知识学习(二)中动态路由的JS跳转,此处我们仅需要修改 product-detail.component.ts 文件:


import { Component, OnInit } from '@angular/core';
import {ActivatedRoute} from '@angular/router';
@Component({
  selector: 'app-product-detail',
  templateUrl: './product-detail.component.html',
  styleUrls: ['./product-detail.component.css']
})
export class ProductDetailComponent implements OnInit {
  constructor(public router: ActivatedRoute) {
  }
  ngOnInit(): void {
    this.router.url.subscribe(url => console.log('The URL changed to: ' + url));
  }
}
复制代码


页面测试结果为:


2.jpg


又比如 ActivatedRoute.queryParams 包含路由跳转传递参数的可观察对象。还是结合我之前文章的案例,修改 news.component.ts 文件:


import {Component, OnInit} from '@angular/core';
import {ActivatedRoute} from '@angular/router';
@Component({
  selector: 'app-news',
  templateUrl: './news.component.html',
  styleUrls: ['./news.component.css']
})
export class NewsComponent implements OnInit {
  nums: any[] = [];
  constructor(public router: ActivatedRoute) {
    this.router.queryParams.subscribe((data) => {
      console.log(data);
    });
  }
  ngOnInit(): void {
  }
}
复制代码


页面测试结果为:


3.jpg


响应式表单 (reactive forms)


响应式表单具有一些属性,它们使用可观察对象来监听表单控件的值。 FormControlvalueChanges 属性和 statusChanges 属性包含了会发出变更事件的可观察对象。订阅可观察的表单控件属性是在组件类中触发应用逻辑的途径之一。比如:


import { FormGroup } from '@angular/forms';
@Component({
  selector: 'my-component',
  template: 'MyComponent Template'
})
export class MyComponent implements OnInit {
  nameChangeLog: string[] = [];
  heroForm: FormGroup;
  ngOnInit() {
    this.logNameChange();
  }
  logNameChange() {
    const nameControl = this.heroForm.get('name');
    nameControl.valueChanges.forEach(
      (value: string) => this.nameChangeLog.push(value)
    );
  }
}



目录
相关文章
|
6月前
Angular获取Location对象,获取当前网页url、hash、hostname、href、pathname、port、protocal
Angular获取Location对象,获取当前网页url、hash、hostname、href、pathname、port、protocal
|
3月前
|
API 开发者 UED
PrimeFaces:JSF的魔法衣橱,解锁UI设计的无限可能!
【8月更文挑战第31天】本文介绍如何结合 JSF(JavaServer Faces)和 PrimeFaces 构建美观且功能强大的现代用户界面。PrimeFaces 提供丰富的 UI 组件库,包括按钮、输入框、数据网格等,支持现代 Web 标准,简化界面开发。文章通过具体示例展示如何使用 `&lt;p:inputText&gt;` 和 `&lt;p:calendar&gt;` 等组件创建用户表单,并用 `&lt;p:dataTable&gt;` 展示数据集合,提升 JSF 应用的易用性和开发效率。
56 0
|
3月前
|
开发者 安全 SQL
JSF安全卫士:打造铜墙铁壁,抵御Web攻击的钢铁防线!
【8月更文挑战第31天】在构建Web应用时,安全性至关重要。JavaServer Faces (JSF)作为流行的Java Web框架,需防范如XSS、CSRF及SQL注入等攻击。本文详细介绍了如何在JSF应用中实施安全措施,包括严格验证用户输入、使用安全编码实践、实施内容安全策略(CSP)及使用CSRF tokens等。通过示例代码和最佳实践,帮助开发者构建更安全的应用,保护用户数据和系统资源。
51 0
|
3月前
|
开发者 C# C++
揭秘:如何轻松驾驭Uno Platform,用C#和XAML打造跨平台神器——一步步打造你的高性能WebAssembly应用!
【8月更文挑战第31天】Uno Platform 是一个跨平台应用程序框架,支持使用 C# 和 XAML 创建多平台应用,包括 Web。通过编译为 WebAssembly,Uno Platform 可实现在 Web 上运行高性能、接近原生体验的应用。本文介绍如何构建高效的 WebAssembly 应用:首先确保安装最新版本的 Visual Studio 或 VS Code 并配置 Uno Platform 开发环境;接着创建新的 Uno Platform 项目;然后通过安装工具链并使用 Uno WebAssembly CLI 编译应用;最后添加示例代码并测试应用。
96 0
|
3月前
|
前端开发 开发者 安全
JSF支付功能大揭秘:探索如何在Java世界中实现安全无缝的在线支付体验
【8月更文挑战第31天】在电子商务和在线交易日益普及的今天,实现在线支付功能已成为许多Web应用的必备需求。JavaServer Faces (JSF) 作为一种流行的Java Web框架,提供了丰富的组件和工具来构建用户界面,包括与支付网关集成以实现在线支付。支付网关是处理信用卡和借记卡支付的系统,在商家和银行之间起到桥梁作用。本文将探讨如何使用JSF与支付网关集成,以及实现在线支付功能时需要考虑的关键点
44 0
|
3月前
|
开发者 前端开发 开发框架
JSF与移动应用,开启全新交互体验!让你的Web应用轻松征服移动设备,让用户爱不释手!
【8月更文挑战第31天】在现代Web应用开发中,移动设备的普及使得构建移动友好的应用变得至关重要。尽管JSF(JavaServer Faces)主要用于Web应用开发,但结合Bootstrap等前端框架,也能实现优秀的移动交互体验。本文探讨如何在JSF应用中实现移动友好性,并通过示例代码展示具体实现方法。使用Bootstrap的响应式布局和组件可以确保JSF页面在移动设备上自适应,并提供友好的表单输入和提交体验。尽管JSF存在组件库较小和学习成本较高等局限性,但合理利用其特性仍能显著提升用户体验。通过不断学习和实践,开发者可以更好地掌握JSF应用的移动友好性,为Web应用开发贡献力量。
48 0
|
3月前
|
JavaScript 开发者
从零基础到实战应用:Angular入门指南带你一步步构建你的第一个Web应用——全面介绍环境搭建、项目创建、组件开发与应用集成
【8月更文挑战第31天】本文档是针对初学者的Angular入门指南。通过详细步骤与示例代码,教你如何使用Angular CLI搭建开发环境、创建新项目、添加及配置组件,并运行首个应用。Angular是由Google开发的强大Web框架,专为高效构建复杂单页应用设计。按照本指南操作,你将能够快速上手Angular,开启Web应用开发之旅。
91 0
|
3月前
|
前端开发 JavaScript 测试技术
Angular CLI 快速入门超棒!这个提高开发效率的必备工具,带你轻松开启 Angular 项目之旅!
【8月更文挑战第31天】作为一名前端开发者,我发现Angular CLI是提升Angular项目开发效率的强大工具。它是Angular团队提供的命令行工具,能快捷创建、开发及维护项目。通过简单命令即可创建项目、生成组件、服务、模块等,并支持构建、测试与部署,大幅简化开发流程,使开发者更专注业务逻辑。项目结构清晰,便于理解和维护,极大地提高了开发效率。如果你还未尝试Angular CLI,强烈推荐一试,其便捷性定会让你爱不释手。
41 0
|
3月前
|
前端开发 JavaScript 编译器
【性能革命】Angular Ivy编译器:一场前端开发者的极速盛宴,揭秘应用瘦身与提速的黑科技,让你的Angular项目焕发新生的终极指南
【8月更文挑战第31天】Angular Ivy编译器是Angular团队推出的更新,旨在改善应用性能,减少构建时间和代码量。自Angular 9起,Ivy成为默认编译器。本文通过案例分析,介绍Ivy的工作原理及其优势。以一个复杂应用为例,展示了Ivy如何通过减少生成的JavaScript代码量、优化模板表达式解析等方式提升性能。通过创建示例项目并比较启用与未启用Ivy的构建结果,证明了Ivy在构建速度和文件大小上的显著改进,同时提高了运行时性能。这对于追求高性能和快速开发的应用至关重要。
34 0