Attribute directives are used as attributes of elements.
The built-in NgStyle directive in the
Template Syntax guide, for example,
can change several element styles at the same time.
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.
This page demonstrates building a simple myHighlight attribute
directive to set an element's background color
when the user hovers over that element. You can apply it like this:
The CSS selector for an attribute
is the attribute name in square brackets.
Here, the directive's selector is [myHighlight].
Angular locates all elements in the template that have an attribute named myHighlight.
为什么不直接叫做 "highlight"?
Why not call it "highlight"?
尽管highlight 是一个比 myHighlight 更简洁的名字,而且它确实也能工作。
但是最佳实践是在选择器名字前面添加前缀,以确保它们不会与标准 HTML 属性冲突。
它同时减少了与第三方指令名字发生冲突的危险。
Though highlight is a more concise name than myHighlight and would work,
a 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.
Make sure you do not prefix the highlight directive name with ng because
that prefix is reserved for Angular and using it could cause bugs that are difficult to diagnose.
For a simple demo, the short prefix, my, helps distinguish your custom directive.
After the @Directive metadata comes the directive's controller class,
called HighlightDirective, which contains the logic for the directive.
Exporting HighlightDirective makes it accessible to other components.
Angular 会为每个匹配的元素创建一个指令控制器类的实例,并把 Angular 的ElementRef和Renderer注入进构造函数。
ElementRef是一个服务,它赋予我们通过它的nativeElement属性直接访问 DOM 元素的能力。
Renderer服务允许通过代码设置元素的样式。
Angular creates a new instance of the directive's controller class for
each matching element, injecting an Angular ElementRef
into the constructor.
ElementRef is a service that grants direct access to the DOM element
through its nativeElement property.
To use the new HighlightDirective, create a template that
applies the directive as an attribute to a paragraph (<p>) element.
In Angular terms, the <p> element is the attribute host.
我们把这个模板放到它的app.component.html文件中,就像这样:
Put the template in its own app.component.html
file that looks like this:
Next, add an import statement to fetch the Highlight directive and
add that class to the declarations NgModule metadata. This way Angular
recognizes the directive when it encounters myHighlight in the template.
Angular detects that you're trying to bind to something but it can't find this directive
in the module's declarations array.
After specifying HighlightDirective in the declarations array,
Angular knows it can apply the directive to components declared in this module.
To summarize, Angular found the myHighlight attribute on the <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.
Currently, myHighlight 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加进导入列表中,同时再添加Input符号,因为我们很快就要用到它。
Begin by adding HostListener to the list of imported symbols;
add the Input symbol as well because you'll need it soon.
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 and attach event listeners manually.
There are at least three problems with that approach:
必须正确的书写事件监听器。
You have to write the listeners correctly.
当指令被销毁的时候,必须拆卸事件监听器,否则会导致内存泄露。
The code must detach the listener when the directive is destroyed to avoid memory leaks.
必须直接和 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 DOM element, el,
which you declare and initialize in the constructor.
Run the app and confirm that the background color appears when the mouse hovers over the p and
disappears as it moves out.We run the app and confirm that the background color appears as we move the mouse over the p and
disappears as we move out.
使用数据绑定向指令传递值
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.
我们先把highlightColor属性添加到指令类中,就像这样:
Start by adding a highlightColor property to the directive class like this:
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 about that.
试试把下列指令绑定变量添加到AppComponent的模板中:
Try it by adding the following directive binding variations to the AppComponent template:
src/app/app.component.html (excerpt)
<pmyHighlighthighlightColor="yellow">Highlighted in yellow</p><pmyHighlight [highlightColor]="'orange'">Highlighted in orange</p>
把color属性添加到AppComponent中:
Add a color property to the AppComponent.
src/app/app.component.ts (class)
exportclassAppComponent{
color ='yellow';}
让它通过属性绑定来控制高亮颜色。
Let it control the highlight color with a property binding.
src/app/app.component.html (excerpt)
<pmyHighlight [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.
The [myHighlight] 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 ([myHighlight]) to do both jobs.
That's a crisp, compact syntax.
Now that you're binding to highlightColor, modify the onMouseEnter() method to use it.
If someone neglects to bind to highlightColor, highlight in red:
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><h4>Pick a highlight color</h4><div><inputtype="radio"name="colors" (click)="color='lightgreen'">Green
<inputtype="radio"name="colors" (click)="color='yellow'">Yellow
<inputtype="radio"name="colors" (click)="color='cyan'">Cyan
</div><p [myHighlight]="color">Highlight me!</p>
修改AppComponent.color,让它不再有初始值。
Revise the AppComponent.color so that it has no initial value.
exportclassAppComponent{
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:
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.
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 [myHighlight]="color"defaultColor="violet">
Highlight me too!
</p>
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?
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.
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.
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:
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.
The myHighlight property on the left refers to an aliased property of the HighlightDirective,
not a property of the template's component. There are trust issues.
Therefore, the directive property must carry the @Input decorator.