docs(attribute-directives): clarify @Input and aliasing (#3007)

This commit is contained in:
Ward Bell 2016-12-19 17:12:42 -08:00 committed by GitHub
parent 5d89f90a3b
commit dee9bd1f84
10 changed files with 267 additions and 158 deletions

View File

@ -2,6 +2,17 @@
<h1>My First Attribute Directive</h1> <h1>My First Attribute Directive</h1>
<p myHighlight>Highlight me!</p> <p myHighlight>Highlight me!</p>
<!-- #enddocregion --> <!-- #enddocregion -->
<!-- #docregion color-1 -->
<p myHighlight highlightColor="yellow">Highlighted in yellow</p>
<p myHighlight [highlightColor]="'orange'">Highlighted in orange</p>
<!-- #enddocregion color-1 -->
<!-- #docregion color-2 -->
<p myHighlight [highlightColor]="color">Highlighted with parent component's color</p>
<!-- #enddocregion color-2 -->
<!-- #docregion p-style-background --> <!-- #docregion p-style-background -->
<p [style.background]="'lime'">I am green with envy!</p> <p [style.background]="'lime'">I am green with envy!</p>
<!-- #enddocregion p-style-background --> <!-- #enddocregion p-style-background -->

View File

@ -0,0 +1,12 @@
import { Component } from '@angular/core';
@Component({
moduleId: module.id,
selector: 'my-app',
templateUrl: 'app.component.1.html'
})
// #docregion class
export class AppComponent {
color = 'yellow';
}
// #enddocregion class

View File

@ -1,20 +1,27 @@
<!-- #docregion --> <!-- #docregion -->
<!-- #docregion v2 --> <!-- #docregion v2 -->
<h1>My First Attribute Directive</h1> <h1>My First Attribute Directive</h1>
<h4>Pick a highlight color</h4> <h4>Pick a highlight color</h4>
<div> <div>
<input type="radio" name="colors" (click)="color='lightgreen'">Green <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='yellow'">Yellow
<input type="radio" name="colors" (click)="color='cyan'">Cyan <input type="radio" name="colors" (click)="color='cyan'">Cyan
</div> </div>
<!-- #docregion pHost --> <!-- #docregion color -->
<p [myHighlight]="color">Highlight me!</p> <p [myHighlight]="color">Highlight me!</p>
<!-- #enddocregion pHost --> <!-- #enddocregion color -->
<!-- #enddocregion v2 --> <!-- #enddocregion v2 -->
<!-- #docregion defaultColor --> <!-- #docregion defaultColor -->
<p [myHighlight]="color" [defaultColor]="'violet'"> <p [myHighlight]="color" defaultColor="violet">
Highlight me too! Highlight me too!
</p> </p>
<!-- #enddocregion defaultColor --> <!-- #enddocregion defaultColor -->
<!-- #enddocregion --> <!-- #enddocregion -->
<hr>
<p><i>Mouse over the following lines to see fixed highlights</i></p>
<p [myHighlight]="'yellow'">Highlighted in yellow</p>
<p myHighlight="orange">Highlighted in orange</p>

View File

@ -6,6 +6,9 @@ import { Component } from '@angular/core';
selector: 'my-app', selector: 'my-app',
templateUrl: 'app.component.html' templateUrl: 'app.component.html'
}) })
// #docregion class
export class AppComponent { } export class AppComponent {
color: string;
}
// #enddocregion class
// #enddocregion // #enddocregion

View File

@ -0,0 +1,17 @@
// Not used. Keep away from plunker
// Keeps ATLS from complaining about undeclared directives.
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component.1';
import { HighlightDirective as HLD1 } from './highlight.directive.1';
import { HighlightDirective as HLD2 } from './highlight.directive.2';
import { HighlightDirective as HLD3 } from './highlight.directive.3';
@NgModule({
imports: [ BrowserModule ],
declarations: [
AppComponent, HLD1, HLD2, HLD3
]
})
export class DummyModule { }

View File

@ -1,15 +1,26 @@
/* tslint:disable:no-unused-variable */ /* tslint:disable:no-unused-variable member-ordering */
// #docplaster
// #docregion // #docregion
import { Directive, ElementRef, HostListener, Input } from '@angular/core'; import { Directive, ElementRef, HostListener, Input } from '@angular/core';
@Directive({ @Directive({
selector: '[myHighlight]' selector: '[myHighlight]'
}) })
export class HighlightDirective { export class HighlightDirective {
// #docregion ctor // #docregion ctor
constructor(private el: ElementRef) { } constructor(private el: ElementRef) { }
// #enddocregion ctor // #enddocregion ctor
// #enddocregion
// #docregion color
@Input() highlightColor: string;
// #enddocregion color
// #docregion color-2
@Input() myHighlight: string;
// #enddocregion color-2
// #docregion
// #docregion mouse-methods, host // #docregion mouse-methods, host
@HostListener('mouseenter') onMouseEnter() { @HostListener('mouseenter') onMouseEnter() {
@ -26,7 +37,7 @@ export class HighlightDirective {
// #enddocregion host // #enddocregion host
private highlight(color: string) { private highlight(color: string) {
this.el.nativeElement.style.backgroundColor = 'yellow'; this.el.nativeElement.style.backgroundColor = color;
} }
// #enddocregion mouse-methods // #enddocregion mouse-methods

View File

@ -0,0 +1,27 @@
/* tslint:disable:member-ordering */
// #docregion
import { Directive, ElementRef, HostListener, Input } from '@angular/core';
@Directive({
selector: '[myHighlight]'
})
export class HighlightDirective {
constructor(private el: ElementRef) { }
@Input('myHighlight') highlightColor: string;
// #docregion mouse-enter
@HostListener('mouseenter') onMouseEnter() {
this.highlight(this.highlightColor || 'red');
}
// #enddocregion mouse-enter
@HostListener('mouseleave') onMouseLeave() {
this.highlight(null);
}
private highlight(color: string) {
this.el.nativeElement.style.backgroundColor = color;
}
}

View File

@ -1,24 +1,20 @@
/* tslint:disable:member-ordering */ /* tslint:disable:member-ordering */
// #docplaster // #docplaster
// #docregion full // #docregion
// #docregion imports
import { Directive, ElementRef, HostListener, Input } from '@angular/core'; import { Directive, ElementRef, HostListener, Input } from '@angular/core';
// #enddocregion imports
@Directive({ @Directive({
selector: '[myHighlight]' selector: '[myHighlight]'
}) })
// #docregion class
export class HighlightDirective { export class HighlightDirective {
private _defaultColor = 'red';
constructor(private el: ElementRef) { } constructor(private el: ElementRef) { }
// #enddocregion class
// #docregion defaultColor // #docregion defaultColor
@Input() set defaultColor(colorName: string){ @Input() defaultColor: string;
this._defaultColor = colorName || this._defaultColor;
}
// #enddocregion defaultColor // #enddocregion defaultColor
// #docregion class
// #docregion color // #docregion color
@Input('myHighlight') highlightColor: string; @Input('myHighlight') highlightColor: string;
@ -26,9 +22,10 @@ export class HighlightDirective {
// #docregion mouse-enter // #docregion mouse-enter
@HostListener('mouseenter') onMouseEnter() { @HostListener('mouseenter') onMouseEnter() {
this.highlight(this.highlightColor || this._defaultColor); this.highlight(this.highlightColor || this.defaultColor || 'red');
} }
// #enddocregion mouse-enter // #enddocregion mouse-enter
@HostListener('mouseleave') onMouseLeave() { @HostListener('mouseleave') onMouseLeave() {
this.highlight(null); this.highlight(null);
} }
@ -37,10 +34,3 @@ export class HighlightDirective {
this.el.nativeElement.style.backgroundColor = color; this.el.nativeElement.style.backgroundColor = color;
} }
} }
// #enddocregion class
// #enddocregion full
/*
// #docregion highlight
@Input() myHighlight: string;
// #enddocregion highlight
*/

View File

@ -3,7 +3,7 @@
"files":[ "files":[
"!**/*.d.ts", "!**/*.d.ts",
"!**/*.js", "!**/*.js",
"!app/*.[1,2].*" "!app/*.[1,2,3].*"
], ],
"tags": ["attribute", "directive"] "tags": ["attribute", "directive"]
} }

View File

@ -13,10 +13,10 @@ block includes
* [Build a simple attribute directive](#write-directive) * [Build a simple attribute directive](#write-directive)
* [Apply the attribute directive to an element in a template](#apply-directive) * [Apply the attribute directive to an element in a template](#apply-directive)
* [Respond to user-initiated events](#respond-to-user) * [Respond to user-initiated events](#respond-to-user)
* [Pass values into the directive using data binding](#bindings) * [Pass values into the directive with an _@Input_ data binding](#bindings)
* [Bind to a second property](#second-property) * [Bind to a second property](#second-property)
Try the <live-example></live-example>. Try the <live-example title="Attribute Directive example"></live-example>.
.l-main-section .l-main-section
a#directive-overview a#directive-overview
@ -116,7 +116,7 @@ a#apply-directive
:marked :marked
## Apply the attribute directive ## Apply the attribute directive
To use the new `HighlightDirective`, create a template that To use the new `HighlightDirective`, create a template that
applies the directive as an attribute to a paragraph (`p`) element. applies the directive as an attribute to a paragraph (`<p>`) element.
In Angular terms, the `<p>` element will be the attribute **host**. In Angular terms, the `<p>` element will be the attribute **host**.
p p
| Put the template in its own | Put the template in its own
@ -141,23 +141,22 @@ figure.image-display
### Your directive isn't working? ### Your directive isn't working?
Did you remember to add the directive to the the `declarations` attribute of `@NgModule`? It is easy to forget! Did you remember to add the directive to the the `declarations` attribute of `@NgModule`? It is easy to forget!
Open the console in the browser tools and look for an error like this: Open the console in the browser tools and look for an error like this:
code-example(format="nocode"). code-example(format="nocode").
EXCEPTION: Template parse errors: EXCEPTION: Template parse errors:
Can't bind to 'myHighlight' since it isn't a known property of 'p'. Can't bind to 'myHighlight' since it isn't a known property of 'p'.
:marked :marked
Angular detects that you're trying to bind to *something* but it doesn't know what, Angular detects that you're trying to bind to *something* but it can't find this directive
so it looks to the `declarations` metadata array. By specifying `HighlightDirective` in the module's `declarations` array.
in the array, Angular knows to check the import statements and from there, After specifying `HighlightDirective` in the `declarations` array,
to go to `highlight.directive.ts` to find out what `myHighlight` does. Angular knows it can apply the directive to components declared in this module.
:marked :marked
To summarize, Angular found the `myHighlight` attribute on the `<p>` element. It created To summarize, Angular found the `myHighlight` attribute on the `<p>` element.
an instance of the `HighlightDirective` class, It created an instance of the `HighlightDirective` class and
injecting a reference to the element into the constructor injected a reference to the `<p>` element into the directive's constructor
where the `<p>` element's background style is set to yellow. which sets the `<p>` element's background style to yellow.
.l-main-section .l-main-section
a#respond-to-user a#respond-to-user
@ -165,37 +164,37 @@ a#respond-to-user
## Respond to user-initiated events ## Respond to user-initiated events
Currently, `myHighlight` simply sets an element color. Currently, `myHighlight` simply sets an element color.
The directive should set the color when the user hovers over an element. 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.
This requires two things: Begin by adding `HostListener` to the list of imported symbols;
1. detecting when the user hovers into and out of the element. add the `Import` symbol as well because you'll need it soon.
2. responding to those actions by setting and clearing the highlight color. +makeExample('attribute-directives/ts/app/highlight.directive.ts','imports')(format=".")
To do this, you can apply the `@HostListener` !{_decorator} to methods which are called when an event is raised. :marked
Then add two eventhandlers that respond when the mouse enters or leaves, each adorned by the `HostListener` !{_decorator}.
+makeExample('attribute-directives/ts/app/highlight.directive.2.ts','mouse-methods')(format=".")
+makeExample('attribute-directives/ts/app/highlight.directive.2.ts','host')(format=".") :marked
The `@HostListener` !{_decorator} lets you subscribe to events of the DOM element that hosts an attribute directive, the `<p>` in this case.
.l-sub-section .l-sub-section
:marked :marked
The `@HostListener` !{_decorator} refers to the DOM element that hosts an attribute directive, the `<p>` in this case. 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:
It is possible to attach event listeners by manipulating the host DOM element directly, but
there are at least three problems with such an approach:
1. You have to write the listeners correctly. 1. You have to write the listeners correctly.
1. The code must *detach* the listener when the directive is destroyed to avoid memory leaks. 1. The code must *detach* the listener when the directive is destroyed to avoid memory leaks.
1. Talking to DOM API directly isn't a best practice. 1. Talking to DOM API directly isn't a best practice.
:marked :marked
Now implement the two mouse event handlers: The handlers delegate to a helper method that sets the color on the DOM element, `#{_priv}el`,
+makeExample('attribute-directives/ts/app/highlight.directive.2.ts','mouse-methods')(format=".") which you declare and initialize in the constructor.
:marked
Notice that they delegate to a helper method that sets the color via a private local variable, `#{_priv}el`.
Next, revise the constructor to capture the `ElementRef.nativeElement` in this variable.
+makeExample('attribute-directives/ts/app/highlight.directive.2.ts','ctor')(format=".") +makeExample('attribute-directives/ts/app/highlight.directive.2.ts','ctor')(format=".")
:marked :marked
Here's the updated directive: Here's the updated directive in full:
+makeExample('app/highlight.directive.2.ts') +makeExample('app/highlight.directive.2.ts')
:marked :marked
Run the app and confirm that the background color appears when the mouse hovers over the `p` and Run the app and confirm that the background color appears when the mouse hovers over the `p` and
@ -205,70 +204,90 @@ figure.image-display
.l-main-section .l-main-section
a#bindings a#bindings
:marked :marked
## Pass values into the directive using data binding ## Pass values into the directive with an _@Input_ data binding
Currently the highlight color is hard-coded within the directive. That's inflexible. Currently the highlight color is hard-coded _within_ the directive. That's inflexible.
A better practice is to set the color externally with a binding as follows: Let the user of the directive set the color in the template with a binding.
+makeExample('attribute-directives/ts/app/app.component.html','pHost')
:marked Start by adding a `highlightColor` property to the directive class like this:
You can extend the directive class with a bindable **input** `highlightColor` property and use it to highlight text. +makeExample('attribute-directives/ts/app/highlight.directive.2.ts','color', 'app/highlight.directive.ts')
Here is the final version of the class:
+makeExcerpt('app/highlight.directive.ts', 'class')
a#input a#input
:marked :marked
The new `highlightColor` property is called an *input* property because data flows from the binding expression into the directive. ### Binding to an _@Input_ property
Notice the `@Input()` #{_decorator} applied to the property.
+makeExcerpt('app/highlight.directive.ts', 'color')
:marked
`@Input` adds metadata to the class that makes the `highlightColor` property available for
property binding under the `myHighlight` alias.
Without this input metadata Angular rejects the binding.
See the [appendix](#why-input) below for more information.
.l-sub-section
:marked
### @Input(_alias_)
Currently, the code **aliases** the `highlightColor` property with the attribute name by
passing `myHighlight` into the `@Input` #{_decorator}:
+makeExcerpt('app/highlight.directive.ts', 'color', '')
:marked
The code binds to the attribute name, `myHighlight`, but the
the directive property name is `highlightColor`. That's a disconnect.
You can resolve the discrepancy by renaming the property to `myHighlight` and define it as follows: Notice the `@Input` !{_decorator}. It adds metadata to the class that makes the directive's `highlightColor` property available for binding.
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](#why-input "Why add @Input?") for more about that.
+makeExcerpt('app/highlight.directive.ts', 'highlight', '') Try it by adding the following directive binding variations to the `AppComponent` template:
+makeExample('attribute-directives/ts/app/app.component.1.html','color-1', 'app/app.component.html')(format='.')
:marked :marked
Now that you're getting the highlight color as an input, modify the `onMouseEnter()` method to use Add a `color` property to the `AppComponent`.
it instead of the hard-coded color name and define red as the default color. +makeExample('attribute-directives/ts/app/app.component.1.ts','class', 'app/app.component.ts (class)')(format='.')
+makeExcerpt('attribute-directives/ts/app/highlight.directive.ts', 'mouse-enter', '')
:marked :marked
To let users pick the highlight color and bind their choice to the directive, Let it control the highlight color with a property binding.
update `app.component.html` as follows: +makeExample('attribute-directives/ts/app/app.component.1.html','color-2', 'app/app.component.html')
:marked
That's good, but it would be nice to _simultaneously_ apply the directive and set the color _in the same attribute_ like this.
+makeExample('attribute-directives/ts/app/app.component.html','color')
:marked
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.
You'll have to rename the directive's `highlightColor` property to `myHighlight` because that's now the color property binding name.
+makeExample('attribute-directives/ts/app/highlight.directive.2.ts','color-2', 'app/highlight.directive.ts (renamed to match directive selector)')
:marked
This is disagreeable. The word, `myHighlight`, is a terrible property name and it doesn't convey the property's intent.
a#input-alias
:marked
### Bind to an _@Input_ alias
Fortunately you can name the directive property whatever you want _and_ **_alias it_** for binding purposes.
Restore the original property name and specify the selector as the alias in the argument to `@Input`.
+makeExcerpt('app/highlight.directive.ts', 'color', 'app/highlight.directive.ts (color property with alias')
:marked
_Inside_ the directive the property is known as `highlightColor`.
_Outside_ the directive, where you bind to it, it's known as `myHighlight`.
You get the best of both worlds: the property name you want and the binding syntax you want:
+makeExample('attribute-directives/ts/app/app.component.html','color')
:marked
Now that you're binding to `highlightColor`, modify the `onMouseEnter()` method to use it.
If someone neglects to bind to `highlightColor`, highlight in "red" by default.
+makeExample('attribute-directives/ts/app/highlight.directive.3.ts', 'mouse-enter', 'app/highlight.directive.ts (mouse enter)')(format='.')
:marked
Here's the latest version of the directive class.
+makeExcerpt('app/highlight.directive.3.ts')
:marked
## Write a harness to try it
:marked
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.
Update `app.component.html` as follows:
+makeExcerpt('attribute-directives/ts/app/app.component.html', 'v2', '') +makeExcerpt('attribute-directives/ts/app/app.component.html', 'v2', '')
.l-sub-section :marked
:marked Revise the `AppComponent.color` so that it has no initial value.
### Where is the templated *color* property? +makeExcerpt('attribute-directives/ts/app/app.component.ts', 'class', '')
You may notice that the radio button click handlers in the template set a `color` property
and the code is binding that `color` to the directive.
However, you never defined a color property for the host `AppComponent`.
Yet this code works. Where is the template `color` value going?
Browser debugging reveals that Angular dynamically added a `color` property
to the runtime instance of the `AppComponent`.
This is *convenient* behavior but it is also *implicit* behavior that could be confusing.
For clarity, consider adding the `color` property to the `AppComponent`.
:marked :marked
Here is the second version of the directive in action. Here is the harness and directive in action.
figure.image-display figure.image-display
img(src="/resources/images/devguide/attribute-directives/highlight-directive-v2-anim.gif" alt="Highlight v.2") img(src="/resources/images/devguide/attribute-directives/highlight-directive-v2-anim.gif" alt="Highlight v.2")
@ -276,31 +295,29 @@ figure.image-display
a#second-property a#second-property
:marked :marked
## Bind to a second property ## Bind to a second property
This example directive only has a single customizable property. A real app often needs more. This highlight directive has a single customizable property. In a real app, it may need more.
Let's allow the template developer to set the default color&mdash;the color that prevails until the user picks a highlight color. At the moment, the default color &mdash; the color that prevails until the user picks a highlight color &mdash;
To do this, first add a second **input** property to `HighlightDirective` called `defaultColor`: is hard-coded as "red". Let the template developer set the default color.
+makeExample('attribute-directives/ts/app/highlight.directive.ts', 'defaultColor')(format=".")
Add a second **input** property to `HighlightDirective` called `defaultColor`:
+makeExcerpt('attribute-directives/ts/app/highlight.directive.ts', 'defaultColor','app/highlight.directive.ts (defaultColor)')
:marked :marked
The `defaultColor` property has a setter that overrides the hard-coded default color, "red". Revise the directive's `onMouseEnter` so that it first tries to highlight with the `highlightColor`,
You don't need a getter. then with the `defaultColor`, and falls back to "red" if both properties are undefined.
+makeExample('attribute-directives/ts/app/highlight.directive.ts', 'mouse-enter')(format=".")
How do you bind to it? The app is already using `myHighlight` attribute name as a binding target.
Remember that a *component is a directive, too*.
You can add as many component property bindings as you need by stringing them along in the template
as in this example that sets the `a`, `b`, `c` properties to the string literals 'a', 'b', and 'c'.
code-example(format="." ).
&lt;my-component [a]="'a'" [b]="'b'" [c]="'c'">&lt;my-component>
:marked :marked
The same holds true for an attribute directive. How do you bind to a second property when you're already binding to the `myHighlight` attribute name?
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.
+makeExample('attribute-directives/ts/app/app.component.html', 'defaultColor')(format=".") +makeExample('attribute-directives/ts/app/app.component.html', 'defaultColor')(format=".")
:marked :marked
Here the code is binding the user's color choice to the `myHighlight` attribute as before. Angular knows that the `defaultColor` binding belongs to the `HighlightDirective`
It is *also* binding the literal string, 'violet', to the `defaultColor`. because you made it _public_ with the `@Input` !{_decorator}.
Here's how the harness should work when you're done coding.
Here is the final version of the directive in action.
figure.image-display figure.image-display
img(src="/resources/images/devguide/attribute-directives/highlight-directive-final-anim.gif" alt="Final Highlight") img(src="/resources/images/devguide/attribute-directives/highlight-directive-final-anim.gif" alt="Final Highlight")
@ -308,12 +325,12 @@ figure.image-display
:marked :marked
## Summary ## Summary
This page covered how to: This page covered how to:
- [Build a simple **attribute directive** to attach behavior to an HTML element](#write-directive). - [Build an **attribute directive**](#write-directive) that modifies the behavior of an element.
- [Use that directive in a template](#apply-directive). - [Apply the directive](#apply-directive) to an element in a template.
- [Respond to **events** to change behavior based on an event](#respond-to-user). - [Respond to **events**](#respond-to-user) that change the directive's behavior.
- [Use **binding** to pass values to the attribute directive](#bindings). - [**Bind** values to the directive](#bindings).
The final source: The final source code follows:
+makeTabs( +makeTabs(
`attribute-directives/ts/app/app.component.ts, `attribute-directives/ts/app/app.component.ts,
@ -323,7 +340,7 @@ figure.image-display
attribute-directives/ts/app/main.ts, attribute-directives/ts/app/main.ts,
attribute-directives/ts/index.html attribute-directives/ts/index.html
`, `,
',,full', '',
`app.component.ts, `app.component.ts,
app.component.html, app.component.html,
highlight.directive.ts, highlight.directive.ts,
@ -332,43 +349,57 @@ figure.image-display
index.html index.html
`) `)
:marked
You can also experience and download the <live-example title="Attribute Directive example"></live-example>.
a#why-input a#why-input
.l-main-section .l-main-section
:marked :marked
### Appendix: Input properties ### Appendix: Why add _@Input_?
In this demo, the `highlightColor` property is an ***input*** property of In this demo, the `hightlightColor` property is an ***input*** property of
`HighlightDirective`. the `HighlightDirective`. You've seen it applied without an alias:
+makeExample('attribute-directives/ts/app/highlight.directive.2.ts','color')
You've seen properties in bindings before but never had to declare them as anything. Why now?
Angular makes a subtle but important distinction between binding **sources** and **targets**.
In all previous bindings, the directive or component property was a binding ***source***.
A property is a *source* if it appears in the template expression to the ***right*** of the equals (=).
A property is a *target* when it appears in **square brackets** ([ ]) to the **left** of the equals (=)
as it is does when binding to the `myHighlight` property of the `HighlightDirective`.
+makeExample('attribute-directives/ts/app/app.component.html','pHost')(format=".")
:marked :marked
The 'color' in `[myHighlight]="color"` is a binding ***source***. You've seen it with an alias:
A source property doesn't require a declaration. +makeExample('attribute-directives/ts/app/highlight.directive.ts','color')
The 'myHighlight' in `[myHighlight]="color"` *is* a binding ***target***. :marked
You must declare it as an *input* property or Either way, the `@Input` !{_decorator} tells Angular that this property is
Angular rejects the binding with a clear error. _public_ and available for binding by a parent component.
Without `@Input`, Angular refuses to bind to the property.
Angular treats a *target* property differently for a good reason. You've bound template HTML to component properties before and never used `@Input`.
A component or directive in target position needs protection. What's different?
Imagine that `HighlightDirective` did truly wonderous things in a The difference is a matter of trust.
popular open source project. 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}.
Surprisingly, some people &mdash; perhaps naively &mdash; But a component or directive shouldn't blindly trust _other_ components and directives.
start binding to *every* property of the directive. The properties of a component or directive are hidden from binding by default.
Not just the one or two properties you expected them to target. *Every* property. They are _private_ from an Angular binding perspective.
That could really mess up your directive in ways you didn't anticipate and have no desire to support. 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.
You can tell if `@Input` is needed by the position of the property name in a binding.
The ***input*** declaration ensures that consumers of your directive can only bind to * When it appears in the template expression to the ***right*** of the equals (=),
the properties of the public API but nothing else. 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:
+makeExample('attribute-directives/ts/app/app.component.html','color')(format=".")
:marked
* 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 `MyHighlightDirective`,
not a property of the template's component. There are trust issues.
Therefore, the directive property must carry the `@Input` !{_decorator}.