From cca9d5a1c58f1f0a1072ed1b632525377f632628 Mon Sep 17 00:00:00 2001 From: Kathy Walrath Date: Wed, 6 Jan 2016 10:45:40 -0800 Subject: [PATCH] docs(user-guide): update Dart text to match new TS text also update the Dart sample a bit closes #677 --- .../user-input/dart/lib/app_component.dart | 15 +- ...{app-component.html => app_component.html} | 0 .../dart/lib/click_me_component.dart | 7 +- .../dart/lib/click_me_component_2.dart | 2 - ..._components.dart => keyup_components.dart} | 2 - .../dart/lib/little_tour_component.dart | 4 - .../dart/lib/loop_back_component.dart | 2 - .../_examples/user-input/dart/pubspec.yaml | 2 +- public/docs/dart/latest/guide/user-input.jade | 419 ++++++++++-------- public/resources/css/_print.scss | 2 +- 10 files changed, 250 insertions(+), 205 deletions(-) rename public/docs/_examples/user-input/dart/lib/{app-component.html => app_component.html} (100%) 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 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 @@ -.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: +include ../../../../_includes/_util-fns - 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: - +: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. - code-example(language="html"). - <input #myname (keyup)> - <p>{{myname.value}}</p> +:marked + ## Binding to user input events - 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. + We can use Angular event bindings + to respond to [any DOM event](https://developer.mozilla.org/en-US/docs/Web/Events). - 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") - - -.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. - -

- -.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> + 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") -.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. +: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. - 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. + 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: - 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; - } - } ++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 - h2#section-final-code Final code +: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. - code-tabs - code-pane(language="dart" name="lib/todo_list.dart" format="linenums"). - library user_input.todo_list; + 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=".") - import 'dart:html'; - import 'package:angular2/angular2.dart'; +: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. - @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> +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`. - <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' - ]; ++makeExample('user-input/dart/lib/keyup_components.dart', 'key-up-component-1-class-no-type', 'web/keyup_components.dart (class v.1)')(format=".") - addTodo(String todo) { - todos.add(todo); - } +: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. - 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'; + 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. - 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 + 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 +: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 +: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 +: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 +: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;