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

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: 从子组件中获取父组件传递的内容
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值