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

news2025/3/13 3:14:05

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

之前的笔记:

  • [Angular 基础] - Angular 渲染过程 & 组件的创建

  • [Angular 基础] - 数据绑定(databinding)

  • [Angular 基础] - 指令(directives)

    以上为静态页面,即不涉及到跨组件交流的内容

    以下涉及到组件内的沟通,从这开始数据就“活”了

  • [Angular 基础] - 自定义事件 & 自定义属性

下面的例子依旧会沿用 [Angular 基础] - 自定义事件 & 自定义属性 这里创建的项目

视图封装(view encapsulation)

在 [Angular 基础] - Angular 渲染过程 & 组件的创建 中曾经提到过 CSS 的作用域为当前组件,这是因为 Angular 实现的 view encapsulation。

这个部分可以在 @Component 中修改,如:

@Component({
  selector: 'my-component',
  template: `
    <p>My Component</p>
  `,
  encapsulation: ViewEncapsulation.Emulated // default
})

Angular 的 view encapsulation 有 3 个值:Emulated, NoneShadowDom

Emulated

这也是 Angular 默认的实现,在这个实现里,Angular 会为当前组件增添独特的属性,这样当前组件的 CSS 只能绑定于当前的组件上,是一个对 shadow dom 的拟态实现,如下:

在这里插入图片描述

注意这里的 _ngcontent-hash-value,这就是 Angular 随机生成的属性名称,有且只会作用于当前组件上。我这里搜索的是对应的属性名称,可以看到整个 app-server-element 下的 HTML 标签都共享同一个属性名称,无论是 server 还是 blueprint,只要是被 app-server-element 渲染的,都是如此:

在这里插入图片描述

None

None 代表着 Angular 将不会提供任何的 view encapsulation。

如修改一下 app.component.ts,将其 view encapsulation 修改为 None,同时为 p 标签增加颜色:

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
  encapsulation: ViewEncapsulation.None,
})
p {
  color: blue;
}

其效果如下:

在这里插入图片描述

鉴于该样式写在 app.component.css 中,会被所有的组件访问,因此它会成为所有 p 标签的默认样式,一直到被覆盖为止

ShadowDom

这个使用方法就是仰仗原生浏览器去实现 Shadow DOM,这里将其添加到 app-server-elemen 中:

@Component({
  selector: 'app-server-element',
  templateUrl: './server-element.component.html',
  styleUrl: './server-element.component.css',
  encapsulation: ViewEncapsulation.ShadowDom,
})

实现效果如下:

在这里插入图片描述

可以看到,不仅 Angular 没有新增对应的属性,并且其他的样式也消失不见了。这是因为对于浏览器来说,CSS 库不会自动被应用的,因此如果要使用 CSS 库的话,要么手动导入,要么重新实现一下,或者使用 JS 动态绑定

前者依旧会引入大量的重复,因此在常见的 2C 项目中是一个比较少见的实现,比较常规的使用是在自己写库的时候会用到

局部引用(local reference)

local reference 在 [Angular 基础] - 指令(directives) 中的 ngIf 中出现过:

<p *ngIf="serverCreated; else noServer">
  Sever was created, server name is {{ serverName }}
</p>
<ng-template #noServer>
  <p>No server was created!</p>
</ng-template>

其中 #noServer 就是对 ng-template 的 local reference,这里起到的作用就是可以让 Angular 直接在 else 这个条件中获取 <ng-template #noServer> <p>No server was created!</p> </ng-template> 这个元素。

这个做法其实和 React 中的 ref 的作用有些相似,比如说以当前的代码为例,在 cockpit 中获取 server name 和 server content 用的方法是双向绑定,也就是这样的语法 [(ngModel)]="newServerName",但是如果不需要追踪这个值的变化,只需要在点击提交的时候获取元素中的值,则是可以通过 local reference 去实现:

  • V 层修改

    <!-- <input type="text" class="form-control" [(ngModel)]="newServerName" /> -->
    <input type="text" class="form-control" #serverNameInput />
    <!-- 省略若干实现,注意这里传的值 -->
    <button class="btn btn-primary" (click)="onAddServer(serverNameInput)">
      Add Server
    </button>
    

    其中需要注意的一点就是,local reference 只能在 View 层中传递,它无法 直接 在 VM 层中被访问

  • VM 层修改

    export class CockpitComponent {
      onAddServer(nameInput: HTMLInputElement) {
        console.log(nameInput);
    
        console.dir(nameInput);
      }
    }
    

    输出结果为:

    在这里插入图片描述

Local Reference 的主要用法如下:

  • 直接访问 DOM 元素

  • 直接访问子元素

    这点下面会提到怎么实现

  • 搭配 structural directives 进行条件渲染

  • 获取第三方库中的值

父子组件间内容的访问与投射

[Angular 基础] - 自定义事件 & 自定义属性 是父子组件中的属性与事件的交流,这里是内容 (content, DOM) 之间的投射与访问

@ViewChild

虽然说是父组件访问子组件的方法,不过也可以用在同组件的 VM 层和 V 层

获取 Element Ref

具体的方法也是通过绑定 local reference 和 @ViewChild decorator 去实现,代码如下:

  • V 层

    这里的修改和 local reference 中对 V 层的修改类似

    <input type="text" class="form-control" #serverContentInput />
    
  • VM 层

    export class CockpitComponent {
      @ViewChild('serverContentInput', { static: true })
      serverContentInput: ElementRef;
    
      onAddServer(nameInput: HTMLInputElement) {
        console.log(this.serverContentInput);
      }
    }
    

输出结果如下:

在这里插入图片描述

在这里插入图片描述

⚠️:和直接使用 local reference 不同,这里创造的是一个 ElementRef

父组件获取子组件

这里只有 VM 层的修改,在 app.component.ts 中添加一下代码:

export class AppComponent {
  @ViewChild(CockpitComponent, { static: true })
  cockpitComponent: CockpitComponent;

  onServerAdded(serverData: Omit<ServerElement, 'type'>) {
    console.log(this.cockpitComponent);
  }
}

输出结果如下:

在这里插入图片描述

⚠️:这个方法只能获取第一个 instance,如果有多个子组件,可以用 @ViewChildren 进行实现,这个用法暂时不会涉及,等到用到时再补充

❗: 一般不推荐用这种方法去访问/获取 V 层的数据

投射内容

这里是在父元素中渲染一个 placeholder,随后等数据接收完毕后,让子元素重写这个 placeholder,以当前项目为例,目前 server-element 渲染的内容为:

<p>
  <strong *ngIf="aliasElement.type === 'server'" style="color: red"
    >{{ aliasElement.content }}</strong
  >
  <em *ngIf="aliasElement.type === 'blueprint'">{{ aliasElement.content }}</em>
</p>

这样的数据是在 child component 中处理的,不过在有些情况下,对应的数据处理可能需要在父组件完成,而不是通过传递 props 在子组件中进行二次检查——尤其很多时候需要传递 onclick, onsubmit 这种点击事件到子组件中,但是逻辑处理依旧存在于父组件里就会显得比较麻烦——也是可以实现的,如这里将 p 标签的渲染改放到父组件中:

<app-server-element
  *ngFor="let serverElement of serverElements"
  [element]="serverElement"
>
  <p>
    <strong *ngIf="serverElement.type === 'server'" style="color: red"
      >{{ serverElement.content }}</strong
    >
    <em *ngIf="serverElement.type === 'blueprint'"
      >{{ serverElement.content }}</em
    >
  </p>
</app-server-element>

不过直接这么修改会导致数据丢失:

在这里插入图片描述

这时候,需要在 server-element 中放置一个 ng-content 的指令(directive),这样当前组件就会接受从父组件传来的 内容(content),并且将其投射出来,现在的 server-element 代码如下:

<div class="panel panel-default">
  <div class="panel-heading">{{ aliasElement.name }}</div>
  <div class="panel-body">
    <ng-content></ng-content>
  </div>
</div>

这一部分相对于 React 来说确实更加的动态,子组件不需要从父组件接受数据——当然,也可以动态绑定属性和事件进行实现

@ContentChild

使用 @ContentChild 可以让子组件访问 父组件投射到子组件中的 内容(content),也就是上面使用 ng-content 进行投射的渲染内容

具体方法如下:

  • 父组件中声明一个 local reference

    这里的实现在 V 层:

    <p #contentParagraph>
      <strong *ngIf="serverElement.type === 'server'" style="color: red"
        >{{ serverElement.content }}</strong
      >
      <em *ngIf="serverElement.type === 'blueprint'"
        >{{ serverElement.content }}</em
      >
    </p>
    
  • 子组件中从 VM 层访问 local reference

    export class ServerElementComponent {
      @ContentChild('contentParagraph', { static: true }) paragraph: ElementRef;
    
      // 这个在 lifecycle 会重新提一下
      ngAfterContentInit() {
        console.log('ngAfterContentInit in ServerElementComponent');
        console.log(this.paragraph, this.paragraph.nativeElement.textContent);
      }
    }
    

    渲染结果如下:

    在这里插入图片描述

⚠️:这个 directive 其实和 @ViewChild/@ViewChildren 一样,也可以用在父组件中获取子组件的投射,并且在 ngAfterContentInit 确认投射完成后做一些对应操作

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

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

相关文章

每日五道java面试题之java基础篇(十一)

目录: 第一题. Java死锁如何避免&#xff1f;第二题. 为什么⽤线程池&#xff1f;解释下线程池参数&#xff1f;第三题. 线程池的底层⼯作原理第四题. ReentrantLock中tryLock()和lock()⽅法的区别第五题. Sychronized和ReentrantLock的区别? 第一题. Java死锁如何避免&#x…

元器件焊盘的PCB处理方式分析与总结

对于高速信号走线的特性阻抗&#xff0c;都需要按照实际要求进行精度控制&#xff0c;所以&#xff0c;任何因设计因素带来的阻抗波动都应该进行优化&#xff0c;如下图所示&#xff0c;为一个12层板设计中的50Ω微带走线&#xff0c;需要在走线之上放置电感&#xff1b; 但是&…

Open CASCADE学习|管道建模

​这是用Open CASCADE Technology (OCCT)库来创建一个管道模型的示例。OCCT是一个开源的几何建模库&#xff0c;广泛应用于CAD/CAM/CAE和其他几何建模应用中。 在下面的代码中&#xff0c;首先创建了一些点&#xff0c;并用这些点来构建B样条曲线&#xff0c;进而创建边(Edges…

openGauss学习笔记-222 openGauss性能调优-系统调优-操作系统参数调优

文章目录 openGauss学习笔记-222 openGauss性能调优-系统调优-操作系统参数调优222.1 前提条件222.2 内存相关参数设置222.3 网络相关参数设置222.4 I/O相关参数设置 openGauss学习笔记-222 openGauss性能调优-系统调优-操作系统参数调优 在性能调优过程中&#xff0c;可以根据…

面试题-01

1、JDK 和 JRE 和 JVM 分别是什么&#xff0c;有什么区别&#xff1f; JDK&#xff08;Java Development Kit&#xff0c;Java 软件开发工具包&#xff09; JDK&#xff08;Java Development Kit&#xff09;&#xff1a;JDK 是 Java 开发⼯具包&#xff0c;包含了编写、编译…

政安晨:【完全零基础】认知人工智能(一)【超级简单】的【机器学习神经网络】 —— 预测机

开个头 很多小伙伴们很想亲近人工智能与机器学习领域&#xff0c;然而这个领域里的核心理论、算法、工具给人感觉都太过“高冷”&#xff0c;让很多小伙伴们望而却步&#xff0c;导致一直无法入门。 如何捅破这层窗户纸&#xff1f; 让高冷的不再高冷&#xff0c;让神秘的不…

jwt+redis实现登录认证

项目环境&#xff1a;spring boot项目 pom.xml引入jwt和redis <!-- jwt --><dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>4.3.0</version></dependency><!-- redis坐标-->…

使用redis-insight连接到服务器上的redis数据库

一、安装redis&#xff1a; 安装 Redis。你可以通过运行下面的命令来使用 yum 安装 Redis&#xff1a; sudo yum install redis 启动 Redis 服务。安装完成后&#xff0c;你可以通过运行下面的命令来启动 Redis 服务&#xff1a; sudo systemctl start redis 设置 Redis 服务…

Eclipse - 查看工程或者文件的磁盘路径

Eclipse - 查看工程或者文件的磁盘路径 1. Help -> Eclipse Marketplace -> Find: Explorer -> Eclipse Explorer 4.1.0 -> Install2. right-click -> Open in ExplorerReferences 1. Help -> Eclipse Marketplace -> Find: Explorer -> Eclipse Explo…

数据结构-邻接矩阵

介绍 邻接矩阵&#xff0c;是表示图的一种常见方式&#xff0c;具体表现为一个记录了各顶点连接情况的呈正方形的矩阵。 假设一共有以下顶点&#xff0c;其连接关系如图所示 那么&#xff0c;怎么表示它们之间的连接关系呢&#xff1f; 我们发现&#xff0c;各条边所连接的都…

春节专题|产业7问:区块链厂商的现在和未来——数字资产厂商

2023转瞬即逝&#xff0c;不同于加密领域沉寂一整年后在年末集中爆发&#xff0c;对于我国的区块链厂商而言&#xff0c;稳中求胜才是关键词&#xff0c;在平稳发展的基调下&#xff0c;产业洗牌也悄无声息的到来。 从产业总体而言&#xff0c;在经过了接近3年的快速发展后&…

Linux命令-netstat

用于端口和服务之间的故障排除 格式&#xff1a;netstat [常用参数] | grep 端口号/进程名称 -n&#xff1a;显示接口和端口的编号 -t&#xff1a;显示TCP套接字 -u&#xff1a;显示UDP套接字 -l&#xff1a;显示监听中的套接字 -p&#xff1a;显示端口对应的进程信息 -a&a…

蓝桥杯:C++队列、优先队列、链表

C普通队列 算法竞赛中一般用静态数组来模拟队列&#xff0c;或者使用STL queue。使用C的STL queue时&#xff0c;由于不用自己管理队列&#xff0c;因此代码很简洁。队列的部分操作如下。 C优先队列 很多算法需要用到一种特殊的队列&#xff1a;优先队列。它的特点是最优数据…

详解自定义类型:枚举与联合体!

目录 ​编辑 一、枚举类型 1.枚举类型的声明 2.枚举类型的优点 3.枚举类型的使用 二、联合体类型(共用体&#xff09; 1.联合体类型的声明 2.联合体的特点 3.相同成员的结构体和联合体的对比 4.联合体大小的计算 5.用联合体判断大小端 三.完结散花 悟已往之不谏&…

从零开始学习数据结构—【链表】—【探索环形链的设计之美】

环形链表 文章目录 环形链表1.结构图2.具体实现2.1.环形链表结构2.2.头部添加数据2.2.1.具体实现2.2.2.测试添加数据 2.3.尾部添加数据2.3.1.具体实现2.3.2.添加测试数据 2.4.删除头部数据2.4.1.具体实现2.4.2.测试删除数据 2.5.删除尾部数据2.5.1.具体实现2.5.2.测试删除数据 …

【leetcode】深搜、暴搜、回溯、剪枝(C++)3

深搜、暴搜、回溯、剪枝&#xff08;C&#xff09;3 一、解数独1、题目描述2、代码3、解析 二、单词搜索1、题目描述2、代码3、解析 三、黄金矿工1、题目描述2、代码3、解析 四、不同路径III1、题目描述2、代码3、解析 一、解数独 1、题目描述 leetcode链接 2、代码 class…

BIG DATA —— 大数据时代

大数据时代 [英] 维克托 迈尔 — 舍恩伯格 肯尼斯 库克耶 ◎ 著 盛杨燕 周涛◎译 《大数据时代》是国外大数据研究的先河之作&#xff0c;本书作者维克托迈尔舍恩伯格被誉为“大数据商业应用第一人”&#xff0c;他在书中前瞻性地指出&#xff0c;大数据带来的信息…

微服务—DSL基础语法与RestClient操作

本博客为个人学习笔记&#xff0c;学习网站&#xff1a;黑马程序员SpringCloud 2021教程 目录 DSL语法 索引库操作 mapping属性 创建索引库 字段拷贝 查询、删除、修改索引库 文档操作 新增文档 查询、删除文档 修改文档 全量修改 增量修改 DSL文档语法小结 Rest…

Mac配置Python3最简单的方法

此文介绍Mac用Anaconda配置Python3 达成效果 能让你目前只装有Python2的Mac装上Python3&#xff0c;同时拥有很多科学计算库 anaconda介绍 anaconda 是一个python的发行版&#xff0c;包括了python和很多常见的软件库, 和一个包管理器conda。常见的科学计算类的库都包含在里…

美国突然致敬中本聪

作者&#xff1a;秦晋 有点看不懂美国的神操作。 2月16日&#xff0c;据《Bitcoin Magazine》报道&#xff0c;比特币的竞争对手、美国参议员伊丽莎白-沃伦对比特币的立场突然180度大转弯。由反对立场转为支持立场。让很多行业媒体出乎意料&#xff0c;甚至惊掉下巴。 报道称&a…