前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >AngularDart4.0 高级-属性(Attribute)指令 顶

AngularDart4.0 高级-属性(Attribute)指令 顶

作者头像
南郭先生
发布2018-08-14 15:57:07
3.1K0
发布2018-08-14 15:57:07
举报
文章被收录于专栏:Google DartGoogle Dart

属性指令改变DOM元素的外观或行为。

尝试一下实例(查看源代码)。

指令概述

Angular有三种指令:

  1. 组件 - 指令与模板。
  2. 结构指令 - 通过添加和删除DOM元素来更改DOM布局。
  3. 属性(attribute)指令 - 改变元素,组件或其他指令的外观或行为。

组件是三个指令中最常见的。 您在Starter App中看到了一个简单的组件。

结构指令改变了视图的结构。 两个例子是NgForNgIf。 在“结构指令”页面中了解它们。

属性指令被用作元素的属性。 例如,“模板语法”页面中的内置NgStyle指令可以同时更改多个元素样式。

属性指令有两种:

  • 基于类:一个全功能的属性指令,使用类实现。
  • 功能化:无状态属性指令,使用顶层函数实现。

创建一个基于类的属性指令

 创建一个基于类的属性指令需要编写一个用@Directive()注解的控制器类,它指定标识属性的选择器。控制器类实现指令所需的行为。

本页演示了如何构建一个简单的myHighlight属性指令当用户悬停在那个元素上时来设置元素的背景颜色 你可以像这样应用它:

代码语言:javascript
复制
<p myHighlight>Highlight me!</p>

编写指令代码

按照设置说明创建名为attribute_directives的新本地项目。

在指定的文件夹中创建以下源文件:lib/src/highlight_directive.dart

代码语言:javascript
复制
import 'dart:html';

import 'package:angular/angular.dart';

@Directive(selector: '[myHighlight]')
class HighlightDirective {
  HighlightDirective(Element el) {
    el.style.backgroundColor = 'yellow';
  }
}

@Directive()需要一个CSS选择器来标识与该指令相关联的模板中的HTML。属性的CSS选择器是方括号中的属性名称。这里指令的选择器是[myHighlight]。 Angular定位模板中具有名为myHighlight的属性的所有元素。

为什么不叫它“highlight”? 虽然highlight是比myHighlight更简洁的名字,并会工作,最佳做法是为选择器名称加上前缀,以确保它们不与标准HTML属性发生冲突。这也降低了与第三方指令名称相冲突的风险。 请确保您不要对highlight指令名称使用ng前缀,因为该前缀是为Angular保留的,并且使用它可能会导致难以诊断的错误。对于简单的演示,简短的前缀my可以帮助区分您的自定义指令。

在@Directive()元数据之后是指令的控制器类,称为HighlightDirective,它包含指令的逻辑。

Angular为每个匹配元素创建一个指令控制器类的新实例,将HTML元素注入到构造函数中。

应用属性指令

要使用新的HighlightDirective,请创建一个将该指令作为属性应用于段落(<p>)元素的模板。 对Angular来说,<p>元素是属性宿主。

将模板放在它自己的app_component.html文件中,如下所示:lib/app_component.html

代码语言:javascript
复制
<h1>My First Attribute Directive</h1>
<p myHighlight>Highlight me!</p>

现在在AppComponent中引用此模板,并将Highlight指令添加到指令列表中。 当Angular在模板中遇到myHighlight时,就会识别该指令。

lib/app_component.dart
代码语言:javascript
复制
import 'package:angular/angular.dart';

import 'src/auto_id_directive.dart';
import 'src/highlight_directive.dart';

@Component(
  selector: 'my-app',
  templateUrl: 'app_component.html',
  directives: const [autoIdDirective, HighlightDirective],
)
class AppComponent {
  String color;
}

刷新浏览器。 应用程序运行,myHighlight指令突出显示段落文本。

你的指令不工作?

你记得设置@Component的指令属性吗?很容易忘记! 在浏览器工具中打开控制台,找到如下错误:

代码语言:javascript
复制
EXCEPTION: Template parse errors:
Can't bind to 'myHighlight' since it isn't a known property of 'p'.

Angular检测到你正试图绑定到某个东西,但是它找不到这个指令。 您可以通过在directives列表中列出HighlightDirective让Angular知道。

总而言之,Angular在<p>元素上找到了myHighlight属性。它创建了一个HighlightDirective类的实例,并将<p>元素的引用注入到指令的构造函数中,该构造函数将<p>元素的背景样式设置为黄色。

响应用户发起的事件

目前,myHighlight只是设置一个元素的颜色。 该指令可能更具动态性。 它可以检测到用户将鼠标移入或移出元素,并通过设置或清除高亮颜色来进行响应。

添加两个事件处理程序,当鼠标进入或离开时进行响应,每个都由HostListener注解装饰。

代码语言:javascript
复制
@HostListener('mouseenter')
void onMouseEnter() {
  _highlight('yellow');
}

@HostListener('mouseleave')
void onMouseLeave() {
  _highlight();
}

void _highlight([String color]) {
  _el.style.backgroundColor = color;
}

@HostListener注解允许您订阅托管属性指令的宿主DOM元素的事件,在这种情况下是<p>。

当然,你可以用标准的JavaScript访问DOM,并手动添加事件监听器。 这种方法至少有三个问题:

  1. 你必须正确的写下监听器。
  2. 当指令被销毁时,代码必须分离监听器以避免内存泄漏。
  3. 直接与DOM API交互不是最佳实践。

处理程序委托给一个帮助器方法,该方法设置DOM元素_el的颜色,在构造函数中声明并初始化它。

lib/src/highlight_directive.dart (constructor)

代码语言:javascript
复制
final Element _el;

HighlightDirective(this._el);

以下是更新后的指令:lib/src/highlight_directive.dart

代码语言:javascript
复制
import 'dart:html';

import 'package:angular/angular.dart';

@Directive(selector: '[myHighlight]')
class HighlightDirective {
  final Element _el;

  HighlightDirective(this._el);

  @HostListener('mouseenter')
  void onMouseEnter() {
    _highlight('yellow');
  }

  @HostListener('mouseleave')
  void onMouseLeave() {
    _highlight();
  }

  void _highlight([String color]) {
    _el.style.backgroundColor = color;
  }
}

刷新浏览器。 确认当鼠标悬停在p上时出现背景颜色,并在移出时消失。

通过@Input数据绑定将值传入指令

目前,高亮颜色在指令中被硬编码。 这是不灵活的。 在本节中,您将为开发人员提供在应用指令时设置突出显示颜色的能力。

开始通过像这样的指令类添加一个highlightColor属性:lib/src/highlight_directive.dart (highlightColor)

代码语言:javascript
复制
@Input()
String highlightColor;

绑定到@Input属性

注意@Input注解。 它将元数据添加到使指令的highlightColor属性可用于绑定的类。

它被称为输入属性,因为数据从绑定表达式流入指令。 没有这个输入元数据,Angular拒绝绑定; 请参阅下面的更多关于这一点。

尝试通过向AppComponent模板添加以下指令绑定变量:lib/app_component.html (excerpt)

代码语言:javascript
复制
<p myHighlight highlightColor="yellow">Highlighted in yellow</p>
<p myHighlight [highlightColor]="'orange'">Highlighted in orange</p>

将color属性添加到AppComponent。lib/app_component.dart (class)

代码语言:javascript
复制
class AppComponent {
  String color = 'yellow';
}

让它用一个属性绑定来控制高亮颜色。lib/app_component.html (excerpt)

代码语言:javascript
复制
<p myHighlight [highlightColor]="color">Highlighted with parent component's color</p>

这很好,但同时应用指令并将颜色设置为相同的属性会很好。

代码语言:javascript
复制
<p [myHighlight]="color">Highlight me!</p>

[myHighlight]属性绑定都将highlighting 显示指令应用于<p>元素,并使用属性绑定来设置指令的突出显示颜色。您正在重新使用该指令的属性选择器([myHighlight])来执行这两个任务。 这是一个清晰,紧凑的语法。

您必须将指令的highlightColor属性重命名为myHighlight,因为这是现在的颜色属性绑定名称。

lib/src/highlight_directive.dart (renamed to match directive selector)

代码语言:javascript
复制
@Input()
String myHighlight;

这是不愉快的。 myHighlight这个词是一个可怕的财产名称,它并不表达财产的意图。

绑定到@Input别名

幸运的是,您可以根据需要命名指令属性,并将其别名用于绑定目的。

还原原始属性名称,并将选择器指定为@Input参数中的别名。

lib/src/highlight_directive.dart (color property with alias)

代码语言:javascript
复制
@Input('myHighlight')
String highlightColor;

该指令内的属性被称为highlightColor。 在指令之外,绑定到它的地方,它被称为myHighlight。

您可以得到两全其美的效果:所需的属性名称和所需的绑定语法:

代码语言:javascript
复制
<p [myHighlight]="color">Highlight me!</p>

现在你已经绑定了highlightColor,修改了onMouseEnter()方法来使用它。如果有人忽略绑定到highlightColor,以红色突出显示:lib/src/highlight_directive.dart (mouse enter)

代码语言:javascript
复制
@HostListener('mouseenter')
void onMouseEnter() => _highlight(highlightColor ?? 'red');

这是指令类的最新版本。

lib/src/highlight_directive.dart

代码语言:javascript
复制
import 'dart:html';

import 'package:angular/angular.dart';

@Directive(selector: '[myHighlight]')
class HighlightDirective {
  final Element _el;

  HighlightDirective(this._el);

  @Input('myHighlight')
  String highlightColor;

  @HostListener('mouseenter')
  void onMouseEnter() => _highlight(highlightColor ?? 'red');

  @HostListener('mouseleave')
  void onMouseLeave() => _highlight();

  void _highlight([String color]) {
    _el.style.backgroundColor = color;
  }
}

写一个控制装置来尝试

可能很难想象这个指令是如何工作的。在本节中,您将把AppComponent转换为一个线束,让您用单选按钮选取高亮颜色,并将您的颜色选择绑定到指令。

更新app_component.html,如下所示:

代码语言:javascript
复制
<h1>My First Attribute Directive</h1>

<h4>Pick a highlight color</h4>
<div>
  <input type="radio" name="colors" (click)="color='lightgreen'">Green
  <input type="radio" name="colors" (click)="color='yellow'">Yellow
  <input type="radio" name="colors" (click)="color='cyan'">Cyan
</div>
<p [myHighlight]="color">Highlight me!</p>

修改AppComponent.color,使其没有初始值。

代码语言:javascript
复制
class AppComponent {
  String color;
}

刷新浏览器。 这是执行中的线束和指令。

绑定到第二个属性

这个highlight指令有一个可定制的属性。 在一个真正的应用程序,它可能需要更多。

目前,默认的颜色 - 直到用户选择高亮颜色为止的颜色 - 被硬编码为“red”。 让模板开发人员设置默认颜色。

将第二个输入属性添加到HighlightDirective,名为defaultColor:lib/src/highlight_directive.dart (defaultColor)

代码语言:javascript
复制
@Input()
String defaultColor;

修改指令的onMouseEnter,使其首先尝试用highlightColor高亮显示,然后用defaultColor,如果两个属性都是未定义的,则回退到“红色”。

代码语言:javascript
复制
@HostListener('mouseenter')
void onMouseEnter() => _highlight(highlightColor ?? defaultColor ?? 'red');

当您已经绑定到myHighlight属性名称时,如何绑定到第二个属性?

与组件一样,您可以根据需要添加尽可能多的指令属性绑定,方法是在模板中将它们串起来。 开发人员应该能够编写下面的模板HTML绑定到AppComponent.color并回退到“violet”作为默认颜色。

代码语言:javascript
复制
<p [myHighlight]="color" defaultColor="violet">
  Highlight me too!
</p>

Angular知道defaultColor绑定属于HighlightDirective,因为您使用@Input注解将其公开。

刷新浏览器。 编码完成后,下方演示应该如何工作。

写一个函数指令

一个函数指令是一个无状态的指令。 您可以通过使用@Directive()注解一个公共的顶级函数来创建一个函数指令。

创建以下功能属性指令:lib/src/auto_id_directive.dart

代码语言:javascript
复制
import 'dart:html';
import 'package:angular/angular.dart';

int _idCounter = 0;

@Directive(selector: '[autoId]')
void autoIdDirective(
  Element el,
  @Attribute('autoId') String prefix,
) {
  el.id = '$prefix${_idCounter++}';
}

像基于类的指令中的构造函数参数一样,函数参数定义了函数指令的依赖关系。

当您编写功能指令时,请遵循以下规则:

  • 使函数返回类型void。
  • @Directive()注释中,只使用selector参数,必要时使用providers。

虽然函数指令是无状态的,但它们可能是不纯的(利用全局状态),正如autoId指令所示。

在应用程序组件模板的末尾添加以下行:lib/app_component.html (autoId)

代码语言:javascript
复制
<h4 #h1 autoId="heading-">Auto-ID at work</h4>
<p>The previous heading has ID {{h1.id}}</p>

<h4 #h2 autoId="heading-">Auto-ID at work, again</h4>
<p>The previous heading has ID {{h2.id}}</p>

刷新浏览器。 该应用报告标题ID heading-0 和 heading-1。

概要

该页面介绍了如何:

最终的源代码如下:

lib/app_component.dart

代码语言:javascript
复制
import 'package:angular/angular.dart';
import 'src/auto_id_directive.dart';
import 'src/highlight_directive.dart';
@Component(
  selector: 'my-app',
  templateUrl: 'app_component.html',
  directives: const [autoIdDirective, HighlightDirective],
)
class AppComponent {
  String color;
}

lib/app_component.html

代码语言:javascript
复制
<h1>My First Attribute Directive</h1>
<h4>Pick a highlight color</h4>
<div>
  <input type="radio" name="colors" (click)="color='lightgreen'">Green
  <input type="radio" name="colors" (click)="color='yellow'">Yellow
  <input type="radio" name="colors" (click)="color='cyan'">Cyan
</div>
<p [myHighlight]="color">Highlight me!</p>
<p [myHighlight]="color" defaultColor="violet">
  Highlight me too!
</p>
<hr>
<h4 #h1 autoId="heading-">Auto-ID at work</h4>
<p>The previous heading has ID {{h1.id}}</p>
<h4 #h2 autoId="heading-">Auto-ID at work, again</h4>
<p>The previous heading has ID {{h2.id}}</p>

lib/src/auto_id_directive.dart

代码语言:javascript
复制
import 'dart:html';
import 'package:angular/angular.dart';
int _idCounter = 0;
@Directive(selector: '[autoId]')
void autoIdDirective(
  Element el,
  @Attribute('autoId') String prefix,
) {
  el.id = '$prefix${_idCounter++}';
}

lib/src/highlight_directive.dart

代码语言:javascript
复制
import 'dart:html';
import 'package:angular/angular.dart';
@Directive(selector: '[myHighlight]')
class HighlightDirective {
  final Element _el;
  HighlightDirective(this._el);
  @Input()
  String defaultColor;
  @Input('myHighlight')
  String highlightColor;
  @HostListener('mouseenter')
  void onMouseEnter() => _highlight(highlightColor ?? defaultColor ?? 'red');
  @HostListener('mouseleave')
  void onMouseLeave() => _highlight();
  void _highlight([String color]) {
    _el.style.backgroundColor = color;
  }
}

web/main.dart

代码语言:javascript
复制
import 'package:angular/angular.dart';
import 'package:attribute_directives/app_component.dart';
void main() {
  bootstrap(AppComponent);
}

web/index.html

代码语言:javascript
复制
<!DOCTYPE html>
<html>
  <head>
    <script>
      // WARNING: DO NOT set the <base href> like this in production!
      // Details: https://webdev.dartlang.org/angular/guide/router
      (function () {
        var m = document.location.pathname.match(/^(\/[-\w]+)+\/web($|\/)/);
        document.write('<base href="' + (m ? m[0] : '/') + '" />');
      }());
    </script>
    <title>Attribute Directives</title>
    <link rel="stylesheet" href="styles.css">
    <link rel="icon" type="image/png" href="favicon.png">
    <script defer src="main.dart" type="application/dart"></script>
    <script defer src="packages/browser/dart.js"></script>
  </head>
  <body>
    <my-app>Loading...</my-app>
  </body>
</html>

您还可以体验并下载实例(查看源代码)。

附录:为什么要添加@Input?

在这个演示中,hightlightColor属性是HighlightDirective的输入属性。 你已经看到它没有使用别名:

代码语言:javascript
复制
@Input()
String highlightColor;

它使用别名:

代码语言:javascript
复制
@Input('myHighlight')
String highlightColor;

无论哪种方式,@Input注解告诉Angular这个属性是由父组件公开的,并可以进行绑定。没有@Input,Angular拒绝绑定到属性。

您之前已将模板HTML绑定到组件属性,并且从未使用@Input。 有什么不同?

差别是一个信任的问题。 Angular将组件的模板视为属于组件。组件和它的模板隐式互相信任。因此,组件自己的模板可以绑定到该组件的任何属性,无论有没有@Input注解。

但是组件或指令不应该盲目地信任其他组件和指令。 默认情况下,组件或指令的属性是隐式绑定的。从Angular绑定角度来看,它们是私密的。当用@Input注解装饰时,该属性从Angular绑定的角度变成公共的。只有这样它才能受到其他组件或指令的绑定。

您可以通过绑定中属性名称的位置来判断是否需要@Input。

  • 当它出现在等号(=)右边的模板表达式中时,它属于模板的组件,不需要@Input注解。
  • 当它出现在等号(=)左边的方括号([])中时,该属性属于某个其他组件或指令; 该属性必须用@Input注解来修饰。

现在将该推理应用于以下示例:

代码语言:javascript
复制
<p [myHighlight]="color">Highlight me!</p>
  • 右边表达式中的颜色属性属于模板的组件。模板及其组件互相信任。color属性不需要@Input注解。
  • 左边的myHighlight属性指的是HighlightDirective的别名属性,而不是模板组件的属性。有信任问题。 因此,指令属性必须带有@Input注解。
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 指令概述
  • 创建一个基于类的属性指令
    • 编写指令代码
    • 应用属性指令
      • 你的指令不工作?
      • 响应用户发起的事件
      • 通过@Input数据绑定将值传入指令
        • 绑定到@Input属性
          • 绑定到@Input别名
          • 写一个控制装置来尝试
          • 绑定到第二个属性
          • 写一个函数指令
          • 概要
            • 附录:为什么要添加@Input?
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档