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

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

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

指令概述

Angular有三种指令:

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

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

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

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

属性指令有两种:

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

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

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

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

<p myHighlight>Highlight me!</p>

编写指令代码

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

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

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

<h1>My First Attribute Directive</h1>
<p myHighlight>Highlight me!</p>

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

lib/app_component.dart

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的指令属性吗?很容易忘记! 在浏览器工具中打开控制台,找到如下错误:

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注解装饰。

@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)

final Element _el;

HighlightDirective(this._el);

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

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)

@Input()
String highlightColor;

绑定到@Input属性

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

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

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

<p myHighlight highlightColor="yellow">Highlighted in yellow</p>
<p myHighlight [highlightColor]="'orange'">Highlighted in orange</p>

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

class AppComponent {
  String color = 'yellow';
}

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

<p myHighlight [highlightColor]="color">Highlighted with parent component's color</p>

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

<p [myHighlight]="color">Highlight me!</p>

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

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

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

@Input()
String myHighlight;

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

绑定到@Input别名

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

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

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

@Input('myHighlight')
String highlightColor;

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

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

<p [myHighlight]="color">Highlight me!</p>

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

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

这是指令类的最新版本。

lib/src/highlight_directive.dart

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,如下所示:

<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,使其没有初始值。

class AppComponent {
  String color;
}

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

绑定到第二个属性

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

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

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

@Input()
String defaultColor;

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

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

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

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

<p [myHighlight]="color" defaultColor="violet">
  Highlight me too!
</p>

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

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

写一个函数指令

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

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

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)

<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

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

<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

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

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

import 'package:angular/angular.dart';
import 'package:attribute_directives/app_component.dart';
void main() {
  bootstrap(AppComponent);
}

web/index.html

<!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的输入属性。 你已经看到它没有使用别名:

@Input()
String highlightColor;

它使用别名:

@Input('myHighlight')
String highlightColor;

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

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

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

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

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

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

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

<p [myHighlight]="color">Highlight me!</p>
  • 右边表达式中的颜色属性属于模板的组件。模板及其组件互相信任。color属性不需要@Input注解。
  • 左边的myHighlight属性指的是HighlightDirective的别名属性,而不是模板组件的属性。有信任问题。 因此,指令属性必须带有@Input注解。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏黒之染开发日记

【easeljs】事件汇总

文章说明:为了方便我自己查找easeljs的所有事件,所以我从easeljs的文档里抄过来加上自己的翻译,会慢慢补全,漏了的,错了的,评论一下我会补上去哦。(不...

14120
来自专栏Google Dart

AngularDart4.0 指南- 用户输入 顶

用户的操作,如点击链接,按下按钮,输入文字引发DOM事件。 本页说明如何使用Angular事件绑定语法将这些事件绑定到组件事件处理程序。

24400
来自专栏pangguoming

vue-自定义组件传值

项目中,我们经常会遇到自定义组件传值的问题,方法很多种,但是原理很简单,下述文档总结实际项目中使用的传值方式。

11410
来自专栏我的博客

XML和PHP

1、XML知识 Xml就是可扩展标记语言与html一样,都是通用标记语言。 用于标记电子文件使其具有结构性的标记语言,可以用来标记数据、定义数据类型,是一种允...

37170
来自专栏软件开发

一个小时学会jQuery

前一段时间录了一套关于jQuery的视频分享给大家,可以在下载区下载到,本来想配合文字一起的,后面发现视频+帮助文档也是非常好的学习方法。 一、jQuery简介...

30670
来自专栏从零开始学自动化测试

appium+python自动化48-长按(long_press)

长按操作是经常会遇到的场景,通过driver可以直接调出long_press_keycode方法,但是这个方法是长按手机上某个按钮,比如长按电源键,长按home...

28410
来自专栏mySoul

微信小程序继续入坑指南

上方完成了一次列表渲染,其中index为默认的遍历到的数组的小标,从0开始,item为当前遍历到的数组对应下标的元素。

12680
来自专栏GreenLeaves

Jquer学习之jQuery(function(){})与(function(){})(jQuery)之间的区别

Jquery是优秀的Javascrīpt框架。我们现在来讨论下在 Jquery 中两个页面载入后执行的函数。 $(document).ready(functio...

1K50
来自专栏逸鹏说道

01.Web大前端时代之:HTML5+CSS3入门系列~初识HTML5

Web大前端时代之:HTML5+CSS3入门系列:http://www.cnblogs.com/dunitian/p/5121725.html 文档申明 <!-...

31040
来自专栏前端布道

JavaScript实现简单的双向数据绑定

双向数据绑定简单来说就是UI视图(View)与数据(Model)相互绑定在一起,当数据改变之后相应的UI视图也同步改变。反之,当UI视图改变之后相应的数据也同步...

13130

扫码关注云+社区

领取腾讯云代金券