AngularDart4.0 高級-屬性(Attribute)指令

屬性指令改變DOM元素的外觀或行爲。css

嘗試一下實例(查看源代碼)。html

指令概述

Angular有三種指令:java

  1. 組件 - 指令與模板。
  2. 結構指令 - 經過添加和刪除DOM元素來更改DOM佈局。
  3. 屬性(attribute)指令 - 改變元素,組件或其餘指令的外觀或行爲。

組件是三個指令中最多見的。 您在Starter App中看到了一個簡單的組件。git

結構指令改變了視圖的結構。 兩個例子是NgForNgIf。 在「結構指令」頁面中瞭解它們。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時,就會識別該指令。

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屬性添加到AppComponentlib/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,名爲defaultColorlib/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註解。
相關文章
相關標籤/搜索