angular 跨平台&dom操作&组件嵌套&投影

2023-11-13

angular 跨平台

angular 是跨平台的,不仅仅可以再pc端运行。

anulgar 为跨平台做的工作

为了能够支持跨平台,Angular 通过抽象层封装了不同平台的差异。比如定义了抽象类 Renderer2 、抽象类 RootRenderer 等。此外还定义了以下引用类型:ElementRef、TemplateRef、ViewRef 、ComponentRef 和 ViewContainerRef 等。通过 模板变量、@ViewChild 等方法获取DOM元素。

不要使用window、 document、 navigator等浏览器特有的类型以及直接操作DOM元素。

Render2

Render2 是angular中用于操作dom的,Angular做了封装,屏蔽底层差异,通用性更强。不仅仅可以用于浏览器端,还可以用于Server Side rendering, Web-Worker, mobile apps, and desktop apps等。

Render2定义

class Renderer2 {

get data: {...}

destroyNode: ((node: any) => void) | null

destroy(): void

createElement(name: string, namespace?: string | null): any // 创建元素

createComment(value: string): any // 创建注释元素

createText(value: string): any // 创建文本元素

appendChild(parent: any, newChild: any): void // 添加子元素(在最后)

insertBefore(parent: any, newChild: any, refChild: any): void // 添加子元素(在最前)

removeChild(parent: any, oldChild: any): void // 移除子元素

selectRootElement(selectorOrNode: string | any): any // 获取根元素

parentNode(node: any): any // 获取父元素

nextSibling(node: any): any // 获取下一个兄弟元素

setAttribute(el: any, name: string, value: string, namespace?: string | null): void // 设置属性

removeAttribute(el: any, name: string, namespace?: string | null): void // 移除属性

addClass(el: any, name: string): void // 添加样式类

removeClass(el: any, name: string): void // 移除样式类

setStyle(el: any, style: string, value: any, flags?: RendererStyleFlags2): void // 设置样式

removeStyle(el: any, style: string, flags?: RendererStyleFlags2): void // 移除样式

setProperty(el: any, name: string, value: any): void // 设置DOM对象属性,不同于元素属性

setValue(node: any, value: string): void // 设置元素值

listen(target: 'window' | 'document' | 'body' | any, eventName: string, callback: (event: any) => boolean | void): () => void // 注册事件

}

demo

import {Component, ElementRef, OnInit, Renderer2, AfterViewInit} from '@angular/core';

// http://localhost:4200/demo/render2

@Component({
  selector: 'app-render2',
  templateUrl: './render2.component.html',
  styleUrls: ['./render2.component.less']
})
export class Render2Component implements OnInit, AfterViewInit {

  constructor(
    private renderer: Renderer2,
    private elementRef: ElementRef,
  ) { }

  private createEle: Element;
  private displayEle: Element;

  ngOnInit(): void {

  }

  ngAfterViewInit(): void {
    this.displayEle = this.elementRef.nativeElement.querySelector('#display');
  }

  // createElement(name: string, namespace?: string | null): any // 创建元素
  public createElement() {
    this.createEle = this.renderer.createElement('span');

  }

  // appendChild(parent: any, newChild: any): void // 添加子元素(在最后)
  public appendChild() {
    this.renderer.appendChild(this.displayEle, this.createEle);
  }

  // setStyle(el: any, style: string, value: any, flags?: RendererStyleFlags2): void // 设置样式
  public setStyle() {
    this.renderer.setStyle(this.createEle, 'display', 'inline-block');
    this.renderer.setStyle(this.createEle, 'width', '100px');
    this.renderer.setStyle(this.createEle, 'height', '100px');
    this.renderer.setStyle(this.createEle, 'border', '1px solid black');
  }

  // setAttribute(el: any, name: string, value: string, namespace?: string | null): void // 设置属性
  public setAttribute() {
    this.renderer.setAttribute(this.createEle, 'class', 'create-ele-bg');
  }

  // addClass(el: any, name: string): void // 添加样式类
  public addClass() {
    this.renderer.addClass(this.createEle, 'create-ele');
  }

  // listen(target: 'window' | 'document' | 'body' | any, eventName: string, callback: (event: any) => boolean | void): () => void // 注册事件
  public listen() {
    this.renderer.listen(this.createEle, 'mouseover', () => {
      this.renderer.setStyle(this.createEle, 'background-color', 'yellow');
    });

    this.renderer.listen(this.createEle, 'mouseleave', () => {
      this.renderer.setStyle(this.createEle, 'background-color', 'green');
    })
  }

}

<button class="mg-rm" (click)="createElement()">创建元素</button>
<button class="mg-rm" (click)="appendChild()">添加子元素</button>
<button class="mg-rm" (click)="setStyle()">设置样式</button>
<button class="mg-rm" (click)="setAttribute()">设置属性</button>
<button class="mg-rm" (click)="addClass()">添加样式类</button>
<button class="mg-rm" (click)="listen()">注册事件</button>


<h2>display</h2>

<div id="display">

</div>
.mg-rm  {
  margin-right: 20px;
}

.create-ele-bg {
  background-color: #999;
}

.create-ele {
  border: 1px solid blue !important;
}

ElementRef

  • ElementRef 可以封装不同平台下视图层中的 native 元素 (在浏览器环境中,native 元素通常是指 DOM 元素)。
  • 它表示宿主元素(指令),当前组件元素(组件),
  • nativeElement是他的唯一属性
  • 该对象提供的方法和属性可以用来操作DOM元素及其指令

demo

Renderer2可以对元素执行的操作,可以借助elementref执行

import {AfterViewInit, Component, ElementRef, OnInit, Renderer2} from '@angular/core';

// http://localhost:4200/demo/element

@Component({
  selector: 'app-element',
  templateUrl: './element.component.html',
  styleUrls: ['./element.component.less']
})
export class ElementComponent implements OnInit, AfterViewInit {

  constructor(
    private renderer: Renderer2,
    private elementRef: ElementRef,
  ) { }

  private createEle: Element;
  private displayEle: Element;

  ngOnInit(): void {

  }

  ngAfterViewInit(): void {
    this.displayEle = this.elementRef.nativeElement.querySelector('#display');
  }

  // createElement(name: string, namespace?: string | null): any // 创建元素
  public createElement() {
    this.createEle = this.renderer.createElement('span');

  }

  // 添加子元素(在最后)
  public appendChild() {
    this.displayEle.appendChild(this.createEle)
  }

  // 设置样式
  public setStyle() {
    this.elementRef.nativeElement.querySelector('span').style.display = 'inline-block';
    this.elementRef.nativeElement.querySelector('span').style.width = '100px';
    this.elementRef.nativeElement.querySelector('span').style.height = '100px';
    this.elementRef.nativeElement.querySelector('span').style.border = '1px solid black';
  }

  // 设置属性
  public setAttribute() {
    this.elementRef.nativeElement.querySelector('span').setAttribute('class', 'create-ele-bg');
  }

  // 添加样式类
  public addClass() {
    this.elementRef.nativeElement.querySelector('span').classList.add('create-ele');
  }

  // 注册事件
  public listen() {
    this.elementRef.nativeElement.querySelector('span').addEventListener('mouseover', () => {
      this.elementRef.nativeElement.querySelector('span').style.backgroundColor = 'yellow';
    });

    this.elementRef.nativeElement.querySelector('span').addEventListener('mouseleave', () => {
      this.elementRef.nativeElement.querySelector('span').style.backgroundColor = 'green';
    });
  }

}

ViewContainerRef & TemplateRef

  • ViewContainerRef 对象用于管理试图容器(view container)的内容,它属于HTML文档的一部分,也就是ng-template元素出现的区域。视图容器负责管理视图(view)的集合。视图是包含指令、绑定和表达式的Html元素区域,而视图的创建管理是通过ViewContainerRef类提供的方法和属性来完成的
  • ViewContainerRef 表示 ng-template 元素在 HTML文档中占用的地方,用于创建和管理内嵌视图或组件视图。
  • TemplateRef 表示 ng-template元素的内容,是封装后的nativeElement

demo

import {Component, OnInit, TemplateRef, ViewChild, ViewContainerRef} from '@angular/core';

// http://localhost:4200/demo/container

@Component({
  selector: 'app-container-template',
  templateUrl: './container-template.component.html',
  styleUrls: ['./container-template.component.less']
})
export class ContainerTemplateComponent implements OnInit {

  constructor() { }

  @ViewChild('template') template: TemplateRef<any>;
  @ViewChild('template', { read: ViewContainerRef } ) container: ViewContainerRef;
  
  ngOnInit(): void {}

  // window.testShow()
  public show() {
    this.container.createEmbeddedView(this.template);
  }

  // window.testHide()
  public hide() {
    this.container.clear();
  }

}
<button (click)="show()" style="margin-right: 20px;">show</button>
<button (click)="hide()">hide</button>

<h2>display</h2>

<div id="display" style="border: 1px solid yellow;width: 100px; height: 100px;">
  <ng-template #template>
    <span style="display: inline-block;color: blue;">template</span>
  </ng-template>
</div>

@ViewChild & @ContentChild

  • ViewChild: 属性装饰器,用于配置一个视图查询。 变更检测器会在视图的 DOM 中查找能匹配上该选择器的第一个元素或指令。 如果视图的 DOM 发生了变化,出现了匹配该选择器的新的子节点,该属性就会被更新。
  • ContentChild: 用于配置内容查询的参数装饰器。用于从内容 DOM 获取与此选择器匹配的第一个元素或指令。如果内容 DOM 发生了更改,并且有一个新的子项与选择器匹配,则该属性将被更新。
  • 区别点:
  1. 时机:ViewChild在ngAfterViewInit的回调中调用;ContentChild在ngAfterContentInit的回调用调用
  2. ViewChild从模板中获取内容;ontentChild需要从ng-content中投影的内容中获取内容,也就是没有使用ng-content投影就无法获取内容

demo1

import {Component, ContentChild, OnInit, ViewChild, AfterViewInit, AfterContentInit} from '@angular/core';

// http://localhost:4200/demo/child

@Component({
  selector: 'app-view-content-child',
  templateUrl: './view-content-child.component.html',
  styleUrls: ['./view-content-child.component.less']
})
export class ViewContentChildComponent implements OnInit, AfterViewInit, AfterContentInit {

  constructor() { }

  @ViewChild('template') viewChild: any;
  @ContentChild('template') contentChild: any;


  ngOnInit(): void {
  }

  ngAfterViewInit(): void {
    console.log('---------------------');
    console.log('ngAfterViewInit ');
    console.log(this.viewChild);
  }

  ngAfterContentInit(): void {
    console.log('---------------------');
    console.log('ngAfterContentInit ');
    console.log(this.contentChild);
  }

}
<h2>display</h2>

<div id="display">
  <div #template>
    this is a test.
  </div>
</div>

组件镶嵌

在组件交互中,组件之间的镶嵌一般有两种方式:

  • 在创建父组件时将子组件直接写在模版中
  • 子组件通过投影方式嵌入父级组件,通过 ng-content 形式。

在这两种情况下,如果我们需要访问子组件的公开属性或方法,就需要使用 @ViewChild 与 @ContentChild 装饰器了。他们依次代表上面的两种情况,具体使用如下。

  • ViewChild: 从父组件获取子组件
  • ContentChild: 从子组件中获取父组件传递的内容
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

angular 跨平台&dom操作&组件嵌套&投影 的相关文章

随机推荐

  • [转] PyTorch 0.4新版本 升级指南 no_grad

    转自PyTorch 0 4新版本 升级指南 博主为ShellCollector PyTorch 0 4新版本 升级指南 PyTorch 终于从0 3 1升级到0 4 0了 首先引入眼帘的 是PyTorch官方对自己的描述的巨大变化 PyTo
  • K8s如何在不重新打版本号的情况 更新镜像

    1 重新部署的时候 打上版本号 是不会默认将镜像更新到最新 如果不打版本号 v3 0 11改为latest make docekr push会将镜像更新到最新 2 解决方法 手动将镜像拉到最新 1 用SecureCRT登录上k8s 找到部署
  • YouTube 的视频推荐算法

    转载 https www zhihu com question 20829671 answer 205421638 第一阶段 基于User Video图游历算法 2008年 1 在这个阶段 YouTube认为应该给用户推荐曾经观看过视频的同
  • C++ replace用法

    replace算法 replace函数包含于头文件 include中 泛型算法replace把队列中与给定值相等的所有值替换为另一个值 整个队列都被扫描 即此算法的各个版本都在 线性时间内执行 其复杂度为O n 即replace的执行要遍历
  • C#增删查改

    C 代码都是做后台数据处理的 它将浏览器与数据库互通形成一个动态数据的平台 而C 对于数据处理方式最多的就是查询 新增 修改 删除 以这四个方面为主做数据的处理 根据实际不同的使用 对这四个方法的使用难度也不一样 一 查询 查询方法使用是最
  • 50 亿观众的 “云上奥运”,顶级媒体背后的数智化力量

    东京 2020 奥运会即将闭幕 本届奥运会由于疫情限制 东京地区赛事以无观众的空场形式举行 在无法亲临现场的情况下 全球观众首次以 云上 方式观看奥运 云上奥运 该如何保证赛事的生动性和现场感 缩短观众与赛场之间的距离 随时随地捕捉精彩赛事
  • pydantic学习与使用-8.required-fields必填字段省略号( ...)

    前言 必填字段可以仅用注释来声明 也可以使用省略号 作为值 必填字段 必填字段 可以仅用注释来声明 以下name和age2个字段是必填字段 from pydantic import BaseModel class User BaseMode
  • 社区团购的运营模式是什么?

    社区团购是一种近年来兴起的新型电商模式 它通过社区的力量 以线上线下联动的方式将消费者聚集起来 以优惠的价格和更好的商品为社区居民提供服务 这种模式能够更好地满足社区居民的需求 并且可以有效地提高社区居民的生活质量 运营模式主要分为以下几个
  • 不懂23种设计模式?别灰心,这份核心笔记来帮你,你想知道的都在这里!

    设计模式是软件工程中各种常见问题的经典解决方案 设计模式不只是代码 而是组织代码的方式 假设一行行的代码是砖 设计模式就是蓝图 什么是设计模式 设计模式是解决问题的一种思想 和语言无关 在面向对象软件设计的工程中 针对特定的问题简洁优雅的一
  • Java实现五子棋小游戏(附思路讲解,全部代码,游戏截图)

    本文章是如何实现一个单机版双人五子棋小游戏 通过Swing技术进行可视操作 个人简介 个人主页 码云不秃头 本人是一名大三学生 马上就要变成考研狗啦 通过一学期对Java学习 经过老师的教学 实现单机版的双人五子棋小游戏 大家互相学习 也同
  • 一个web app有多主题,多环境

    在一个web app应用中 需要有多个运行环境 并且每个运行环境主题也是不一样 本项目解决方案 import http es6 ruanyifeng com docs module import ES6 import 可以动态加载 可以利用
  • Extjs入门

    1 什么是Extjs Ext JS 是一个强大的JavaScript类库 提供了丰富且美观的UI组件 和easyUI类似 但更强大 因而使用了Ext JS 您需要写的代码基本上是JavaScript 不需要写HTML 它主要用于创建前端用户
  • LLM推理部署(一):LLM七种推理服务框架总结

    自从ChatGPT发布以来 国内外的开源大模型如雨后春笋般成长 但是对于很多企业和个人从头训练预训练模型不太现实 即使微调开源大模型也捉襟见肘 那么直接部署这些开源大模型服务于企业业务将会有很大的前景 本文将介绍七中主流的LLM推理和服务开
  • 数字电路和模拟电路-10时序逻辑电路的分析和设计

    前言 学习同步时序逻辑电路的分析 设计 一 同步时序逻辑电路的分析 1 时序逻辑电路的分析步骤 步骤一 逻辑图 同步or异步 计数器or状态机 一条总线同步 多条总线是异步 计数器无输入 状态机有输入 状态机还分摩尔型和米里型 步骤二 驱动
  • zotero配置

    1 下载安装 2 配置坚果云同步 编辑 首选项 同步 输入zotero账户密码进行数据同步 文件同步选择坚果云同步 3 配置茉莉花插件 安装pdftk
  • C++-函数模板特化如何避免重复定义

    本文转自 https www cnblogs com dracohan p 3401660 html 转来收藏以便查阅 感谢原作者 另一篇相关博文 https blog csdn net shixin 0125 article detail
  • 【Tensorflow 2.12 电影推荐系统之排序模型】

    Tensorflow 2 12 电影推荐系统之排序模型 学习笔记 导入相关模块 准备数据 加载数据 数据预处理 获取词汇表 构建模型 定义评分排序模型 定义损失函数以及模型评估指标 定义完整的评分排序模型 训练和评估 创建排序模型实例 缓存
  • 2022年大厂Android高级面试题分享,安卓Apk安装过程

    现在的IT行业竞争压力越来越大 尤其是Android开发行业 而很多Android程序员却每天都在重复CRUD 原地徘徊 今年年初 你就想改变现状 于是在网上刷了大量面试题 强行记下之后 开始参加面试 但是你发现 现在的面试 却越来越难了
  • 2017.03 JAVA 面试题 中高级

    2017年3月份 从北京跳槽来到深圳 各种面试 面试的大部分公司都发了offer 现整理出面试的问答题目 如下 一 基础知识 1 集合类 List和Set比较 各自的子类比较 ArrayList Vector LinkedList Hash
  • angular 跨平台&dom操作&组件嵌套&投影

    angular 跨平台 angular 是跨平台的 不仅仅可以再pc端运行 anulgar 为跨平台做的工作 为了能够支持跨平台 Angular 通过抽象层封装了不同平台的差异 比如定义了抽象类 Renderer2 抽象类 RootRend