填寫這份《一分鐘調查》,幫我們(開發組)做得更好!去填寫Home

屬性型指令

Attribute directives

屬性型指令用於改變一個 DOM 元素的外觀或行為。

An Attribute directive changes the appearance or behavior of a DOM element.

你可以到這裡試試:屬性型指令範例 / 下載範例

Try theAttribute Directive example / 下載範例.

指令概覽

Directives overview

在 Angular 中有三種類型的指令:

There are three kinds of directives in Angular:

  1. 元件 — 擁有範本的指令

    Components—directives with a template.

  2. 結構型指令 — 透過新增和移除 DOM 元素改變 DOM 佈局的指令

    Structural directives—change the DOM layout by adding and removing DOM elements.

  3. 屬性型指令 — 改變元素、元件或其它指令的外觀和行為的指令。

    Attribute directives—change the appearance or behavior of an element, component, or another directive.

元件是這三種指令中最常用的。 你在快速上手例子中第一次見到元件。

Components are the most common of the three directives. You saw a component for the first time in the Getting Started tutorial.

結構型指令修改檢視的結構。例如,NgForNgIf。 要了解更多,參閱結構型指令 指南。

Structural Directives change the structure of the view. Two examples are NgFor and NgIf. Learn about them in the Structural Directives guide.

屬性型指令改變一個元素的外觀或行為。例如,內建的 NgStyle 指令可以同時修改元素的多個樣式。

Attribute directives are used as attributes of elements. The built-in NgStyle directive in the Built-in directives guide, for example, can change several element styles at the same time.

建立一個簡單的屬性型指令

Build a simple attribute directive

屬性型指令至少需要一個帶有 @Directive 裝飾器的控制器類別。該裝飾器指定了一個用於標識屬性的選擇器。 控制器類別實現了指令需要的指令行為。

An attribute directive minimally requires building a controller class annotated with @Directive, which specifies the selector that identifies the attribute. The controller class implements the desired directive behavior.

本章展示了如何建立一個簡單的屬性型指令 appHighlight,當用戶把滑鼠懸停在一個元素上時,改變它的背景色。你可以這樣用它:

This page demonstrates building a simple appHighlight attribute directive to set an element's background color when the user hovers over that element. You can apply it like this:

<p appHighlight>Highlight me!</p>
src/app/app.component.html (applied)
      
      <p appHighlight>Highlight me!</p>
    

注意,指令不支援名稱空間。

Please note that directives do not support namespaces.

<p app:Highlight>This is invalid</p>
src/app/app.component.avoid.html (unsupported)
      
      <p app:Highlight>This is invalid</p>
    

編寫指令程式碼

Write the directive code

在命令列視窗下用 CLI 命令 ng generate directive建立指令類別檔案。

Create the directive class file in a terminal window with the CLI command ng generate directive.

ng generate directive highlight
      
      ng generate directive highlight
    

CLI 會建立 src/app/highlight.directive.ts 及相應的測試檔案(src/app/highlight.directive.spec.ts),並且在根模組 AppModule 中宣告這個指令類別。

The CLI creates src/app/highlight.directive.ts, a corresponding test file src/app/highlight.directive.spec.ts, and declares the directive class in the root AppModule.

元件一樣,這些指令也必須在Angular 模組中進行宣告。

Directives must be declared in Angular Modules in the same manner as components.

產生的 src/app/highlight.directive.ts 檔案如下:

The generated src/app/highlight.directive.ts is as follows:

import { Directive } from '@angular/core'; @Directive({ selector: '[appHighlight]' }) export class HighlightDirective { constructor() { } }
src/app/highlight.directive.ts
      
      import { Directive } from '@angular/core';

@Directive({
  selector: '[appHighlight]'
})
export class HighlightDirective {
  constructor() { }
}
    

這裡匯入的 Directive 符號提供了 Angular 的 @Directive 裝飾器。

The imported Directive symbol provides Angular the @Directive decorator.

@Directive 裝飾器的配置屬性中指定了該指令的 CSS 屬性型選擇器 [appHighlight]

The @Directive decorator's lone configuration property specifies the directive's CSS attribute selector, [appHighlight].

這裡的方括號([])表示它的屬性型選擇器。 Angular 會在範本中定位每個擁有名叫 appHighlight 屬性的元素,並且為這些元素加上本指令的邏輯。

It's the brackets ([]) that make it an attribute selector. Angular locates each element in the template that has an attribute named appHighlight and applies the logic of this directive to that element.

正因如此,這類別指令被稱為 屬性選擇器

The attribute selector pattern explains the name of this kind of directive.

為什麼不直接叫做 "highlight"?

Why not "highlight"?

儘管 highlight 是一個比 appHighlight 更簡潔的名字,而且它確實也能工作。 但是最佳實踐是在選擇器名字前面新增字首,以確保它們不會與標準 HTML 屬性衝突。 它同時減少了與第三方指令名字發生衝突的危險。

Though highlight would be a more concise selector than appHighlight and it would work, the best practice is to prefix selector names to ensure they don't conflict with standard HTML attributes. This also reduces the risk of colliding with third-party directive names. The CLI added the app prefix for you.

確認你沒有highlight 指令新增ng字首。 那個字首屬於 Angular,使用它可能導致難以診斷的 bug。例如,這個簡短的字首 app 可以幫助你區分自訂指令。

Make sure you do not prefix the highlight directive name with ngbecause that prefix is reserved for Angular and using it could cause bugs that are difficult to diagnose.

緊跟在 @Directive 元資料之後的就是該指令的控制器類別,名叫 HighlightDirective,它包含了該指令的邏輯(目前為空邏輯)。然後匯出 HighlightDirective,以便它能在別處訪問到。

After the @Directive metadata comes the directive's controller class, called HighlightDirective, which contains the (currently empty) logic for the directive. Exporting HighlightDirective makes the directive accessible.

現在,把剛才產生的 src/app/highlight.directive.ts 編輯成這樣:

Now edit the generated src/app/highlight.directive.ts to look as follows:

import { Directive, ElementRef } from '@angular/core'; @Directive({ selector: '[appHighlight]' }) export class HighlightDirective { constructor(el: ElementRef) { el.nativeElement.style.backgroundColor = 'yellow'; } }
src/app/highlight.directive.ts
      
      import { Directive, ElementRef } from '@angular/core';

@Directive({
  selector: '[appHighlight]'
})
export class HighlightDirective {
    constructor(el: ElementRef) {
       el.nativeElement.style.backgroundColor = 'yellow';
    }
}
    

import 語句還從 Angular 的 core 函式庫中匯入了一個 ElementRef 符號。

The import statement specifies an additional ElementRef symbol from the Angular core library:

你可以在指令的建構函式中使用 ElementRef注入宿主 DOM 元素的參考,也就是你放置 appHighlight 的那個元素。

You use the ElementRef in the directive's constructor to inject a reference to the host DOM element, the element to which you applied appHighlight.

ElementRef 透過其 nativeElement 屬性給你了直接訪問宿主 DOM 元素的能力。

ElementRef grants direct access to the host DOM element through its nativeElement property.

這裡的第一個實現把宿主元素的背景色設定為了黃色。

This first implementation sets the background color of the host element to yellow.

使用屬性型指令

Apply the attribute directive

要想使用這個新的 HighlightDirective,就往根元件 AppComponent 的範本中新增一個 <p> 元素,並把該指令作為一個屬性使用。

To use the new HighlightDirective, add a paragraph (<p>) element to the template of the root AppComponent and apply the directive as an attribute.

<p appHighlight>Highlight me!</p>
src/app/app.component.html
      
      <p appHighlight>Highlight me!</p>
    

執行這個應用以檢視 HighlightDirective 的實際效果。

Now run the application to see the HighlightDirective in action.

ng serve
      
      ng serve
    

總結:Angular 在宿主元素 <p> 上發現了一個 appHighlight 屬性。 然後它建立了一個 HighlightDirective 類別的實例,並把所在元素的參考注入到了指令的建構函式中。 在建構函式中,該指令把 <p> 元素的背景設定為了黃色。

To summarize, Angular found the appHighlight attribute on the host <p> element. It created an instance of the HighlightDirective class and injected a reference to the <p> element into the directive's constructor which sets the <p> element's background style to yellow.

響應使用者引發的事件

Respond to user-initiated events

當前,appHighlight 只是簡單的設定元素的顏色。 這個指令應該在使用者滑鼠懸浮一個元素時,設定它的顏色。

Currently, appHighlight simply sets an element color. The directive could be more dynamic. It could detect when the user mouses into or out of the element and respond by setting or clearing the highlight color.

先把 HostListener 加進匯入列表中。

Begin by adding HostListener to the list of imported symbols.

import { Directive, ElementRef, HostListener } from '@angular/core';
src/app/highlight.directive.ts (imports)
      
      import { Directive, ElementRef, HostListener } from '@angular/core';
    

然後使用 HostListener 裝飾器新增兩個事件處理器,它們會在滑鼠進入或離開時進行響應。

Then add two event handlers that respond when the mouse enters or leaves, each adorned by the HostListener decorator.

@HostListener('mouseenter') onMouseEnter() { this.highlight('yellow'); } @HostListener('mouseleave') onMouseLeave() { this.highlight(null); } private highlight(color: string) { this.el.nativeElement.style.backgroundColor = color; }
src/app/highlight.directive.ts (mouse-methods)
      
      @HostListener('mouseenter') onMouseEnter() {
  this.highlight('yellow');
}

@HostListener('mouseleave') onMouseLeave() {
  this.highlight(null);
}

private highlight(color: string) {
  this.el.nativeElement.style.backgroundColor = color;
}
    

@HostListener 裝飾器讓你訂閱某個屬性型指令所在的宿主 DOM 元素的事件,在這個例子中就是 <p>

The @HostListener decorator lets you subscribe to events of the DOM element that hosts an attribute directive, the <p> in this case.

當然,你可以透過標準的 JavaScript 方式手動給宿主 DOM 元素附加一個事件監聽器。 但這種方法至少有三個問題:

Of course you could reach into the DOM with standard JavaScript and attach event listeners manually. There are at least three problems with that approach:

  1. 必須正確的書寫事件監聽器。

    You have to write the listeners correctly.

  2. 當指令被銷燬的時候,必須拆卸事件監聽器,否則會導致記憶體洩露。

    The code must detach the listener when the directive is destroyed to avoid memory leaks.

  3. 必須直接和 DOM API 打交道,應該避免這樣做。

    Talking to DOM API directly isn't a best practice.

這些處理器委託了一個輔助方法來為 DOM 元素(el)設定顏色。

The handlers delegate to a helper method that sets the color on the host DOM element, el.

這個輔助方法(highlight)被從建構函式中提取了出來。 修改後的建構函式只負責宣告要注入的元素 el: ElementRef

The helper method, highlight, was extracted from the constructor. The revised constructor simply declares the injected el: ElementRef.

constructor(private el: ElementRef) { }
src/app/highlight.directive.ts (constructor)
      
      constructor(private el: ElementRef) { }
    

下面是修改後的指令程式碼:

Here's the updated directive in full:

import { Directive, ElementRef, HostListener } from '@angular/core'; @Directive({ selector: '[appHighlight]' }) export class HighlightDirective { constructor(private el: ElementRef) { } @HostListener('mouseenter') onMouseEnter() { this.highlight('yellow'); } @HostListener('mouseleave') onMouseLeave() { this.highlight(null); } private highlight(color: string) { this.el.nativeElement.style.backgroundColor = color; } }
src/app/highlight.directive.ts
      
      import { Directive, ElementRef, HostListener } from '@angular/core';

@Directive({
  selector: '[appHighlight]'
})
export class HighlightDirective {
  constructor(private el: ElementRef) { }

  @HostListener('mouseenter') onMouseEnter() {
    this.highlight('yellow');
  }

  @HostListener('mouseleave') onMouseLeave() {
    this.highlight(null);
  }

  private highlight(color: string) {
    this.el.nativeElement.style.backgroundColor = color;
  }
}
    

執行本應用並確認:當把滑鼠移到 p 上的時候,背景色就出現了,而移開時就消失了。

Run the app and confirm that the background color appears when the pointer hovers over the paragraph element and disappears as the pointer moves out.

使用 @Input 資料綁定向指令傳遞值

Pass values into the directive with an @Input data binding

高亮的顏色目前是硬編碼在指令中的,這不夠靈活。 在這一節中,你應該讓指令的使用者可以指定要用哪種顏色進行高亮。

Currently the highlight color is hard-coded within the directive. That's inflexible. In this section, you give the developer the power to set the highlight color while applying the directive.

先從 @angular/core 中匯入 Input

Begin by adding Input to the list of symbols imported from @angular/core.

import { Directive, ElementRef, HostListener, Input } from '@angular/core';
src/app/highlight.directive.ts (imports)
      
      import { Directive, ElementRef, HostListener, Input } from '@angular/core';
    

然後把 highlightColor 屬性新增到指令類別中,就像這樣:

Add a highlightColor property to the directive class like this:

@Input() highlightColor: string;
src/app/highlight.directive.ts (highlightColor)
      
      @Input() highlightColor: string;
    

繫結到 @Input 屬性

Binding to an @Input() property

注意看 @Input 裝飾器。它往類別上添加了一些元資料,從而讓該指令的 highlightColor 能用於繫結。

Notice the @Input() decorator. It adds metadata to the class that makes the directive's highlightColor property available for binding.

它之所以稱為輸入屬性,是因為資料流是從繫結表示式流向指令內部的。 如果沒有這個元資料,Angular 就會拒絕繫結,參閱稍後瞭解更多。

It's called an input property because data flows from the binding expression into the directive. Without that @Input() metadata, Angular rejects the binding; see below for more information.

試試把下列指令繫結變數新增到 AppComponent 的範本中:

Try it by adding the following directive binding variations to the AppComponent template:

<p appHighlight highlightColor="yellow">Highlighted in yellow</p> <p appHighlight [highlightColor]="'orange'">Highlighted in orange</p>
src/app/app.component.html (excerpt)
      
      <p appHighlight highlightColor="yellow">Highlighted in yellow</p>
<p appHighlight [highlightColor]="'orange'">Highlighted in orange</p>
    

color 屬性新增到 AppComponent 中:

Add a color property to the AppComponent.

export class AppComponent { color = 'yellow'; }
src/app/app.component.ts (class)
      
      export class AppComponent {
  color = 'yellow';
}
    

讓它透過屬性繫結來控制高亮顏色。

Let it control the highlight color with a property binding.

<p appHighlight [highlightColor]="color">Highlighted with parent component's color</p>
src/app/app.component.html (excerpt)
      
      <p appHighlight [highlightColor]="color">Highlighted with parent component's color</p>
    

很不錯,但如果可以在應用該指令時在同一個屬性中設定顏色就更好了,就像這樣:

That's good, but it would be nice to simultaneously apply the directive and set the color in the same attribute like this.

<p [appHighlight]="color">Highlight me!</p>
src/app/app.component.html (color)
      
      <p [appHighlight]="color">Highlight me!</p>
    

[appHighlight] 屬性同時做了兩件事:把這個高亮指令應用到了 <p> 元素上,並且透過屬性繫結設定了該指令的高亮顏色。 你複用了該指令的屬性選擇器 [appHighlight] 來同時完成它們。 這是清爽、簡約的語法。

The [appHighlight] attribute binding both applies the highlighting directive to the <p> element and sets the directive's highlight color with a property binding. You're re-using the directive's attribute selector ([appHighlight]) to do both jobs. That's a crisp, compact syntax.

你還要把該指令的 highlightColor 改名為 appHighlight,因為它是顏色屬性目前的繫結名。

You'll have to rename the directive's highlightColor property to appHighlight because that's now the color property binding name.

@Input() appHighlight: string;
src/app/highlight.directive.ts (renamed to match directive selector)
      
      @Input() appHighlight: string;
    

這可不好。因為 appHighlight 是一個糟糕的屬性名,而且不能反映該屬性的意圖。

This is disagreeable. The word, appHighlight, is a terrible property name and it doesn't convey the property's intent.

繫結到 @Input 別名

Bind to an @Input alias

幸運的是,你可以隨意命名該指令的屬性,並且給它指定一個用於繫結的別名

Fortunately you can name the directive property whatever you want and alias it for binding purposes.

恢復原始屬性名,並在 @Input 的引數中把該選擇器指定為別名。

Restore the original property name and specify the selector as the alias in the argument to @Input().

@Input('appHighlight') highlightColor: string;
src/app/highlight.directive.ts (color property with alias)
      
      @Input('appHighlight') highlightColor: string;
    

在指令內部,該屬性叫 highlightColor,在外部,你繫結到它地方,它叫 appHighlight

Inside the directive the property is known as highlightColor. Outside the directive, where you bind to it, it's known as appHighlight.

這是最好的結果:理想的內部屬性名,理想的繫結語法:

You get the best of both worlds: the property name you want and the binding syntax you want:

<p [appHighlight]="color">Highlight me!</p>
src/app/app.component.html (color)
      
      <p [appHighlight]="color">Highlight me!</p>
    

現在,你透過別名繫結到了 highlightColor 屬性,並修改 onMouseEnter() 方法來使用它。 如果有人忘了繫結到 appHighlight,那就用紅色進行高亮。

Now that you're binding via the alias to the highlightColor, modify the onMouseEnter() method to use that property. If someone neglects to bind to appHighlight, highlight the host element in red:

@HostListener('mouseenter') onMouseEnter() { this.highlight(this.highlightColor || 'red'); }
src/app/highlight.directive.ts (mouse enter)
      
      @HostListener('mouseenter') onMouseEnter() {
  this.highlight(this.highlightColor || 'red');
}
    

這是最終版本的指令類別。

Here's the latest version of the directive class.

import { Directive, ElementRef, HostListener, Input } from '@angular/core'; @Directive({ selector: '[appHighlight]' }) export class HighlightDirective { constructor(private el: ElementRef) { } @Input('appHighlight') highlightColor: string; @HostListener('mouseenter') onMouseEnter() { this.highlight(this.highlightColor || 'red'); } @HostListener('mouseleave') onMouseLeave() { this.highlight(null); } private highlight(color: string) { this.el.nativeElement.style.backgroundColor = color; } }
src/app/highlight.directive.ts (excerpt)
      
      import { Directive, ElementRef, HostListener, Input } from '@angular/core';

@Directive({
  selector: '[appHighlight]'
})
export class HighlightDirective {

  constructor(private el: ElementRef) { }

  @Input('appHighlight') highlightColor: string;

  @HostListener('mouseenter') onMouseEnter() {
    this.highlight(this.highlightColor || 'red');
  }

  @HostListener('mouseleave') onMouseLeave() {
    this.highlight(null);
  }

  private highlight(color: string) {
    this.el.nativeElement.style.backgroundColor = color;
  }
}
    

寫個測試程式試驗下

Write a harness to try it

憑空想象該指令如何工作可不容易。 在本節,你將把 AppComponent 改成一個測試程式,它讓你可以透過單選按鈕來選取高亮顏色,並且把你選取的顏色繫結到指令中。

It may be difficult to imagine how this directive actually works. In this section, you'll turn AppComponent into a harness that lets you pick the highlight color with a radio button and bind your color choice to the directive.

app.component.html 修改成這樣:

Update app.component.html as follows:

<h1>My First Attribute Directive</h1> <h2>Pick a highlight color</h2> <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 [appHighlight]="color">Highlight me!</p>
src/app/app.component.html (v2)
      
      <h1>My First Attribute Directive</h1>

<h2>Pick a highlight color</h2>
<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 [appHighlight]="color">Highlight me!</p>
    

修改 AppComponent.color,讓它不再有初始值。

Revise the AppComponent.color so that it has no initial value.

export class AppComponent { color: string; }
src/app/app.component.ts (class)
      
      export class AppComponent {
  color: string;
}
    

下面是測試程式和指令的動畫。

Here are the harness and directive in action.

繫結到第二個屬性

Bind to a second property

本例的指令只有一個可訂製屬性,真實的應用通常需要更多。

This highlight directive has a single customizable property. In a real app, it may need more.

目前,預設顏色(它在使用者選取了高亮顏色之前一直有效)被硬編碼為紅色。應該允許範本的開發者設定預設顏色。

At the moment, the default color—the color that prevails until the user picks a highlight color—is hard-coded as "red". Let the template developer set the default color.

把第二個名叫 defaultColor輸入屬性新增到 HighlightDirective 中:

Add a second input property to HighlightDirective called defaultColor:

@Input() defaultColor: string;
src/app/highlight.directive.ts (defaultColor)
      
      @Input() defaultColor: string;
    

修改該指令的 onMouseEnter,讓它首先嚐試使用 highlightColor 進行高亮,然後用 defaultColor,如果它們都沒有指定,那就用紅色作為後備。

Revise the directive's onMouseEnter so that it first tries to highlight with the highlightColor, then with the defaultColor, and falls back to "red" if both properties are undefined.

@HostListener('mouseenter') onMouseEnter() { this.highlight(this.highlightColor || this.defaultColor || 'red'); }
src/app/highlight.directive.ts (mouse-enter)
      
      @HostListener('mouseenter') onMouseEnter() {
  this.highlight(this.highlightColor || this.defaultColor || 'red');
}
    

當已經繫結過 appHighlight 屬性時,要如何繫結到第二個屬性呢?

How do you bind to a second property when you're already binding to the appHighlight attribute name?

像元件一樣,你也可以繫結到指令的很多屬性,只要把它們依次寫在範本中就行了。 開發者可以繫結到 AppComponent.color,並且用紫羅蘭色作為預設顏色,程式碼如下:

As with components, you can add as many directive property bindings as you need by stringing them along in the template. The developer should be able to write the following template HTML to both bind to the AppComponent.color and fall back to "violet" as the default color.

<p [appHighlight]="color" defaultColor="violet"> Highlight me too! </p>
src/app/app.component.html (defaultColor)
      
      <p [appHighlight]="color" defaultColor="violet">
  Highlight me too!
</p>
    

Angular 之所以知道 defaultColor 繫結屬於 HighlightDirective,是因為你已經透過 @Input 裝飾器把它設定成了公共屬性。

Angular knows that the defaultColor binding belongs to the HighlightDirective because you made it public with the @Input() decorator.

當這些程式碼完成時,測試程式工作時的動畫如下:

Here's how the harness should work when you're done coding.


ngNonBindable

使用由範本引擎原生支援的 ngNonBindable 偽指令,可以讓 Angular 不對範本中的表示式進行求值。例如:

With the built-in template primitive ngNonBindable, Angular won't evaluate expressions in elements. For example:

<p>Use ngNonBindable to stop evaluation.</p> <p ngNonBindable>This should not evaluate: {{ 1 + 1 }}</p>
src/app/app.component.html
      
      <p>Use ngNonBindable to stop evaluation.</p>
<p ngNonBindable>This should not evaluate: {{ 1 + 1 }}</p>
    

表示式 {{ 1 + 1 }} 將會原樣渲染,就像你的程式碼編輯器中一樣,而不會顯示為 2。當你要在瀏覽器中渲染程式碼時,這很有用。

The expression {{ 1 + 1 }} will render just as it does in your code editor, and will not display 2. This is helpful when you want to render code in the browser.

當你把 ngNonBindable 應用在元素上時,它會阻止元素及其所有子元素的繫結。不過,ngNonBindable 仍然允許指令作用於受 ngNonBindable 影響的元素上。下面的例子中,appHighlight 指令仍然會生效,但是 Angular 不會對表示式 {{ 1 + 1 }} 進行求值。

When you apply ngNonBindable to an element, it stops any binding starting at that element, including child elements. However, ngNonBindable still allows directives to work to the element where you apply ngNonBindable. In the following example, the appHighlight directive will still be active but Angular will not evaluate the expression {{ 1 + 1 }}.

<h3>ngNonBindable with a directive</h3> <div ngNonBindable [appHighlight]="'yellow'">This should not evaluate: {{ 1 +1 }}, but will highlight yellow. </div>
src/app/app.component.html
      
      <h3>ngNonBindable with a directive</h3>
<div ngNonBindable [appHighlight]="'yellow'">This should not evaluate: {{ 1 +1 }}, but will highlight yellow.
</div>
    

另外,如果你把 ngNonBindable 應用於某個父元素,就會在它的所有子元素上禁用插值和任何型別的繫結比如屬性繫結或事件繫結。

Additionally, if you apply ngNonBindable to a parent element, interpolation and binding of any sort, such as property binding, or event binding, is disabled for its children.

小結

Summary

本章介紹瞭如何:

This page covered how to:

最終的原始碼如下:

The final source code follows:

import { Component } from '@angular/core'; @Component({ selector: 'app-root', templateUrl: './app.component.html' }) export class AppComponent { color: string; }<h1>My First Attribute Directive</h1> <h2>Pick a highlight color</h2> <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 [appHighlight]="color">Highlight me!</p> <p [appHighlight]="color" defaultColor="violet"> Highlight me too! </p>/* tslint:disable:member-ordering */ import { Directive, ElementRef, HostListener, Input } from '@angular/core'; @Directive({ selector: '[appHighlight]' }) export class HighlightDirective { constructor(private el: ElementRef) { } @Input() defaultColor: string; @Input('appHighlight') highlightColor: string; @HostListener('mouseenter') onMouseEnter() { this.highlight(this.highlightColor || this.defaultColor || 'red'); } @HostListener('mouseleave') onMouseLeave() { this.highlight(null); } private highlight(color: string) { this.el.nativeElement.style.backgroundColor = color; } }import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { AppComponent } from './app.component'; import { HighlightDirective } from './highlight.directive'; @NgModule({ imports: [ BrowserModule ], declarations: [ AppComponent, HighlightDirective ], bootstrap: [ AppComponent ] }) export class AppModule { }import { enableProdMode } from '@angular/core'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { AppModule } from './app/app.module'; import { environment } from './environments/environment'; if (environment.production) { enableProdMode(); } platformBrowserDynamic().bootstrapModule(AppModule);<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Attribute Directives</title> <base href="/"> <meta name="viewport" content="width=device-width, initial-scale=1"> </head> <body> <app-root></app-root> </body> </html>
      
      import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html'
})
export class AppComponent {
  color: string;
}
    

你還可以體驗和下載屬性型指令範例 / 下載範例.

You can also experience and download theAttribute Directive example / 下載範例.

附錄:為什麼要加@Input

Appendix: Why add @Input()?

在這個例子中 hightlightColorHighlightDirective 的一個輸入型屬性。你見過它沒有用別名時的程式碼:

In this demo, the highlightColor property is an @Input() property of the HighlightDirective. You've seen it applied without an alias:

@Input() highlightColor: string;
src/app/highlight.directive.ts (color)
      
      @Input() highlightColor: string;
    

也見過用別名時的程式碼:

You've seen it with an alias:

@Input('appHighlight') highlightColor: string;
src/app/highlight.directive.ts (color)
      
      @Input('appHighlight') highlightColor: string;
    

無論哪種方式,@Input 裝飾器都告訴 Angular,該屬性是公共的,並且能被父元件繫結。 如果沒有 @Input,Angular 就會拒絕繫結到該屬性。

Either way, the @Input() decorator tells Angular that this property is public and available for binding by a parent component. Without @Input(), Angular refuses to bind to the property.

但你以前也曾經把範本 HTML 繫結到元件的屬性,而且從來沒有用過 @Input。 差異何在?

You've bound template HTML to component properties before and never used @Input(). What's different?

差異在於信任度不同。 Angular 把元件的範本看做從屬於該元件的。 元件和它的範本預設會相互信任。 這也就是意味著,元件自己的範本可以繫結到元件的任意屬性,無論是否使用了 @Input 裝飾器。

The difference is a matter of trust. Angular treats a component's template as belonging to the component. The component and its template trust each other implicitly. Therefore, the component's own template may bind to any property of that component, with or without the @Input() decorator.

但元件或指令不應該盲目的信任其它元件或指令。 因此元件或指令的屬性預設是不能被繫結的。 從 Angular 繫結機制的角度來看,它們是私有的,而當添加了 @Input 時,Angular 繫結機制才會把它們當成公共的。 只有這樣,它們才能被其它元件或屬性繫結。

But a component or directive shouldn't blindly trust other components and directives. The properties of a component or directive are hidden from binding by default. They are private from an Angular binding perspective. When adorned with the @Input() decorator, the property becomes public from an Angular binding perspective. Only then can it be bound by some other component or directive.

你可以根據屬性名在繫結中出現的位置來判定是否要加 @Input

You can tell if @Input() is needed by the position of the property name in a binding.

  • 當它出現在等號右側的範本表示式中時,它屬於範本所在的元件,不需要 @Input 裝飾器。

    When it appears in the template expression to the right of the equals (=), it belongs to the template's component and does not require the @Input() decorator.

  • 當它出現在等號左邊方括號([ ])中時,該屬性屬於其它元件或指令,它必須帶有 @Input 裝飾器。

    When it appears in square brackets ([ ]) to the left of the equals (=), the property belongs to some other component or directive; that property must be adorned with the @Input() decorator.

試用此原理分析下列範例:

Now apply that reasoning to the following example:

<p [appHighlight]="color">Highlight me!</p>
src/app/app.component.html (color)
      
      <p [appHighlight]="color">Highlight me!</p>
    
  • color 屬性位於右側的繫結表示式中,它屬於範本所在的元件。 該範本和元件相互信任。因此 color 不需要 @Input 裝飾器。

    The color property in the expression on the right belongs to the template's component. The template and its component trust each other. The color property doesn't require the @Input() decorator.

  • appHighlight 屬性位於左側,它參考了 HighlightDirective 中一個帶別名的屬性,它不是範本所屬元件的一部分,因此存在信任問題。 所以,該屬性必須帶 @Input 裝飾器。

    The appHighlight property on the left refers to an aliased property of the HighlightDirective, not a property of the template's component. For security, the directive property must carry the @Input() decorator.