From dee9bd1f8479750cd9292ad6f20ac9c949784c9c Mon Sep 17 00:00:00 2001 From: Ward Bell Date: Mon, 19 Dec 2016 17:12:42 -0800 Subject: [PATCH] docs(attribute-directives): clarify @Input and aliasing (#3007) --- .../ts/app/app.component.1.html | 11 + .../ts/app/app.component.1.ts | 12 + .../ts/app/app.component.html | 13 +- .../ts/app/app.component.ts | 7 +- .../ts/app/dummy.module.1.ts | 17 + .../ts/app/highlight.directive.2.ts | 17 +- .../ts/app/highlight.directive.3.ts | 27 ++ .../ts/app/highlight.directive.ts | 22 +- .../attribute-directives/ts/plnkr.json | 4 +- .../ts/latest/guide/attribute-directives.jade | 295 ++++++++++-------- 10 files changed, 267 insertions(+), 158 deletions(-) create mode 100644 public/docs/_examples/attribute-directives/ts/app/app.component.1.ts create mode 100644 public/docs/_examples/attribute-directives/ts/app/dummy.module.1.ts create mode 100644 public/docs/_examples/attribute-directives/ts/app/highlight.directive.3.ts diff --git a/public/docs/_examples/attribute-directives/ts/app/app.component.1.html b/public/docs/_examples/attribute-directives/ts/app/app.component.1.html index e5ee1c6463..a345271921 100644 --- a/public/docs/_examples/attribute-directives/ts/app/app.component.1.html +++ b/public/docs/_examples/attribute-directives/ts/app/app.component.1.html @@ -2,6 +2,17 @@

My First Attribute Directive

Highlight me!

+ + +

Highlighted in yellow

+

Highlighted in orange

+ + + + +

Highlighted with parent component's color

+ +

I am green with envy!

diff --git a/public/docs/_examples/attribute-directives/ts/app/app.component.1.ts b/public/docs/_examples/attribute-directives/ts/app/app.component.1.ts new file mode 100644 index 0000000000..13bb5ef1f4 --- /dev/null +++ b/public/docs/_examples/attribute-directives/ts/app/app.component.1.ts @@ -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 diff --git a/public/docs/_examples/attribute-directives/ts/app/app.component.html b/public/docs/_examples/attribute-directives/ts/app/app.component.html index e4e445b1a8..0ef41b925d 100644 --- a/public/docs/_examples/attribute-directives/ts/app/app.component.html +++ b/public/docs/_examples/attribute-directives/ts/app/app.component.html @@ -1,20 +1,27 @@

My First Attribute Directive

+

Pick a highlight color

Green Yellow Cyan
- +

Highlight me!

- + -

+

Highlight me too!

+ +
+

Mouse over the following lines to see fixed highlights

+ +

Highlighted in yellow

+

Highlighted in orange

diff --git a/public/docs/_examples/attribute-directives/ts/app/app.component.ts b/public/docs/_examples/attribute-directives/ts/app/app.component.ts index cc57c9e6b1..800a5864d6 100644 --- a/public/docs/_examples/attribute-directives/ts/app/app.component.ts +++ b/public/docs/_examples/attribute-directives/ts/app/app.component.ts @@ -6,6 +6,9 @@ import { Component } from '@angular/core'; selector: 'my-app', templateUrl: 'app.component.html' }) - -export class AppComponent { } +// #docregion class +export class AppComponent { + color: string; +} +// #enddocregion class // #enddocregion diff --git a/public/docs/_examples/attribute-directives/ts/app/dummy.module.1.ts b/public/docs/_examples/attribute-directives/ts/app/dummy.module.1.ts new file mode 100644 index 0000000000..7ba41d53bb --- /dev/null +++ b/public/docs/_examples/attribute-directives/ts/app/dummy.module.1.ts @@ -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 { } diff --git a/public/docs/_examples/attribute-directives/ts/app/highlight.directive.2.ts b/public/docs/_examples/attribute-directives/ts/app/highlight.directive.2.ts index 6b2101b543..156fabaaa8 100644 --- a/public/docs/_examples/attribute-directives/ts/app/highlight.directive.2.ts +++ b/public/docs/_examples/attribute-directives/ts/app/highlight.directive.2.ts @@ -1,15 +1,26 @@ -/* tslint:disable:no-unused-variable */ +/* tslint:disable:no-unused-variable member-ordering */ +// #docplaster // #docregion import { Directive, ElementRef, HostListener, Input } from '@angular/core'; @Directive({ selector: '[myHighlight]' }) - export class HighlightDirective { // #docregion ctor constructor(private el: ElementRef) { } // #enddocregion ctor + // #enddocregion + + // #docregion color + @Input() highlightColor: string; + // #enddocregion color + + // #docregion color-2 + @Input() myHighlight: string; + // #enddocregion color-2 + + // #docregion // #docregion mouse-methods, host @HostListener('mouseenter') onMouseEnter() { @@ -26,7 +37,7 @@ export class HighlightDirective { // #enddocregion host private highlight(color: string) { - this.el.nativeElement.style.backgroundColor = 'yellow'; + this.el.nativeElement.style.backgroundColor = color; } // #enddocregion mouse-methods diff --git a/public/docs/_examples/attribute-directives/ts/app/highlight.directive.3.ts b/public/docs/_examples/attribute-directives/ts/app/highlight.directive.3.ts new file mode 100644 index 0000000000..bf76769c93 --- /dev/null +++ b/public/docs/_examples/attribute-directives/ts/app/highlight.directive.3.ts @@ -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; + } +} diff --git a/public/docs/_examples/attribute-directives/ts/app/highlight.directive.ts b/public/docs/_examples/attribute-directives/ts/app/highlight.directive.ts index 5f5c33b0db..68c9f0cc73 100644 --- a/public/docs/_examples/attribute-directives/ts/app/highlight.directive.ts +++ b/public/docs/_examples/attribute-directives/ts/app/highlight.directive.ts @@ -1,24 +1,20 @@ /* tslint:disable:member-ordering */ // #docplaster -// #docregion full +// #docregion +// #docregion imports import { Directive, ElementRef, HostListener, Input } from '@angular/core'; +// #enddocregion imports @Directive({ selector: '[myHighlight]' }) -// #docregion class export class HighlightDirective { - private _defaultColor = 'red'; constructor(private el: ElementRef) { } - // #enddocregion class // #docregion defaultColor - @Input() set defaultColor(colorName: string){ - this._defaultColor = colorName || this._defaultColor; - } + @Input() defaultColor: string; // #enddocregion defaultColor - // #docregion class // #docregion color @Input('myHighlight') highlightColor: string; @@ -26,9 +22,10 @@ export class HighlightDirective { // #docregion mouse-enter @HostListener('mouseenter') onMouseEnter() { - this.highlight(this.highlightColor || this._defaultColor); + this.highlight(this.highlightColor || this.defaultColor || 'red'); } // #enddocregion mouse-enter + @HostListener('mouseleave') onMouseLeave() { this.highlight(null); } @@ -37,10 +34,3 @@ export class HighlightDirective { this.el.nativeElement.style.backgroundColor = color; } } -// #enddocregion class -// #enddocregion full -/* -// #docregion highlight -@Input() myHighlight: string; -// #enddocregion highlight -*/ diff --git a/public/docs/_examples/attribute-directives/ts/plnkr.json b/public/docs/_examples/attribute-directives/ts/plnkr.json index 7124dcb6a7..a64cbd2e95 100644 --- a/public/docs/_examples/attribute-directives/ts/plnkr.json +++ b/public/docs/_examples/attribute-directives/ts/plnkr.json @@ -3,7 +3,7 @@ "files":[ "!**/*.d.ts", "!**/*.js", - "!app/*.[1,2].*" + "!app/*.[1,2,3].*" ], "tags": ["attribute", "directive"] -} \ No newline at end of file +} diff --git a/public/docs/ts/latest/guide/attribute-directives.jade b/public/docs/ts/latest/guide/attribute-directives.jade index 911e815b02..b3d9efc990 100644 --- a/public/docs/ts/latest/guide/attribute-directives.jade +++ b/public/docs/ts/latest/guide/attribute-directives.jade @@ -13,10 +13,10 @@ block includes * [Build a simple attribute directive](#write-directive) * [Apply the attribute directive to an element in a template](#apply-directive) * [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) - Try the . + Try the . .l-main-section a#directive-overview @@ -116,7 +116,7 @@ a#apply-directive :marked ## Apply the attribute directive 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 (`

`) element. In Angular terms, the `

` element will be the attribute **host**. p | Put the template in its own @@ -141,23 +141,22 @@ figure.image-display ### Your directive isn't working? 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: code-example(format="nocode"). EXCEPTION: Template parse errors: Can't bind to 'myHighlight' since it isn't a known property of 'p'. :marked - Angular detects that you're trying to bind to *something* but it doesn't know what, - so it looks to the `declarations` metadata array. By specifying `HighlightDirective` - in the array, Angular knows to check the import statements and from there, - to go to `highlight.directive.ts` to find out what `myHighlight` does. + 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. :marked - To summarize, Angular found the `myHighlight` attribute on the `

` element. It created - an instance of the `HighlightDirective` class, - injecting a reference to the element into the constructor - where the `

` element's background style is set to yellow. + To summarize, Angular found the `myHighlight` attribute on the `

` element. + It created an instance of the `HighlightDirective` class and + injected a reference to the `

` element into the directive's constructor + which sets the `

` element's background style to yellow. .l-main-section a#respond-to-user @@ -165,37 +164,37 @@ a#respond-to-user ## Respond to user-initiated events 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: - 1. detecting when the user hovers into and out of the element. - 2. responding to those actions by setting and clearing the highlight color. + Begin by adding `HostListener` to the list of imported symbols; + add the `Import` symbol as well because you'll need it soon. ++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 `

` in this case. .l-sub-section :marked - The `@HostListener` !{_decorator} refers to the DOM element that hosts an attribute directive, the `

` in this case. - - 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: + 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: 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. Talking to DOM API directly isn't a best practice. :marked - Now implement the two mouse event handlers: -+makeExample('attribute-directives/ts/app/highlight.directive.2.ts','mouse-methods')(format=".") -: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. + The handlers delegate to a helper method that sets the color on the DOM element, `#{_priv}el`, + which you declare and initialize in the constructor. +makeExample('attribute-directives/ts/app/highlight.directive.2.ts','ctor')(format=".") :marked - Here's the updated directive: + Here's the updated directive in full: +makeExample('app/highlight.directive.2.ts') :marked 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 a#bindings :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. - A better practice is to set the color externally with a binding as follows: -+makeExample('attribute-directives/ts/app/app.component.html','pHost') -:marked - You can extend the directive class with a bindable **input** `highlightColor` property and use it to highlight text. + Currently the highlight color is hard-coded _within_ the directive. That's inflexible. + Let the user of the directive set the color in the template with a binding. + + Start by adding a `highlightColor` property to the directive class like this: ++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 :marked - The new `highlightColor` property is called an *input* property because data flows from the binding expression into the directive. - 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. + ### Binding to an _@Input_ property - 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 - Now that you're getting the highlight color as an input, modify the `onMouseEnter()` method to use - it instead of the hard-coded color name and define red as the default color. - -+makeExcerpt('attribute-directives/ts/app/highlight.directive.ts', 'mouse-enter', '') + Add a `color` property to the `AppComponent`. ++makeExample('attribute-directives/ts/app/app.component.1.ts','class', 'app/app.component.ts (class)')(format='.') :marked - To let users pick the highlight color and bind their choice to the directive, - update `app.component.html` as follows: + Let it control the highlight color with a property binding. ++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 `

` 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', '') -.l-sub-section - :marked - ### Where is the templated *color* property? - - 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 + Revise the `AppComponent.color` so that it has no initial value. ++makeExcerpt('attribute-directives/ts/app/app.component.ts', 'class', '') :marked - Here is the second version of the directive in action. + Here is the harness and directive in action. figure.image-display 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 :marked ## 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—the color that prevails until the user picks a highlight color. - To do this, first add a second **input** property to `HighlightDirective` called `defaultColor`: -+makeExample('attribute-directives/ts/app/highlight.directive.ts', 'defaultColor')(format=".") + 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. + + Add a second **input** property to `HighlightDirective` called `defaultColor`: ++makeExcerpt('attribute-directives/ts/app/highlight.directive.ts', 'defaultColor','app/highlight.directive.ts (defaultColor)') :marked - The `defaultColor` property has a setter that overrides the hard-coded default color, "red". - You don't need a getter. - - 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="." ). - <my-component [a]="'a'" [b]="'b'" [c]="'c'"><my-component> + 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. ++makeExample('attribute-directives/ts/app/highlight.directive.ts', 'mouse-enter')(format=".") :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=".") :marked - Here the code is binding the user's color choice to the `myHighlight` attribute as before. - It is *also* binding the literal string, 'violet', to the `defaultColor`. + Angular knows that the `defaultColor` binding belongs to the `HighlightDirective` + because you made it _public_ with the `@Input` !{_decorator}. - - Here is the final version of the directive in action. + Here's how the harness should work when you're done coding. figure.image-display img(src="/resources/images/devguide/attribute-directives/highlight-directive-final-anim.gif" alt="Final Highlight") @@ -308,12 +325,12 @@ figure.image-display :marked ## Summary This page covered how to: - - [Build a simple **attribute directive** to attach behavior to an HTML element](#write-directive). - - [Use that directive in a template](#apply-directive). - - [Respond to **events** to change behavior based on an event](#respond-to-user). - - [Use **binding** to pass values to the attribute directive](#bindings). + - [Build an **attribute directive**](#write-directive) that modifies the behavior of an element. + - [Apply the directive](#apply-directive) to an element in a template. + - [Respond to **events**](#respond-to-user) that change the directive's behavior. + - [**Bind** values to the directive](#bindings). - The final source: + The final source code follows: +makeTabs( `attribute-directives/ts/app/app.component.ts, @@ -323,7 +340,7 @@ figure.image-display attribute-directives/ts/app/main.ts, attribute-directives/ts/index.html `, - ',,full', + '', `app.component.ts, app.component.html, highlight.directive.ts, @@ -332,43 +349,57 @@ figure.image-display index.html `) +:marked + You can also experience and download the . a#why-input .l-main-section :marked - ### Appendix: Input properties + ### Appendix: Why add _@Input_? - In this demo, the `highlightColor` property is an ***input*** property of - `HighlightDirective`. - - 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=".") + In this demo, the `hightlightColor` property is an ***input*** property of + the `HighlightDirective`. You've seen it applied without an alias: ++makeExample('attribute-directives/ts/app/highlight.directive.2.ts','color') :marked - The 'color' in `[myHighlight]="color"` is a binding ***source***. - A source property doesn't require a declaration. + You've seen it with an alias: ++makeExample('attribute-directives/ts/app/highlight.directive.ts','color') - The 'myHighlight' in `[myHighlight]="color"` *is* a binding ***target***. - You must declare it as an *input* property or - Angular rejects the binding with a clear error. +:marked + 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. - Angular treats a *target* property differently for a good reason. - A component or directive in target position needs protection. + You've bound template HTML to component properties before and never used `@Input`. + What's different? - Imagine that `HighlightDirective` did truly wonderous things in a - popular open source project. + 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}. - Surprisingly, some people — perhaps naively — - start binding to *every* property of the directive. - Not just the one or two properties you expected them to target. *Every* property. - That could really mess up your directive in ways you didn't anticipate and have no desire to support. + 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. + + 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 - the properties of the public API but nothing else. + * 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: ++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}.