属性指令改变DOM元素的外观或行为。css
Angular有三种指令:java
组件是三个指令中最多见的。 您在Starter App中看到了一个简单的组件。git
结构指令改变了视图的结构。 两个例子是NgFor和NgIf。 在“结构指令”页面中了解它们。github
属性指令被用做元素的属性。 例如,“模板语法”页面中的内置NgStyle指令能够同时更改多个元素样式。web
属性指令有两种:bootstrap
建立一个基于类的属性指令须要编写一个用@Directive()注解的控制器类,它指定标识属性的选择器。控制器类实现指令所需的行为。api
本页演示了如何构建一个简单的myHighlight属性指令当用户悬停在那个元素上时来设置元素的背景颜色
你能够像这样应用它:浏览器
<p myHighlight>Highlight me!</p>
按照设置说明建立名为attribute_directives的新本地项目。app
在指定的文件夹中建立如下源文件: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时,就会识别该指令。
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,并手动添加事件监听器。 这种方法至少有三个问题:
- 你必须正确的写下监听器。
- 当指令被销毁时,代码必须分离监听器以免内存泄漏。
- 直接与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上时出现背景颜色,并在移出时消失。
目前,高亮颜色在指令中被硬编码。 这是不灵活的。 在本节中,您将为开发人员提供在应用指令时设置突出显示颜色的能力。
开始经过像这样的指令类添加一个highlightColor属性:lib/src/highlight_directive.dart (highlightColor)
@Input() String highlightColor;
注意@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参数中的别名。
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++}'; }
像基于类的指令中的构造函数参数同样,函数参数定义了函数指令的依赖关系。
当您编写功能指令时,请遵循如下规则:
虽然函数指令是无状态的,但它们多是不纯的(利用全局状态),正如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>
在这个演示中,hightlightColor属性是HighlightDirective的输入属性。 你已经看到它没有使用别名:
@Input() String highlightColor;
它使用别名:
@Input('myHighlight') String highlightColor;
不管哪一种方式,@Input注解告诉Angular这个属性是由父组件公开的,并能够进行绑定。没有@Input,Angular拒绝绑定到属性。
您以前已将模板HTML绑定到组件属性,而且从未使用@Input。 有什么不一样?
差异是一个信任的问题。 Angular将组件的模板视为属于组件。组件和它的模板隐式互相信任。所以,组件本身的模板能够绑定到该组件的任何属性,不管有没有@Input注解。
可是组件或指令不该该盲目地信任其余组件和指令。 默认状况下,组件或指令的属性是隐式绑定的。从Angular绑定角度来看,它们是私密的。当用@Input注解装饰时,该属性从Angular绑定的角度变成公共的。只有这样它才能受到其余组件或指令的绑定。
您能够经过绑定中属性名称的位置来判断是否须要@Input。
如今将该推理应用于如下示例:
<p [myHighlight]="color">Highlight me!</p>