From 6519ad0f864568a2665b09f0841cb35e8271edf1 Mon Sep 17 00:00:00 2001 From: Kathy Walrath Date: Wed, 6 Jan 2016 10:45:40 -0800 Subject: [PATCH 1/7] Fix print styles --- public/resources/css/_print.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/resources/css/_print.scss b/public/resources/css/_print.scss index 09bf4a2eb0..f027ab8427 100644 --- a/public/resources/css/_print.scss +++ b/public/resources/css/_print.scss @@ -48,7 +48,7 @@ code { background: transparent; display: inline-block; - } +} th { background: #4D6A79; color: white; From 82c3267814329478f04f7470c593f8789b79e892 Mon Sep 17 00:00:00 2001 From: Kathy Walrath Date: Mon, 11 Jan 2016 14:56:59 -0800 Subject: [PATCH 2/7] Unchanged TS version of user-input --- public/docs/dart/latest/guide/user-input.jade | 423 ++++++++++-------- 1 file changed, 234 insertions(+), 189 deletions(-) diff --git a/public/docs/dart/latest/guide/user-input.jade b/public/docs/dart/latest/guide/user-input.jade index 9ce55e0f3c..d9a8ee8ac3 100644 --- a/public/docs/dart/latest/guide/user-input.jade +++ b/public/docs/dart/latest/guide/user-input.jade @@ -1,199 +1,244 @@ +include ../../../../_includes/_util-fns + +:marked + When the user clicks a link, pushes a button, or enters text + we want to know about it. These user actions all raise DOM events. + In this chapter we learn to bind to those events using the Angular + event binding syntax. + + [Run the live example](/resources/live-examples/user-input/ts/plnkr.html) + +:marked + ## Binding to user input events + + We can use [Angular event bindings](./template-syntax.html#event-binding) + to respond to [any DOM event](https://developer.mozilla.org/en-US/docs/Web/Events). + + The syntax is simple. We assign a template expression to the DOM event name, surrounded in parentheses. + As an example, here's an event binding that implements a click handler: ++makeExample('user-input/ts/app/click-me.component.ts', 'click-me-button')(format=".", language="html") + + +:marked + The `(click)` to the left of the equal sign identifies the button's click event as the **target of the binding**. + The text within quotes on the right is the **template expression** in which we + respond to the click event by calling the component's `onClickMe` method. A [template expression](./template-syntax.html#template-expressions) is a subset + of JavaScript with a few added tricks. + + When writing a binding we must be aware of a template expression's **execution context**. + The identifiers appearing within an expression belong to a specific context object. + That object is usually the Angular component that controls the template ... which it definitely is + in this case because that snippet of HTML belongs to the following component: + ++makeExample('user-input/ts/app/click-me.component.ts', 'click-me-component', 'app/click-me.component.ts')(format=".") +:marked + When the user clicks the button, Angular calls the component's `onClickMe` method. + .l-main-section - p. - Use the event syntax (eventName) to - make your application respond to user input. - p. - You can specify the event handler—a method in the component controller—like this: - - code-example(language="html"). - <input (keyup)="myControllerMethod()"> - p. - As in previous examples, you can make element references available to - other parts of the template as a local - variable using the # syntax. - Using # and events, - you can write the old "update text as you type" example: - - - code-example(language="html"). - <input #myname (keyup)> - <p>{{myname.value}}</p> - - p.text-body(ng-non-bindable). - In that example, #myname creates a local variable in the template that - the <p> element can refer to. - The (keyup) tells Angular to trigger updates when it gets a keyup - event. And {{myname.value}} binds the text node of the - <p> element to the - input's value property. - - p. - Let's do something a little more complex, where the user enters items - that the app adds to a list: - figure.image-display - img(src='/resources/images/examples/user-input-example1.png' alt="Example of Todo App") +:marked + ## Get user input from the $event object + We can bind to all kinds of events. Let's bind to the keyup event of an input box and replay + what the user types back onto the screen. + + This time we'll (1) listen to an event and (2) grab the user's input. ++makeExample('user-input/ts/app/keyup.components.ts', 'key-up-component-1-template', 'app/keyup.components.ts (template v.1)')(format=".") +:marked + Angular makes an event object available in the **`$event`** variable, + which we pass to the component's `onKey()` method. + The user data we want is in that variable somewhere. ++makeExample('user-input/ts/app/keyup.components.ts', 'key-up-component-1-class-no-type', 'app/keyup.components.ts (class v.1)')(format=".") +:marked + The shape of the `$event` object is determined by whatever raises the event. + The `keyup` event comes from the DOM, so `$event` must be a [standard DOM event object](https://developer.mozilla.org/en-US/docs/Web/API/Event). + The `$event.target` gives us an + [`HTMLInputElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement), which + has a `value` property that contains our user input data. + + The `onKey()` component method is where we extract the user's input + from the event object, adding that input to the list of user data that we're accumulating in the component's `values` property. + We then use [interpolation](./template-syntax.html#interpolation) + to display the accumulating `values` property back on screen. + + Enter the letters "abc", and then backspace to remove them. + Here's what the UI displays: +code-example(). + a | ab | abc | ab | a | | +figure.image-display + img(src='/resources/images/devguide/user-input/keyup1-anim.gif' alt="key up 1") + + +.l-sub-section + :marked + We cast the `$event` as an `any` type, which means we've abandoned strong typing + to simplify our code. We generally prefer the strong typing that TypeScript affords. + We can rewrite the method, casting to HTML DOM objects like this. + +makeExample('user-input/ts/app/keyup.components.ts', 'key-up-component-1-class', 'app/keyup.components.ts (class v.1 - strongly typed )')(format=".") + :marked +
Strong typing reveals a serious problem with passing a DOM event into the method: + too much awareness of template details, too little separation of concerns. + + We'll address this problem in our next try at processing user keystrokes. +:marked +.l-main-section +:marked + ## Get user input from a local template variable + There's another way to get the user data without the `$event` variable. + + Angular has a syntax feature called [**local template variables**](./template-syntax.html#local-vars). + These variables grant us direct access to an element. + We declare a local template variable by preceding an identifier with a hash/pound character (#). + + Here's an example of using a local template variable + to implement a clever keystroke loopback in an ultra-simple template. ++makeExample('user-input/ts/app/loop-back.component.ts', 'loop-back-component', 'app/loop-back.component.ts')(format=".") +:marked + We've declared a template local variable named `box` on the `` element. + The `box` variable is a reference to the `` element itself, which means we can + grab the input element's `value` and display it + with interpolation between `

` tags. + + The template is completely self contained. It doesn't bind to the component, + and the component does nothing. + + Type in the input box, and watch the display update with each keystroke. *Voila!* + +figure.image-display + img(src='/resources/images/devguide/user-input/keyup-loop-back-anim.gif' alt="loop back") +.l-sub-section + :marked + **This won't work at all unless we bind to an event**. + + Angular only updates the bindings (and therefore the screen) + if we do something in response to asynchronous events such as keystrokes. + + That's why we bind the `keyup` event to an expression that does ... well, nothing. + We're binding to the number 0, the shortest expression we can think of. + That is all it takes to keep Angular happy. We said it would be clever! +:marked + That local template variable is intriguing. It's clearly easier to get to the textbox with that + variable than to go through the `$event` object. Maybe we can rewrite our previous + keyup example so that it uses the variable to get the user's input. Let's give it a try. ++makeExample('user-input/ts/app/keyup.components.ts', 'key-up-component-2' ,'app/keyup.components.ts (v2)')(format=".") +:marked + That sure seems easier. + An especially nice aspect of this approach is that our component code gets clean data values from the view. + It no longer requires knowledge of the `$event` and its structure. + + +.l-main-section +:marked + ## Key event filtering (with `key.enter`) + Perhaps we don't care about every keystroke. + Maybe we're only interested in the input box value when the user presses Enter, and we'd like to ignore all other keys. + When we bind to the `(keyup)` event, our event handling expression hears *every keystroke*. + We could filter the keys first, examining every `$event.keyCode`, and update the `values` property only if the key is Enter. + + Angular can filter the key events for us. Angular has a special syntax for keyboard events. + We can listen for just the Enter key by binding to Angular's `keyup.enter` pseudo-event. + + Only then do we update the component's `values` property. (In this example, + the update happens inside the event expression. A better practice + would be to put the update code in the component.) ++makeExample('user-input/ts/app/keyup.components.ts', 'key-up-component-3' ,'app/keyup.components.ts (v3)')(format=".") +:marked + Here's how it works. +figure.image-display + img(src='/resources/images/devguide/user-input/keyup3-anim.gif' alt="key up 3") .l-main-section - h2#section-create-an-array-property Create a list property - p. - With the default files in place, - create a TodoController class to manage interactions with the - list. Inside TodoController, add a list with some initial items. - Then add a method that adds new items - to the list. - - code-example(language="dart"). - class TodoList { - List<String> todos =[ - 'Eat breakfast', - 'Walk dog', - 'Breathe', - 'Learn Angular' - ]; - - addTodo(String todo) { - todos.add(todo); - } - } - -.callout.is-helpful - header Production Best Practice - p. - As shown in the previous example, a production application you would - separate the model out into another class - and inject it into TodoController. - We've omitted that here for brevity. - -

+:marked + ## On blur + + Our previous example won't transfer the current state of the input box if the user mouses away and clicks + elsewhere on the page. We update the component's `values` property only when the user presses Enter + while the focus is inside the input box. + + Let's fix that by listening to the input box's blur event as well. + ++makeExample('user-input/ts/app/keyup.components.ts', 'key-up-component-4' ,'app/keyup.components.ts (v4)')(format=".") .l-main-section - h2#section-display-the-list-of-todos Display the list of todos - p. - Using the *ng-for iterator, create an <li> for each item in the todos list and set - its text to the value. - - code-example(language="html"). - <ul> - <li *ng-for="#todo of todos"> - {{ todo }} - </li> - </ul> +:marked + ## Put it all together + We learned how to [display data](./displaying-data.html) in the previous chapter. + We've acquired a small arsenal of event binding techniques in this chapter. - + Let's put it all together in a micro-app + that can display a list of heroes and add new heroes to that list. + The user can add a hero by first typing in the input box and then + pressing Enter, clicking the Add button, or clicking elsewhere on the page. + +figure.image-display + img(src='/resources/images/devguide/user-input/little-tour-anim.gif' alt="Little Tour of Heroes") +:marked + Below is the "Little Tour of Heroes" component. + We'll call out the highlights after we bask briefly in its minimalist glory. + ++makeExample('user-input/ts/app/little-tour.component.ts', 'little-tour', 'app/little-tour.component.ts')(format=".") +:marked + We've seen almost everything here before. A few things are new or bear repeating. + + ### Use template variables to refer to elements + + The `newHero` template variable refers to the `` element. + We can use `newHero` from any sibling or child of the `` element. + + Getting the element from a template variable makes the button click handler + simpler. Without the variable, we'd have to use a fancy CSS selector + to find the input element. + + ### Pass values, not elements + + We could have passed the `newHero` into the component's `addHero` method. + + But that would require `addHero` to pick its way through the `` DOM element, + something we learned to dislike in our first try at a [keyup component](#keyup1). + + Instead, we grab the input box *value* and pass *that* to `addHero`. + The component knows nothing about HTML or the DOM, which is the way we like it. + + ### Keep template expressions simple + We bound `(blur)` to *two* JavaScript statements. + + We like the first one, which calls `addHero`. + We do not like the second one, which assigns an empty string to the input box value. + + The second statement exists for a good reason. We have to clear the input box after adding the new hero to the list. + The component has no way to do that itself because it has no access to the + input box (our design choice). + + Although the example *works*, we are rightly wary of JavaScript in HTML. + Template expressions are powerful. We're supposed to use them responsibly. + Complex JavaScript in HTML is irresponsible. + + Should we reconsider our reluctance to pass the input box into the component? + + There should be a better third way. And there is, as we'll see when we learn about `NgModel` in the [Forms](forms.html) chapter. .l-main-section - h2#section-add-todos-to-the-list Add todos to the list via button click - p. - Now, add a text input and a button for users to add items to the list. As you saw above, you can create a local - variable reference in your template with #varname. Call it #todotext here. - - code-example(language="html"). - <input #todotext> - p. - Specify the target of the click event binding as your controller's - addTodo() method and pass - it the value. Since you created a reference called todotext, - you can get the value with todotext.value. - - code-example(language="html"). - <button (click)="addTodo(todotext.value)">Add Todo</button> - - p. - To make pressing Enter do something useful, - you can add a keyup event handler to the input field. - This event handler uses APIs defined in - dart:html, - so be sure to import that library. - - code-example(language="dart"). - import 'dart:html'; - ... - - // In the template: - <input #todotext (keyup)="doneTyping(\$event)"> - ... - - // In the component controller class: - doneTyping(KeyboardEvent event) { - if (event.keyCode == KeyCode.ENTER) { - InputElement e = event.target; - addTodo(e.value); - e.value = null; - } - } +:marked + ## Source code + + Here is all the code we talked about in this chapter. ++makeTabs(` + user-input/ts/app/click-me.component.ts, + user-input/ts/app/keyup.components.ts, + user-input/ts/app/loop-back.component.ts, + user-input/ts/app/little-tour.component.ts + `,'', + `click-me.component.ts, + keyup.components.ts, + loop-back.component.ts, + little-tour.component.ts`) .l-main-section - h2#section-final-code Final code - - code-tabs - code-pane(language="dart" name="lib/todo_list.dart" format="linenums"). - library user_input.todo_list; - - import 'dart:html'; - import 'package:angular2/angular2.dart'; - - @Component(selector: 'todo-list') - @View( - // An alternative to using \$event is to use a raw string instead. - // For example, change "template: '''" to "template: r'''". - template: ''' - <ul> - <li *ng-for="#todo of todos"> - {{ todo }} - </li> - </ul> - - <input #todotext (keyup)="doneTyping(\$event)"> - <button (click)="addTodo(todotext.value)">Add Todo</button> - ''', directives: const [NgFor]) - class TodoList { - List<String> todos = [ - 'Eat breakfast', - 'Walk dog', - 'Breathe', - 'Learn Angular' - ]; - - addTodo(String todo) { - todos.add(todo); - } - - doneTyping(KeyboardEvent event) { - if (event.keyCode == KeyCode.ENTER) { - InputElement e = event.target; - addTodo(e.value); - e.value = null; - } - } - } - code-pane(language="dart" name="web/main.dart" format="linenums"). - import 'package:angular2/bootstrap.dart'; - import 'package:user_input/todo_list.dart'; - - main() { - bootstrap(TodoList); - } - code-pane(language="html" name="web/index.html" format="linenums"). - <!DOCTYPE html> - <html> - <head> - <title>User Input</title> - <link rel="stylesheet" href="style.css"> - <script async src="main.dart" type="application/dart"></script> - <script async src="packages/browser/dart.js"></script> - </head> - <body> - <todo-list></todo-list> - </body> - </html> - code-pane(language="yaml" name="pubspec.yaml" format="linenums"). - name: user_input - description: User Input example - version: 0.0.1 - dependencies: - angular2: 2.0.0-alpha.45 - browser: ^0.10.0 - transformers: - - angular2: - entry_points: web/main.dart +:marked + ## Summary + + We've mastered the basic primitives for responding to user input and gestures. + As powerful as these primitives are, they are a bit clumsy for handling + large amounts of user input. We're operating down at the low level of events when + we should be writing two-way bindings between data entry fields and model properties. + + Angular has a two-way binding called `NgModel`, which we'll learn about + in the `Forms` chapter. From bc31cbc2a0ce91c0d00d173ed83f6436bb9dc757 Mon Sep 17 00:00:00 2001 From: Kathy Walrath Date: Mon, 11 Jan 2016 15:32:30 -0800 Subject: [PATCH 3/7] Incorporated remaining comments on user-input sample --- public/docs/_examples/user-input/dart/lib/app_component.dart | 2 +- .../dart/lib/{app-component.html => app_component.html} | 0 .../_examples/user-input/dart/lib/little_tour_component.dart | 2 -- public/docs/_examples/user-input/dart/pubspec.yaml | 2 +- 4 files changed, 2 insertions(+), 4 deletions(-) rename public/docs/_examples/user-input/dart/lib/{app-component.html => app_component.html} (100%) diff --git a/public/docs/_examples/user-input/dart/lib/app_component.dart b/public/docs/_examples/user-input/dart/lib/app_component.dart index 5aacf15756..88bc17ed73 100644 --- a/public/docs/_examples/user-input/dart/lib/app_component.dart +++ b/public/docs/_examples/user-input/dart/lib/app_component.dart @@ -10,7 +10,7 @@ import 'package:user_input/little_tour_component.dart'; @Component( selector: 'my-app', - templateUrl: 'app-component.html', + templateUrl: 'app_component.html', directives: const [ ClickMeComponent, ClickMeComponent2, diff --git a/public/docs/_examples/user-input/dart/lib/app-component.html b/public/docs/_examples/user-input/dart/lib/app_component.html similarity index 100% rename from public/docs/_examples/user-input/dart/lib/app-component.html rename to public/docs/_examples/user-input/dart/lib/app_component.html diff --git a/public/docs/_examples/user-input/dart/lib/little_tour_component.dart b/public/docs/_examples/user-input/dart/lib/little_tour_component.dart index 3cbc36c4a6..85de4b36ff 100644 --- a/public/docs/_examples/user-input/dart/lib/little_tour_component.dart +++ b/public/docs/_examples/user-input/dart/lib/little_tour_component.dart @@ -1,8 +1,6 @@ // #docregion library user_input.little_tour_component; -import 'dart:html'; - import 'package:angular2/angular2.dart'; // #docregion little-tour diff --git a/public/docs/_examples/user-input/dart/pubspec.yaml b/public/docs/_examples/user-input/dart/pubspec.yaml index 7789ff50be..04c75df54b 100644 --- a/public/docs/_examples/user-input/dart/pubspec.yaml +++ b/public/docs/_examples/user-input/dart/pubspec.yaml @@ -6,5 +6,5 @@ dependencies: browser: ^0.10.0 transformers: - angular2: - platform_directives: 'package:angular2/src/common/directives.dart#CORE_DIRECTIVES' + platform_directives: 'package:angular2/common.dart#CORE_DIRECTIVES' entry_points: web/main.dart From 35e48a8981c4c7e9a4aa90b132b738e393f16c42 Mon Sep 17 00:00:00 2001 From: Kathy Walrath Date: Mon, 11 Jan 2016 16:18:45 -0800 Subject: [PATCH 4/7] Minor sample changes --- .../docs/_examples/user-input/dart/lib/app_component.dart | 4 +--- .../_examples/user-input/dart/lib/click_me_component.dart | 7 ++++--- .../user-input/dart/lib/click_me_component_2.dart | 2 -- .../lib/{key_up_components.dart => keyup_components.dart} | 2 -- .../user-input/dart/lib/little_tour_component.dart | 2 -- .../_examples/user-input/dart/lib/loop_back_component.dart | 2 -- 6 files changed, 5 insertions(+), 14 deletions(-) rename public/docs/_examples/user-input/dart/lib/{key_up_components.dart => keyup_components.dart} (98%) diff --git a/public/docs/_examples/user-input/dart/lib/app_component.dart b/public/docs/_examples/user-input/dart/lib/app_component.dart index 88bc17ed73..5151e47a35 100644 --- a/public/docs/_examples/user-input/dart/lib/app_component.dart +++ b/public/docs/_examples/user-input/dart/lib/app_component.dart @@ -1,11 +1,9 @@ // #docregion -library user_input.app_component; - import 'package:angular2/angular2.dart'; import 'package:user_input/click_me_component.dart'; import 'package:user_input/click_me_component_2.dart'; import 'package:user_input/loop_back_component.dart'; -import 'package:user_input/key_up_components.dart'; +import 'package:user_input/keyup_components.dart'; import 'package:user_input/little_tour_component.dart'; @Component( diff --git a/public/docs/_examples/user-input/dart/lib/click_me_component.dart b/public/docs/_examples/user-input/dart/lib/click_me_component.dart index 1c11cca4bf..eaad88dd0c 100644 --- a/public/docs/_examples/user-input/dart/lib/click_me_component.dart +++ b/public/docs/_examples/user-input/dart/lib/click_me_component.dart @@ -1,12 +1,13 @@ // #docregion -library user_input.click_me_component; - import 'package:angular2/angular2.dart'; // #docregion click-me-component @Component( selector: 'click-me', - template: ''' + template: ''' + // #docregion click-me-button + + // #enddocregion click-me-button {{clickMessage}}''') class ClickMeComponent { String clickMessage = ''; diff --git a/public/docs/_examples/user-input/dart/lib/click_me_component_2.dart b/public/docs/_examples/user-input/dart/lib/click_me_component_2.dart index 7b1a66418e..e96399d943 100644 --- a/public/docs/_examples/user-input/dart/lib/click_me_component_2.dart +++ b/public/docs/_examples/user-input/dart/lib/click_me_component_2.dart @@ -1,6 +1,4 @@ // #docregion -library user_input.click_me_component_2; - import 'package:angular2/angular2.dart'; @Component( diff --git a/public/docs/_examples/user-input/dart/lib/key_up_components.dart b/public/docs/_examples/user-input/dart/lib/keyup_components.dart similarity index 98% rename from public/docs/_examples/user-input/dart/lib/key_up_components.dart rename to public/docs/_examples/user-input/dart/lib/keyup_components.dart index 3af1f77958..e48056a1aa 100644 --- a/public/docs/_examples/user-input/dart/lib/key_up_components.dart +++ b/public/docs/_examples/user-input/dart/lib/keyup_components.dart @@ -1,7 +1,5 @@ // #docplaster // #docregion -library user_input.key_up_components; - import 'dart:html'; import 'package:angular2/angular2.dart'; diff --git a/public/docs/_examples/user-input/dart/lib/little_tour_component.dart b/public/docs/_examples/user-input/dart/lib/little_tour_component.dart index 85de4b36ff..ba98f37d68 100644 --- a/public/docs/_examples/user-input/dart/lib/little_tour_component.dart +++ b/public/docs/_examples/user-input/dart/lib/little_tour_component.dart @@ -1,6 +1,4 @@ // #docregion -library user_input.little_tour_component; - import 'package:angular2/angular2.dart'; // #docregion little-tour diff --git a/public/docs/_examples/user-input/dart/lib/loop_back_component.dart b/public/docs/_examples/user-input/dart/lib/loop_back_component.dart index a65a135255..b79f3f5ac4 100644 --- a/public/docs/_examples/user-input/dart/lib/loop_back_component.dart +++ b/public/docs/_examples/user-input/dart/lib/loop_back_component.dart @@ -1,6 +1,4 @@ // #docregion -library user_input.loop_back_component; - import 'package:angular2/angular2.dart'; // #docregion loop-back-component From 01da1e135505b58bea0541e11b21355fc1ea0065 Mon Sep 17 00:00:00 2001 From: Kathy Walrath Date: Mon, 11 Jan 2016 16:19:03 -0800 Subject: [PATCH 5/7] Convert TS text to Dart --- public/docs/dart/latest/guide/user-input.jade | 67 +++++++++++-------- 1 file changed, 38 insertions(+), 29 deletions(-) diff --git a/public/docs/dart/latest/guide/user-input.jade b/public/docs/dart/latest/guide/user-input.jade index d9a8ee8ac3..f1edb99d0a 100644 --- a/public/docs/dart/latest/guide/user-input.jade +++ b/public/docs/dart/latest/guide/user-input.jade @@ -6,31 +6,29 @@ include ../../../../_includes/_util-fns In this chapter we learn to bind to those events using the Angular event binding syntax. - [Run the live example](/resources/live-examples/user-input/ts/plnkr.html) - :marked ## Binding to user input events - We can use [Angular event bindings](./template-syntax.html#event-binding) + We can use Angular event bindings to respond to [any DOM event](https://developer.mozilla.org/en-US/docs/Web/Events). The syntax is simple. We assign a template expression to the DOM event name, surrounded in parentheses. As an example, here's an event binding that implements a click handler: -+makeExample('user-input/ts/app/click-me.component.ts', 'click-me-button')(format=".", language="html") ++makeExample('user-input/dart/lib/click_me_component.dart', 'click-me-button')(format=".", language="html") :marked The `(click)` to the left of the equal sign identifies the button's click event as the **target of the binding**. The text within quotes on the right is the **template expression** in which we - respond to the click event by calling the component's `onClickMe` method. A [template expression](./template-syntax.html#template-expressions) is a subset - of JavaScript with a few added tricks. + respond to the click event by calling the component's `onClickMe` method. A template expression is a subset + of Dart with a few added tricks. When writing a binding we must be aware of a template expression's **execution context**. The identifiers appearing within an expression belong to a specific context object. That object is usually the Angular component that controls the template ... which it definitely is in this case because that snippet of HTML belongs to the following component: -+makeExample('user-input/ts/app/click-me.component.ts', 'click-me-component', 'app/click-me.component.ts')(format=".") ++makeExample('user-input/dart/lib/click_me_component.dart', 'click-me-component', 'web/click_me_component.dart')(format=".") :marked When the user clicks the button, Angular calls the component's `onClickMe` method. @@ -41,12 +39,22 @@ include ../../../../_includes/_util-fns what the user types back onto the screen. This time we'll (1) listen to an event and (2) grab the user's input. -+makeExample('user-input/ts/app/keyup.components.ts', 'key-up-component-1-template', 'app/keyup.components.ts (template v.1)')(format=".") ++makeExample('user-input/dart/lib/keyup_components.dart', 'key-up-component-1-template', 'web/keyup_components.dart (template v.1)')(format=".") + :marked Angular makes an event object available in the **`$event`** variable, which we pass to the component's `onKey()` method. The user data we want is in that variable somewhere. -+makeExample('user-input/ts/app/keyup.components.ts', 'key-up-component-1-class-no-type', 'app/keyup.components.ts (class v.1)')(format=".") + +important +.callout.is-important + header $event vs. \$event + :marked + Templates in Dart files need a `\` in front of the `$`. + If the template is in an HTML file, use `$event` instead of `\$event`. + ++makeExample('user-input/dart/lib/keyup_components.dart', 'key-up-component-1-class-no-type', 'web/keyup_components.dart (class v.1)')(format=".") + :marked The shape of the `$event` object is determined by whatever raises the event. The `keyup` event comes from the DOM, so `$event` must be a [standard DOM event object](https://developer.mozilla.org/en-US/docs/Web/API/Event). @@ -56,7 +64,7 @@ include ../../../../_includes/_util-fns The `onKey()` component method is where we extract the user's input from the event object, adding that input to the list of user data that we're accumulating in the component's `values` property. - We then use [interpolation](./template-syntax.html#interpolation) + We then use interpolation to display the accumulating `values` property back on screen. Enter the letters "abc", and then backspace to remove them. @@ -70,9 +78,9 @@ figure.image-display .l-sub-section :marked We cast the `$event` as an `any` type, which means we've abandoned strong typing - to simplify our code. We generally prefer the strong typing that TypeScript affords. + to simplify our code. We generally prefer the strong typing that Dart affords. We can rewrite the method, casting to HTML DOM objects like this. - +makeExample('user-input/ts/app/keyup.components.ts', 'key-up-component-1-class', 'app/keyup.components.ts (class v.1 - strongly typed )')(format=".") + +makeExample('user-input/dart/lib/keyup_components.dart', 'key-up-component-1-class', 'web/keyup_components.dart (class v.1 - strongly typed )')(format=".") :marked
Strong typing reveals a serious problem with passing a DOM event into the method: too much awareness of template details, too little separation of concerns. @@ -85,13 +93,14 @@ figure.image-display ## Get user input from a local template variable There's another way to get the user data without the `$event` variable. - Angular has a syntax feature called [**local template variables**](./template-syntax.html#local-vars). + Angular has a syntax feature called **local template variables**. + These variables grant us direct access to an element. We declare a local template variable by preceding an identifier with a hash/pound character (#). Here's an example of using a local template variable to implement a clever keystroke loopback in an ultra-simple template. -+makeExample('user-input/ts/app/loop-back.component.ts', 'loop-back-component', 'app/loop-back.component.ts')(format=".") ++makeExample('user-input/dart/lib/loop_back_component.dart', 'loop-back-component', 'web/loop_back_component.dart')(format=".") :marked We've declared a template local variable named `box` on the `` element. The `box` variable is a reference to the `` element itself, which means we can @@ -119,7 +128,7 @@ figure.image-display That local template variable is intriguing. It's clearly easier to get to the textbox with that variable than to go through the `$event` object. Maybe we can rewrite our previous keyup example so that it uses the variable to get the user's input. Let's give it a try. -+makeExample('user-input/ts/app/keyup.components.ts', 'key-up-component-2' ,'app/keyup.components.ts (v2)')(format=".") ++makeExample('user-input/dart/lib/keyup_components.dart', 'key-up-component-2' ,'web/keyup_components.dart (v2)')(format=".") :marked That sure seems easier. An especially nice aspect of this approach is that our component code gets clean data values from the view. @@ -140,7 +149,7 @@ figure.image-display Only then do we update the component's `values` property. (In this example, the update happens inside the event expression. A better practice would be to put the update code in the component.) -+makeExample('user-input/ts/app/keyup.components.ts', 'key-up-component-3' ,'app/keyup.components.ts (v3)')(format=".") ++makeExample('user-input/dart/lib/keyup_components.dart', 'key-up-component-3' ,'web/keyup_components.dart (v3)')(format=".") :marked Here's how it works. figure.image-display @@ -156,7 +165,7 @@ figure.image-display Let's fix that by listening to the input box's blur event as well. -+makeExample('user-input/ts/app/keyup.components.ts', 'key-up-component-4' ,'app/keyup.components.ts (v4)')(format=".") ++makeExample('user-input/dart/lib/keyup_components.dart', 'key-up-component-4' ,'web/keyup_components.dart (v4)')(format=".") .l-main-section :marked @@ -175,7 +184,7 @@ figure.image-display Below is the "Little Tour of Heroes" component. We'll call out the highlights after we bask briefly in its minimalist glory. -+makeExample('user-input/ts/app/little-tour.component.ts', 'little-tour', 'app/little-tour.component.ts')(format=".") ++makeExample('user-input/dart/lib/little_tour_component.dart', 'little-tour', 'web/little_tour_component.dart')(format=".") :marked We've seen almost everything here before. A few things are new or bear repeating. @@ -199,7 +208,7 @@ figure.image-display The component knows nothing about HTML or the DOM, which is the way we like it. ### Keep template expressions simple - We bound `(blur)` to *two* JavaScript statements. + We bound `(blur)` to *two* Dart statements. We like the first one, which calls `addHero`. We do not like the second one, which assigns an empty string to the input box value. @@ -208,9 +217,9 @@ figure.image-display The component has no way to do that itself because it has no access to the input box (our design choice). - Although the example *works*, we are rightly wary of JavaScript in HTML. + Although the example *works*, we are rightly wary of Dart in HTML. Template expressions are powerful. We're supposed to use them responsibly. - Complex JavaScript in HTML is irresponsible. + Complex Dart in HTML is irresponsible. Should we reconsider our reluctance to pass the input box into the component? @@ -221,15 +230,15 @@ figure.image-display Here is all the code we talked about in this chapter. +makeTabs(` - user-input/ts/app/click-me.component.ts, - user-input/ts/app/keyup.components.ts, - user-input/ts/app/loop-back.component.ts, - user-input/ts/app/little-tour.component.ts + user-input/dart/lib/click_me_component.dart, + user-input/dart/lib/keyup_components.dart, + user-input/dart/lib/loop_back_component.dart, + user-input/dart/lib/little_tour_component.dart `,'', - `click-me.component.ts, - keyup.components.ts, - loop-back.component.ts, - little-tour.component.ts`) + `click_me_component.dart, + keyup_components.dart, + loop_back_component.dart, + little_tour_component.dart`) .l-main-section :marked From dee2c3405757409bc74390c86800cdd0709b866a Mon Sep 17 00:00:00 2001 From: Kathy Walrath Date: Tue, 19 Jan 2016 10:02:35 -0800 Subject: [PATCH 6/7] replace "expression" w/ "statement" Matches changes made in b24c8139161364576c21d4f7d8d1ef8b605832ac --- public/docs/dart/latest/guide/user-input.jade | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/public/docs/dart/latest/guide/user-input.jade b/public/docs/dart/latest/guide/user-input.jade index f1edb99d0a..89b9a583b3 100644 --- a/public/docs/dart/latest/guide/user-input.jade +++ b/public/docs/dart/latest/guide/user-input.jade @@ -12,19 +12,20 @@ include ../../../../_includes/_util-fns We can use Angular event bindings to respond to [any DOM event](https://developer.mozilla.org/en-US/docs/Web/Events). - The syntax is simple. We assign a template expression to the DOM event name, surrounded in parentheses. + The syntax is simple. We surround the DOM event name with + parentheses and assign a quoted template statement to it. As an example, here's an event binding that implements a click handler: +makeExample('user-input/dart/lib/click_me_component.dart', 'click-me-button')(format=".", language="html") :marked The `(click)` to the left of the equal sign identifies the button's click event as the **target of the binding**. - The text within quotes on the right is the **template expression** in which we - respond to the click event by calling the component's `onClickMe` method. A template expression is a subset - of Dart with a few added tricks. + The text within quotes on the right is the **template statement** in which we + respond to the click event by calling the component's `onClickMe` method. A template statement is a subset + of Dart with restrictions and a few added tricks. - When writing a binding we must be aware of a template expression's **execution context**. - The identifiers appearing within an expression belong to a specific context object. + When writing a binding we must be aware of a template statement's **execution context**. + The identifiers appearing within a statement belong to a specific context object. That object is usually the Angular component that controls the template ... which it definitely is in this case because that snippet of HTML belongs to the following component: @@ -121,8 +122,8 @@ figure.image-display Angular only updates the bindings (and therefore the screen) if we do something in response to asynchronous events such as keystrokes. - That's why we bind the `keyup` event to an expression that does ... well, nothing. - We're binding to the number 0, the shortest expression we can think of. + That's why we bind the `keyup` event to a statement that does ... well, nothing. + We're binding to the number 0, the shortest statement we can think of. That is all it takes to keep Angular happy. We said it would be clever! :marked That local template variable is intriguing. It's clearly easier to get to the textbox with that @@ -140,14 +141,14 @@ figure.image-display ## Key event filtering (with `key.enter`) Perhaps we don't care about every keystroke. Maybe we're only interested in the input box value when the user presses Enter, and we'd like to ignore all other keys. - When we bind to the `(keyup)` event, our event handling expression hears *every keystroke*. + When we bind to the `(keyup)` event, our event handling statement hears *every keystroke*. We could filter the keys first, examining every `$event.keyCode`, and update the `values` property only if the key is Enter. Angular can filter the key events for us. Angular has a special syntax for keyboard events. We can listen for just the Enter key by binding to Angular's `keyup.enter` pseudo-event. Only then do we update the component's `values` property. (In this example, - the update happens inside the event expression. A better practice + the update happens inside the event binding statement. A better practice would be to put the update code in the component.) +makeExample('user-input/dart/lib/keyup_components.dart', 'key-up-component-3' ,'web/keyup_components.dart (v3)')(format=".") :marked @@ -207,7 +208,7 @@ figure.image-display Instead, we grab the input box *value* and pass *that* to `addHero`. The component knows nothing about HTML or the DOM, which is the way we like it. - ### Keep template expressions simple + ### Keep template statements simple We bound `(blur)` to *two* Dart statements. We like the first one, which calls `addHero`. @@ -218,7 +219,7 @@ figure.image-display input box (our design choice). Although the example *works*, we are rightly wary of Dart in HTML. - Template expressions are powerful. We're supposed to use them responsibly. + Template statements are powerful. We're supposed to use them responsibly. Complex Dart in HTML is irresponsible. Should we reconsider our reluctance to pass the input box into the component? From b5df43c790ad7ee28ff8e083f36aea12fb91dbb3 Mon Sep 17 00:00:00 2001 From: Kathy Walrath Date: Tue, 19 Jan 2016 10:13:48 -0800 Subject: [PATCH 7/7] Use relative imports, as suggested by adaojunior --- .../_examples/user-input/dart/lib/app_component.dart | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/public/docs/_examples/user-input/dart/lib/app_component.dart b/public/docs/_examples/user-input/dart/lib/app_component.dart index 5151e47a35..de77bbbc1d 100644 --- a/public/docs/_examples/user-input/dart/lib/app_component.dart +++ b/public/docs/_examples/user-input/dart/lib/app_component.dart @@ -1,10 +1,11 @@ // #docregion import 'package:angular2/angular2.dart'; -import 'package:user_input/click_me_component.dart'; -import 'package:user_input/click_me_component_2.dart'; -import 'package:user_input/loop_back_component.dart'; -import 'package:user_input/keyup_components.dart'; -import 'package:user_input/little_tour_component.dart'; + +import 'click_me_component.dart'; +import 'click_me_component_2.dart'; +import 'loop_back_component.dart'; +import 'keyup_components.dart'; +import 'little_tour_component.dart'; @Component( selector: 'my-app',