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..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,16 +1,15 @@
// #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/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',
- 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/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 3cbc36c4a6..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,8 +1,4 @@
// #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/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
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
diff --git a/public/docs/dart/latest/guide/user-input.jade b/public/docs/dart/latest/guide/user-input.jade
index 9ce55e0f3c..89b9a583b3 100644
--- a/public/docs/dart/latest/guide/user-input.jade
+++ b/public/docs/dart/latest/guide/user-input.jade
@@ -1,199 +1,254 @@
+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.
+
+:marked
+ ## Binding to user input events
+
+ 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 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 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 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:
+
++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.
+
.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/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.
+
+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).
+ 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
+ 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 Dart affords.
+ We can rewrite the method, casting to HTML DOM objects like this.
+ +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.
+
+ 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**.
+
+ 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/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
+ 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 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
+ 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/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.
+ 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 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 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
+ 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/dart/lib/keyup_components.dart', 'key-up-component-4' ,'web/keyup_components.dart (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/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.
+
+ ### 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 statements simple
+ 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.
+
+ 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 Dart in HTML.
+ 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?
+
+ 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/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.dart,
+ keyup_components.dart,
+ loop_back_component.dart,
+ little_tour_component.dart`)
.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.
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;