Angular4 ElementRef的应用


Posted in Javascript onFebruary 26, 2018

Angular 的口号是 - "一套框架,多种平台。同时适用手机与桌面 (One framework.Mobile & desktop.)",即 Angular 是支持开发跨平台的应用,比如:Web 应用、移动 Web 应用、原生移动应用和原生桌面应用等。

为了能够支持跨平台,Angular 通过抽象层封装了不同平台的差异,统一了 API 接口。如定义了抽象类 Renderer 、抽象类 RootRenderer 等。此外还定义了以下引用类型:ElementRef、TemplateRef、ViewRef 、ComponentRef 和 ViewContainerRef 等。下面我们就来分析一下 ElementRef 类:

ElementRef的作用

在应用层直接操作 DOM,就会造成应用层与渲染层之间强耦合,导致我们的应用无法运行在不同环境,如 web worker 中,因为在 web worker 环境中,是不能直接操作 DOM。有兴趣的读者,可以阅读一下 Web Workers 中支持的类和方法 这篇文章。通过 ElementRef 我们就可以封装不同平台下视图层中的 native 元素 (在浏览器环境中,native 元素通常是指 DOM 元素),最后借助于 Angular 提供的强大的依赖注入特性,我们就可以轻松地访问到 native 元素。

ElementRef的定义

export class ElementRef {
 public nativeElement: any;
 constructor(nativeElement: any) { this.nativeElement = nativeElement; }
}

ElementRef的应用

我们先来介绍一下整体需求,我们想在页面成功渲染后,获取页面中的 div 元素,并改变该 div 元素的背景颜色。接下来我们来一步步,实现这个需求。

首先我们要先获取 div 元素,在文中 "ElementRef 的作用" 部分,我们已经提到可以利用 Angular 提供的强大的依赖注入特性,获取封装后的 native 元素。在浏览器中 native 元素就是 DOM 元素,我们只要先获取 my-app元素,然后利用 querySelector API 就能获取页面中 div 元素。具体代码如下:

import { Component, ElementRef } from '@angular/core'; @Component({
 selector: 'my-app',
 template: `
  <h1>Welcome to Angular World</h1>
  <div>Hello {{ name }}</div>
 `,
}) export class AppComponent {

 name: string = 'Semlinker'; constructor(private elementRef: ElementRef) { let divEle = this.elementRef.nativeElement.querySelector('div'); console.dir(divEle);
 }
}

运行上面代码,在控制台中没有出现异常,但是输出的结果却是 null 。什么情况 ? 没有抛出异常,我们可以推断 this.elementRef.nativeElement 这个对象是存在,但却找不到它的子元素,那应该是在调用构造函数的时候,my-app 元素下的子元素还未创建。那怎么解决这个问题呢 ?沉思中… ,不是有 setTimeout 么,我们在稍微改造一下:

constructor(private elementRef: ElementRef) {
 setTimeout(() => { // 此处需要使用箭头函数哈,你懂的...
   let divEle = this.elementRef.nativeElement.querySelector('div');
   console.dir(divEle);
  }, 0); }

问题解决了,但感觉不是很优雅 ?有没有更好的方案,答案是肯定的。Angular 不是有提供组件生命周期的钩子,我们可以选择一个合适的时机,然后获取我们想要的 div 元素。

import { Component, ElementRef, AfterViewInit } from '@angular/core'; @Component({
 selector: 'my-app',
 template: `
  <h1>Welcome to Angular World</h1>
  <div>Hello {{ name }}</div>
 `,
}) export class AppComponent {
 name: string = 'Semlinker'; // 在构造函数中 this.elementRef = elementRef 是可选的,编译时会自动赋值 // function AppComponent(elementRef) { this.elementRef = elementRef; } constructor(private elementRef: ElementRef) { } 
 ngAfterViewInit() { // 模板中的元素已创建完成 console.dir(this.elementRef.nativeElement.querySelector('div')); // let greetDiv: HTMLElement = this.elementRef.nativeElement.querySelector('div'); // greetDiv.style.backgroundColor = 'red'; }
}

运行一下上面的代码,我们看到了意料中的 div 元素。我们直接选用 ngAfterViewInit 这个钩子,不要问我为什么,因为它看得最顺眼咯。不过我们后面也会有专门的文章,详细分析一下 Angular 组件的生命周期。成功取到 div 元素,就剩下的事情就好办了,直接通过 style 对象设置元素的背景颜色。

功能虽然已经实现了,但还有优化的空间么?

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

@Component({
 selector: 'my-app',
 template: ` <h1>Welcome to Angular World</h1>
  <div #greet>Hello {{ name }}</div> `,
}) export class AppComponent { name: string = 'Semlinker';

 @ViewChild('greet')
 greetDiv: ElementRef;

 ngAfterViewInit() { this.greetDiv.nativeElement.style.backgroundColor = 'red';
 }
}

是不是感觉瞬间高大上了,不过先等等,上面的代码是不是还有进一步的优化空间呢 ?我们看到设置 div 元素的背景,我们是默认应用的运行环境在是浏览器中。前面已经介绍了,我们要尽量减少应用层与渲染层之间强耦合关系,从而让我们应用能够灵活地运行在不同环境。最后我们来看一下,最终优化后的代码:

import { Component, ElementRef, ViewChild, AfterViewInit, Renderer } from '@angular/core'; @Component({
 selector: 'my-app',
 template: `
  <h1>Welcome to Angular World</h1>
  <div #greet>Hello {{ name }}</div>
 `,
}) export class AppComponent {
 name: string = 'Semlinker'; @ViewChild('greet')
 greetDiv: ElementRef; constructor(private elementRef: ElementRef, private renderer: Renderer) { }

 ngAfterViewInit() { // this.greetDiv.nativeElement.style.backgroundColor = 'red'; this.renderer.setElementStyle(this.greetDiv.nativeElement, 'backgroundColor', 'red');
 }
}

1.Renderer API 还有哪些常用的方法 ?

export abstract class Renderer { // 创建元素 abstract createElement(parentElement: any, name: string, 
   debugInfo?: RenderDebugInfo): any; // 创建文本元素 abstract createText(parentElement: any, value: string, 
   debugInfo?: RenderDebugInfo): any; // 设置文本 abstract setText(renderNode: any, text: string): void; // 设置元素Property abstract setElementProperty(renderElement: any, propertyName: string, 
   propertyValue: any): void; // 设置元素Attribute abstract setElementAttribute(renderElement: any, attributeName: string, 
   attributeValue: string): void; // 设置元素的Class abstract setElementClass(renderElement: any, className: string,
   isAdd: boolean): void; // 设置元素的样式 abstract setElementStyle(renderElement: any, styleName: string, 
   styleValue: string): void;  
}

需要注意的是在 Angular 4.x+ 版本,我们使用 Renderer2 替代 Renderer (Angular V2)。

2.Renderer2 API 还有哪些常用的方法 ?

export abstract class Renderer2 { abstract createElement(name: string, namespace?: string|null): any; abstract createComment(value: string): any; abstract createText(value: string): any; abstract setAttribute(el: any, name: string, value: string, namespace?: string|null): void; abstract removeAttribute(el: any, name: string, namespace?: string|null): void; abstract addClass(el: any, name: string): void; abstract removeClass(el: any, name: string): void; abstract setStyle(el: any, style: string, value: any, 
  flags?: RendererStyleFlags2): void; abstract removeStyle(el: any, style: string, flags?: RendererStyleFlags2): void; abstract setProperty(el: any, name: string, value: any): void; abstract setValue(node: any, value: string): void; abstract listen(
   target: 'window'|'document'|'body'|any, eventName: string,
   callback: (event: any) => boolean | void): () => void;
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
JavaScript 学习笔记(十一)
Jan 19 Javascript
ASP.NET jQuery 实例15 通过控件CustomValidator验证CheckBoxList
Feb 03 Javascript
js的匿名函数使用介绍
Dec 11 Javascript
快速掌握Node.js中setTimeout和setInterval的使用方法
Mar 21 Javascript
JS实现把鼠标放到链接上出现滚动文字的方法
Apr 06 Javascript
JavaScript函数柯里化详解
Apr 29 Javascript
JS操作input标签属性checkbox全选的实现代码
Mar 02 Javascript
JavaScript的词法结构精华篇
Oct 17 Javascript
使用Vue.js中的过滤器实现幂方求值的方法
Aug 27 Javascript
JS回调函数 callback的理解与使用案例分析
Sep 09 Javascript
vue.js的状态管理vuex中store的使用详解
Nov 08 Javascript
一篇文章弄清楚Ajax请求的五个步骤
Mar 17 Javascript
vue cli构建的项目中请求代理与项目打包问题
Feb 26 #Javascript
vue-cli webpack模板项目搭建及打包时路径问题的解决方法
Feb 26 #Javascript
vue bus全局事件中心简单Demo详解
Feb 26 #Javascript
详解Angular系列之变化检测(Change Detection)
Feb 26 #Javascript
Bootstrap4如何定制自己的颜色和风格
Feb 26 #Javascript
vue-cli下的vuex的简单Demo图解(实现加1减1操作)
Feb 26 #Javascript
使用vue-cli编写vue插件的方法
Feb 26 #Javascript
You might like
mysql中存储过程、函数的一些问题
2007/02/14 PHP
几款免费开源的不用数据库的php的cms
2010/12/19 PHP
PHP编码规范的深入探讨
2013/06/06 PHP
php获取指定数量随机字符串的方法
2017/02/06 PHP
关于PHP转换超过2038年日期出错的问题解决
2017/06/28 PHP
如何优雅的使用 laravel 的 validator验证方法
2018/11/11 PHP
Javascript 异步加载详解(浏览器在javascript的加载方式)
2012/05/20 Javascript
form表单中去掉默认的enter键提交并绑定js方法实现代码
2013/04/01 Javascript
IE、FF、Chrome浏览器中的JS差异介绍
2013/08/13 Javascript
JavaScript自定义方法实现trim()、Ltrim()、Rtrim()的功能
2013/11/03 Javascript
JavaScript 语言基础知识点总结(思维导图)
2013/11/10 Javascript
Jquery节点遍历next与nextAll方法使用示例
2014/07/22 Javascript
2则自己编写的jQuery特效分享
2015/02/26 Javascript
javascript使用递归算法求两个数字组合功能示例
2017/01/03 Javascript
vue.js国际化 vue-i18n插件的使用详解
2017/07/07 Javascript
clipboard在vue中的使用的方法示例
2018/10/19 Javascript
vue实现随机验证码功能的实例代码
2019/04/30 Javascript
VUE路由动态加载实例代码讲解
2019/08/26 Javascript
微信小程序JS加载esmap地图的实例详解
2019/09/04 Javascript
Vue中rem与postcss-pxtorem的应用详解
2019/11/20 Javascript
JavaScript设计模式---单例模式详解【四种基本形式】
2020/05/16 Javascript
element日历calendar组件上月、今天、下月、日历块点击事件及模板源码
2020/07/27 Javascript
Python的词法分析与语法分析
2013/05/18 Python
Python多线程编程(六):可重入锁RLock
2015/04/05 Python
一篇文章搞懂Python的类与对象名称空间
2018/12/10 Python
python如何给字典的键对应的值为字典项的字典赋值
2019/07/05 Python
django处理select下拉表单实例(从model到前端到post到form)
2020/03/13 Python
python实现飞船游戏的纵向移动
2020/04/24 Python
Python代码中如何读取键盘录入的值
2020/05/27 Python
Keras - GPU ID 和显存占用设定步骤
2020/06/22 Python
利用HTML5的新特点实现图片文件异步上传
2014/05/29 HTML / CSS
加拿大床上用品、家居装饰、厨房和浴室产品购物网站:Linen Chest
2018/06/05 全球购物
Linux Interview Questions For software testers
2013/05/17 面试题
房屋出租协议书范本(标准版)
2014/09/24 职场文书
写给医护人员的一封感谢信
2019/09/16 职场文书
Python使用pandas导入csv文件内容的示例代码
2022/12/24 Python