屬性指令改變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>