[Angular 基础] - service 服务

[Angular 基础] - service 服务

之前的笔记就列举三个好了……没想到 Angular 东西这么多(ー ー;)……全加感觉越来越凑字数了

  • [Angular 基础] - 视图封装 & 局部引用 & 父子组件中内容传递

  • [Angular 基础] - 生命周期函数

  • [Angular 基础] - 自定义指令,深入学习 directive


Angular 的 service 如果后端出身的应该很熟悉,它是 Angular 自行管理,并使用 Dependency Injection 去实现的一个类。因此它比较合适使用的场景是,多个嵌套组件需要互相沟通,并需要传递值。

举例说明:

|- a
|  |- b
|  |  |- d
|  |- c
|  |  |- e

这个情况下,a 如果需要和 de 进行沟通的话,那么

  • bc 也需要通过 @Input 去获取从 a 传来的值,并将其传到 de 中去;
  • bc 也需要通过 @Output 去获取从 de 传来的事件,并将其传到 a 中去

这就是一个不可避免的沟通环节。

使用 service 就可以比较有效的解决这个问题

创建一个新的案例

这个案例相对比较简单,就是按照上面的结构创建一个项目。在这个简单的案例里,bc 没有任何作用,只是作为 a <--> da <--> e 之间的承接桥梁。在真实的项目中,bc 的作用可能会包括一些数据处理、选择渲染之类的。

项目结构如下:

❯ tree src/app/
src/app/
├── app.component.css
├── app.component.html
├── app.component.spec.ts
├── app.component.ts
├── app.module.ts
├── b
│   ├── b.component.css
│   ├── b.component.html
│   ├── b.component.ts
│   └── d
│       ├── d.component.css
│       ├── d.component.html
│       └── d.component.ts
└── c├── c.component.css├── c.component.html├── c.component.ts└── e├── e.component.css├── e.component.html└── e.component.ts5 directories, 17 files

a 的实现

这里主要还是传值+绑定事件,具体内容在 [Angular 基础] - 自定义事件 & 自定义属性 里,这里就不多做赘述,直接放代码了:

  • V 层

    <div class="container"><div class="row"><div class="col-xs-12 col-md-8 col-md-offset-2"><app-b [message]="aToD" (messageFromB)="onRecieveMessageFromB"></app-b><app-c [message]="aToE"></app-c></div></div>
    </div>
    
  • VM 层:

    import { Component, EventEmitter, OnInit, Output } from '@angular/core';@Component({selector: 'app-root',templateUrl: './app.component.html',styleUrls: ['./app.component.css'],
    })
    export class AppComponent {aToD = 'message from a to d';aToE = 'message from a to e';@Output() messageFromB = new EventEmitter<string>();onRecieveMessageFromB($event: string): void {this.aToD = $event;console.log('message from b to a: ', $event);}
    }
    

b 的实现

实现基本和 a 一致,这里也就放代码了:

  • V 层

    <div class=""><app-d [message]="message" (messageToB)="onRecieveMessage($event)"></app-d>
    </div>
    
  • VM 层

    import {Component,EventEmitter,Input,OnInit,Output,
    } from '@angular/core';@Component({selector: 'app-b',templateUrl: './b.component.html',styleUrl: './b.component.css',
    })
    export class BComponent implements OnInit {@Input() message: string;@Output() messageToA = new EventEmitter<string>();ngOnInit(): void {}onRecieveMessage($event: string): void {this.message = $event;this.messageToA.emit(this.message);console.log('message from b to a: ', this.message);}
    }
    

d 的实现

  • V 层

    <input type="text" [value]="message" (input)="onChangeText($event)" />
    
  • VM 层

    import {Component,EventEmitter,Input,OnInit,Output,
    } from '@angular/core';@Component({selector: 'app-d',templateUrl: './d.component.html',styleUrl: './d.component.css',
    })
    export class DComponent implements OnInit {@Input() message: string;@Output() messageToB = new EventEmitter<string>();ngOnInit(): void {}onChangeText($event: Event): void {this.message = ($event.target as HTMLInputElement).value;this.messageToB.emit(this.message);console.log('message from d to b: ', this.message);}
    }
    

最后实现效果如下:

在这里插入图片描述

如果说 React 只是将 onChangeHandler 一个个向子组件里传递,做 props drilling,那么 Angular 除了要在 HTML Template 中传值之外,还需要在组件中实现 @Input@Output 去接受从父组件中传下来的值,并且将事件送到父组件中,对比起来操作更加的麻烦

使用 service 代替

这里使用 service 代替上下传递 @Input@Outpu 进行实现

创建 service

这里依旧使用 cli 去创建 service:

❯ ng generate service services/message --skip-tests
CREATE src/app/services/message.service.ts (136 bytes)

此时结构如下:

在这里插入图片描述

实现如下:

import { Injectable } from '@angular/core';@Injectable({providedIn: 'root',
})
export class MessageService {passedMessage = 'message from a to e';constructor() {}updateMessage(msg: string) {this.passedMessage = msg;}
}

具体实现会在下一个 section 说明

调用 service

调用方式是在构造函数中让 Angular 自动使用 dependency injection 实现

a 的修改:
export class AppComponent {// 这里的 dependency injection 是由 angular 实现的constructor(private messageService: MessageService) {}
}
c 的实现
import { Component, DoCheck, Input } from '@angular/core';
import { MessageService } from '../services/message.service';@Component({selector: 'app-c',templateUrl: './c.component.html',styleUrl: './c.component.css',
})
export class CComponent implements DoCheck {message: string;constructor(private messageService: MessageService) {this.message = this.messageService.passedMessage;}ngDoCheck(): void {console.log(this.messageService.passedMessage);}
}

HTML Template 中只需要渲染一个 e 即可:

<app-e></app-e>

⚠️:这里主要是 log 一下 service 中变化的值。因为 message 是一个 primitive,所以想要正确的获取 message 的变化是要使用 Observable 的,目前暂时没有涉及到这个部分,因此只是在 ngDoCheck 中输出一下值,表示当前的变化已经被获取了

e 的实现
import { Component, Input } from '@angular/core';
import { MessageService } from '../../services/message.service';@Component({selector: 'app-e',templateUrl: './e.component.html',styleUrl: './e.component.css',
})
export class EComponent {message: string;constructor(private messageService: MessageService) {this.message = this.messageService.passedMessage;}onChangeText($event: Event): void {this.messageService.updateMessage((<HTMLInputElement>$event.target).value);}
}

最终效果:

在这里插入图片描述

可以看到,对比 a <--> b <--> d 的沟通, a <--> c <--> e 中使用 service 更加的简洁

深入了解 service

Injectable

这个 decorator 在新版的 Angular 是推荐每个 service 都放上,现在默认使用 cli 就会自动带上 Injectable

providedIn 则是挂载的范围,默认情况下挂载的范围是全局。换言之所有的 component 都共享一个 singleton。如果将 providedIn 删除的话,那么 Angular 就可以创建多个 instance

多个 instance & providers

这里首先需要将 Injectable 中的 providedIn 去掉,只保留 @Injectable 这个 decorator 或者去除都行——新版 Angular 是推荐保留 decorator 的

随后需要修改 @Component decorator,这里是修改 B/C 两个组件中的 decorator:

@Component({selector: 'app-b',templateUrl: './b.component.html',styleUrl: './b.component.css',providers: [MessageService],
})

这样当前 component 及其后代 component 都会共享同一个 service:

在这里插入图片描述

⚠️:这里页面显示的(d/e 从 MessageService 中接受的信息)与 log 中是一致的

如果修改 d/e decorator 中的 providers 的话,d/e 二者也会有自己的 service instance:

在这里插入图片描述

⚠️:这里页面显示的(d/e 从 MessageService 中接受的信息)与 log 中是不一致的

这是因为 providers 是 Angular 接受参数用来配置 Dependency Injection 的地方,提供值就会新建一个新的 instance。因此如果想要组件内共享同一个 service 的话,就需要在最近祖先节点修改对应的 providers

👀:传的信息内容我通过 Faker 的随机 lorem 生成,所以每个 service 会不一样

service 注入 service

我这里的实现是两个 service 都会有 @Injectable 这个装饰器,这样的实现会方便一些。MessageService 的实现基本不变,需要修改的就是在构造函数内,通过依赖注入绑定一个 LoggingService,修改如下:

import { Injectable } from '@angular/core';
import { faker } from '@faker-js/faker';
import { LoggingService } from './logging.service';@Injectable()
export class MessageService {passedMessage = faker.lorem.sentence();constructor(private loggingService: LoggingService) {this.loggingService.logMessage('MessageService constructor created message to ' + this.passedMessage);}updateMessage(msg: string) {this.passedMessage = msg;this.loggingService.logMessage('MessageService updated message to ' + msg);}
}

LoggingService 则是一个实现了输出信息的 service:

import { Injectable } from '@angular/core';@Injectable({ providedIn: 'root' })
export class LoggingService {constructor() {}logMessage(msg: string) {console.log(`${msg} received at ${new Date().toLocaleTimeString()}`);}
}

这样每次当 MessageService 被实例化和变动的时候,都会调用一次输出日志方法:

在这里插入图片描述

services 的应用场景

根据案例可以看出来,它可以实现以下几个功能:

  • 数据共享

    不用使用 @Input 进行不同层级的数据传递

  • 状态管理

    这个作用和 React 的 Context 有点相似,在层级内控制状态,并且通过状态进行数据和组件的对应渲染

  • API 交互

    HTTP 请求的抽象实现,比如说实现一个 API 层级的 CRUD 封装,这样所有的组件都可以较为方便的调用

  • 业务逻辑实现

    也是属于功能的一种抽象,如果某些功能不是特定属于几个组件内,那么就可以将其抽离出来进行共享

  • util

    也是属于功能的一种抽象,如果某些功能不是特定属于几个组件内,那么就可以将其抽离出来进行共享

    其中一个例子就是上面实现的 logging util

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/696908.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

请简述你对SpringMVC的理解

SpringMVC是一种基于Java语言开发&#xff0c;实现了WebMVC设计模式&#xff0c;请求驱动类型 的轻量级Web框架。 采用了MVC架构模式的思想&#xff0c;通过把Model&#xff0c;View&#xff0c;Controller分离&#xff0c;将Web层进 行职责解耦&#xff0c;从而把复杂的Web应…

idea打开项目白屏

解决方法&#xff1a; 右键“最大化” idea打开项目白板解决方案_idea打开白屏-CSDN博客 IDEA 2022 CPU占用100%的问题及解决方法_java_脚本之家

STM32控制数码管从0显示到99

首先 先画电路图吧&#xff01;打开proteus&#xff0c;导入相关器件&#xff0c;绘制电路图。如下&#xff1a;&#xff08;记得要保存啊&#xff01;发现模拟一遍程序就自动退出了&#xff0c;有bug&#xff0c;我是解决不了&#xff0c;所以就是要及时保存&#xff0c;自己重…

计算机组成原理(10)----微程序控制器

目录 1.微程序控制器的设计思想 2.微指令的基本格式 3.微程序控制器的基本结构 &#xff08;1&#xff09;控制存储器CM &#xff08;2&#xff09;CMAR &#xff08;3&#xff09;地址译码 &#xff08;4&#xff09;CMDR &#xff08;5&#xff09;微地址形成部件 &…

31.云原生Istio可观测性之官网Bookinfo应用实战演示

云原生专栏大纲 文章目录 可观测性kiali介绍Overview&#xff08;概观&#xff09;Application&#xff08;应用维度&#xff09;workloads&#xff08;负载维度&#xff09;Services&#xff08;服务维度&#xff09;Istio Config&#xff08;配置维度&#xff09; Kiali部署…

音频声波的主观感受

一、响度 声压是“客观”的&#xff0c;响度是“主观”的。 响度又称音量。人耳感受到的声音强弱&#xff0c;它是人对声音大小的一个主观感觉量。响度的大小决定于声音接收处的波幅&#xff0c;就同一声源来说&#xff0c;波幅传播的愈远&#xff0c;响度愈小…

React18原理: React核心对象之Update、UpdateQueue、Hook、Task对象

Update 与 UpdateQueue 对象 1 ) 概述 在fiber对象中有一个属性 fiber.updateQueue是一个链式队列&#xff08;即使用链表实现的队列存储结构&#xff09;是和页面更新有关的 2 &#xff09;Update对象相关的数据结构 // https://github.com/facebook/react/blob/v18.2.0/pa…

【Nginx】Nginx配置反向代理 和 https

nginx.conf配置 进入linux /etc/nginx/ 打开nginx.conf 进行以下配置 http {include mime.types;default_type application/octet-stream;sendfile on;keepalive_timeout 65;server {#监听443端口listen 443 ssl;#你的域名server_name huiblog.top;#ssl证书的pe…

VSCode The preLaunchTask ‘C/C++: clang++ 生成活动文件‘ terminated with exit code -1

更改tasks.json文件里面的type为shell 选择g 选择g&#xff0c;然后点回到text.c&#xff0c;按下F5. 得到结果。 文中内容参考: 从零开始手把手教你配置属于你的VS Code_哔哩哔哩_bilibili https://blog.csdn.net/qq_63872647/article/details/128006861

【EasyV】QGIS转换至EasyV

QGIS转换至EasyV 第一步&#xff1a;导入QGIS第二步 坐标系转换第三步 集合修正第四步 重命名字段第五步 导出WGS geojson坐标第六步 导入EasyV 第一步&#xff1a;导入QGIS 第二步 坐标系转换 第三步 集合修正 第四步 重命名字段 第五步 导出WGS geojson坐标 第六步 导入EasyV…

【es6】模版字面量/模版字符串,标签函数/String.raw()静态方法

模版字符串经常用&#xff0c;但是这个标签函数的功能你肯定不知道&#xff0c;请看官网文档 看完你需要知道 可以自定义标签函数String.raw 的用法 唯一一个内置的模版字符串标签函数第一个参数具有 raw 属性的对象&#xff0c;值时一个类数组字符串对象模版字面量的缓存机制…

【vue vue-seamless-scroll】解决vue-seamless-scroll鼠标悬浮才滚动或者只滚动一次就失效的问题

解决问题&#xff1a;使用vue-seamless-scroll发现只有鼠标悬浮上去才滚动&#xff0c;而且滚动一次停止了 目标效果&#xff1a; 解决方案&#xff1a; 最后发现是因为数据需要在页面挂载好就赋值&#xff0c;否则页面在加载完成后&#xff0c;数据无法自动滚动。但因为数据…

c++:蓝桥杯的基础算法2(构造,模拟)+练习巩固

目录 构造 构造的基础概念&#xff1a; 模拟 练习1&#xff1a;扫雷 练习2&#xff1a;灌溉 练习3&#xff1a;回文日期 构造 构造的基础概念&#xff1a; 构造算法是一种用于解决特定问题的算法设计方法。在C语言中&#xff0c;构造算法通常涉及到创建一个函数或类来实…

ARM服务器上部署zookeeper集群

由于ARM服务器上部署zookeeper集群,会存在加载不到主类问题,现在把遇到的问题进行总结下,问题如下: [rootnode206 apache-zookeeper-3.5.10]# bin/zkServer.sh start ZooKeeper JMX enabled by default Using config: /data1/software/apache-zookeeper-3.5.10/bin/../conf/…

四、Burpsuite工具之proxy模块详解-intercept功能

前言&#xff1a; 过了一个年&#xff0c;感觉好久都没有更新了&#xff0c;今天就从burpsuite的更新开始吧。 前面已经说过了burpsuite的安装和proxy代理的配置&#xff0c;今天说一下proxy模块中非常有用的intercept功能。 intercept功能介绍&#xff1a; intercept是拦截…

Spring之AOP源码解析(中)

前言 在上一篇文章中,我们讲解了Spring中那些注解可能会产生AOP动态代理,我们通过源码发现,完成AOP相关操作都和ProxyFactory这个类有密切关系,这一篇我们将围绕这个类继续解析 演示 作用 ProxyFactory采用策略模式生成动态代理对象,具体生成cglib动态代理还是jdk动态代理,…

算法提升——LeetCode第385场周赛总结

题目 统计前后缀下标对 I 给你一个下标从0开始的字符串数组words。 定义一个布尔函数isPrefixAndSuffix&#xff0c;它接受两个字符串参数str1和str2&#xff1a; 当str1同时是str2的前缀&#xff08;prefix&#xff09;和后缀&#xff08;suffix&#xff09;时&#xff0c…

APP的UI自动化demo(appium+java)

文章目录 appium连接手机java代码实现-第一版第二版-接入testng和隐式等待显示等待 appium连接手机 准备工作 1、查看连接手机模拟器是否连接成功&#xff0c;获取设备名称 执行命令&#xff1a;adb devices 2、查看android内核版本号—>paltformVersion 执行命令&#xf…

MQL语言实现单元测试

文章目录 一、单元测试是什么二、单元测试的过程三、为什么需要单元测试四、MQL测试代码实现 一、单元测试是什么 单元测试是对软件中最小可测单元&#xff08;如类或函数&#xff09;进行独立验证和检查的过程。它是由开发工程师完成的&#xff0c;旨在确保每个单元的功能和逻…