348 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			348 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| .l-main-section
 | |
| 
 | |
|   h2#section-displaying-controller-properties Displaying controller properties
 | |
| 
 | |
| 
 | |
|   p.
 | |
|     Let's walk through how we'd display a property, a list of properties, and then conditionally show content
 | |
|     based on state. We'll end up with a UI that looks like this:
 | |
| 
 | |
|   figure.image-display
 | |
|     img(src='/resources/images/examples/displaying-data-example1.png' alt="Example of Todo App")
 | |
| 
 | |
| .callout.is-helpful
 | |
|   header Typescript vs ES5
 | |
|   p.
 | |
|     Although we work through the examples in TypeScript, you can also use
 | |
|     regular ES5. Click the ES5 link in any code box to see the ES5 JavaScript
 | |
|     version. Note that in ES5, you'd want to name your files <code>.js</code> rather than
 | |
|     <code>.ts</code>.
 | |
| 
 | |
| .l-main-section
 | |
|     h2#section-create-an-entry-point Create an entry point
 | |
| 
 | |
|     p Open your favorite editor and create a <code>show-properties.html</code> file with the content:
 | |
| 
 | |
|     code-example(language="html" escape="html").
 | |
|       <display></display>
 | |
| 
 | |
|     p
 | |
|      | The <code><display></code> component here acts as the site where you'll insert your application.
 | |
|      | We'll assume a structure like this for the rest of the examples here and just focus on the parts that
 | |
|      | are different.
 | |
| 
 | |
| .l-main-section
 | |
|     h2#section-showing-properties-with-interpolation Showing properties with interpolation
 | |
|     p.text-body
 | |
|      | The simple method for binding text into templates is through interpolation where you put the name of a property
 | |
|      | inside <strong>{{ }}</strong>.
 | |
| 
 | |
|     p To see this working, create another file, <code>show-properties.ts</code>, and add the following:
 | |
| 
 | |
|     code-tabs
 | |
|       code-pane(language="javascript" name="TypeScript" format="linenums").
 | |
|         // TypeScript
 | |
|         import {Component, View, bootstrap} from 'angular2/angular2';
 | |
| 
 | |
|         @Component({
 | |
|           selector: 'display'
 | |
|         })
 | |
|         @View({
 | |
|           template: `
 | |
|            <p>My name: {{ myName }}</p>
 | |
|           `
 | |
|         })
 | |
|         class DisplayComponent {
 | |
|           myName: string;
 | |
| 
 | |
|           constructor() {
 | |
|             this.myName = "Alice";
 | |
|           }
 | |
|         }
 | |
|       code-pane(language="javascript" name="ES5" format="linenums").
 | |
|         // ES5
 | |
|         function DisplayComponent() {
 | |
|           this.myName = "Alice";
 | |
|         }
 | |
|         DisplayComponent.annotations = [
 | |
|           new angular.ComponentAnnotation({
 | |
|             selector: "display"
 | |
|           }),
 | |
|           new angular.ViewAnnotation({
 | |
|             template:
 | |
|                '<p>My name: {{ myName }}</p>'
 | |
|           })
 | |
|         ];
 | |
| 
 | |
| 
 | |
|     p.
 | |
|      You've just defined a component that encompasses a view and controller for the app. The view
 | |
|      defines a template:
 | |
| 
 | |
|     code-example(language="html" escape="html").
 | |
|       <p>My name: {{ myName }}</p>
 | |
| 
 | |
|     p.
 | |
|      Angular will automatically pull the value of <code>myName</code> and insert it into the browser and
 | |
|      update it whenever it changes without work on your part.
 | |
| 
 | |
|     p.
 | |
|      One thing to notice here is that though you've written your <code>DisplayComponent</code> class, you haven't
 | |
|      called new to create one anywhere.  By associating your class with elements named 'display' in
 | |
|      the DOM, Angular knows to automatically call new on <code>DisplayComponent</code> and bind its properties to
 | |
|      that part of the template.
 | |
| 
 | |
|     p.
 | |
|      When you're building templates, data bindings like these have access to the same scope of
 | |
|      properties as your controller class does.  Here, your class is the <code>DisplayComponent</code> that has
 | |
|      just one property, myName.
 | |
| 
 | |
|     .callout.is-helpful
 | |
|       header Note
 | |
|       p.
 | |
|         While you've used <code>template:</code> to specify an inline view, for larger templates you'd
 | |
|         want to move them to a separate file and load them with <code>templateUrl:</code> instead.
 | |
| 
 | |
| .l-main-section
 | |
|     h2#Create-an-array Create an array property and use NgFor on the view
 | |
|     p Moving up from a single property, create an array to display as a list.
 | |
| 
 | |
|     code-tabs
 | |
|       code-pane(language="javascript" name="TypeScript" format="linenums").
 | |
|         //Typescript
 | |
|         class DisplayComponent {
 | |
|           myName: string;
 | |
|           <span class='otl'>names: Array<string>;</span>
 | |
| 
 | |
|           constructor() {
 | |
|             this.myName = "Alice";
 | |
|             <span class='otl'>this.names = ["Aarav", "Martín", "Shannon", "Ariana", "Kai"];</span>
 | |
|           }
 | |
|         }
 | |
| 
 | |
|       code-pane(language="javascript" name="Javascript (ES5)" format="linenums").
 | |
|         //ES5
 | |
|         function DisplayComponent() {
 | |
|           this.myName = "Alice";
 | |
|           <span class='otl'>this.names = ["Aarav", "Martín", "Shannon", "Ariana", "Kai"];</span>
 | |
|         }
 | |
|     p.
 | |
|         You can then use this array in your template with the <code>NgFor</code> directive to create copies of DOM elements
 | |
|         with one for each item in the array.
 | |
| 
 | |
|     code-tabs
 | |
|       code-pane(language="javascript" name="TypeScript" format="linenums").
 | |
|         //Typescript
 | |
|         template: `
 | |
|           <p>My name: {{ myName }}</p>
 | |
|           <p>Friends:</p>
 | |
|           <ul>
 | |
|              <li *ng-for="#name of names">
 | |
|                 {{ name }}
 | |
|              </li>
 | |
|           </ul>
 | |
|         `,
 | |
|       code-pane(language="javascript" name="ES5" format="linenums").
 | |
|         //ES5
 | |
|         template:
 | |
|           '<p>My name: {{ myName }}</p>' +
 | |
|           '<p>Friends:</p>' +
 | |
|           '<ul>' +
 | |
|           '<li *ng-for="#name of names">' +
 | |
|           '{{ name }}' +
 | |
|           '</li>' +
 | |
|           '</ul>',
 | |
| 
 | |
|     p.
 | |
|         To make this work, you'll also need to add the <code>NgFor</code> directive used by the template so
 | |
|         that Angular knows to include it:
 | |
| 
 | |
|     code-tabs
 | |
|       code-pane(language="javascript" name="TypeScript" format="linenums").
 | |
|         //Typescript
 | |
|         import {Component, View, bootstrap, NgFor} from 'angular2/angular2';
 | |
|         @View({
 | |
|           ...
 | |
|           directives: [NgFor]
 | |
|         })
 | |
| 
 | |
| 
 | |
|       code-pane(language="javascript" name="ES5" format="linenums").
 | |
|         //ES5
 | |
|         DisplayComponent.annotations = [
 | |
|           ...
 | |
|           new angular.ViewAnnotation({
 | |
|             ...
 | |
|             directives: [angular.NgFor]
 | |
|           })
 | |
|         ];
 | |
| 
 | |
|     p Reload and you've got your list of friends!
 | |
|     p.
 | |
|         Angular will mirror changes you make to this list over in the DOM. Add a new item and it appears in your
 | |
|         list. Delete one and Angular deletes the <li>. Reorder items and Angular makes the corresponding reorder of
 | |
|         the DOM list.
 | |
|     p Let's look at the few lines that do the work again:
 | |
|     code-example(language="html" format="linenums").
 | |
|       //HTML
 | |
|       <li *ng-for="#name of names">
 | |
|          {{ name }}
 | |
|       </li>
 | |
|     p The way to read this is:
 | |
|     ul
 | |
|         li.
 | |
|             <code>*ng-for</code> : create a DOM element for each item in an
 | |
|             <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols">iterable</a>
 | |
|              like an array
 | |
|         li <code>#name</code> : refer to individual values of the iterable as 'name'
 | |
|         li <code>of names</code> : the iterable to use is called 'names' in the current controller
 | |
|     p Using this syntax, you can build UI lists from any iterable object.
 | |
| .l-main-section
 | |
|     h2#Create-a-class Create a class for the array property and inject into component
 | |
| 
 | |
|     p.
 | |
|         Before we get too much further, we should mention that putting our model (array) directly in our controller isn't
 | |
|         proper form.  We should separate the concerns by having another class serve the role of model and inject it into
 | |
|         the controller.
 | |
| 
 | |
|     p Make a <code>FriendsService</code> class to provide the model with the list of friends.
 | |
| 
 | |
|     code-tabs
 | |
|       code-pane(language="javascript" name="TypeScript" format="linenums").
 | |
|         class FriendsService {
 | |
|           names: Array<string>;
 | |
|           constructor() {
 | |
|             this.names = ["Alice", "Aarav", "Martín", "Shannon", "Ariana", "Kai"];
 | |
|           }
 | |
|         }
 | |
| 
 | |
|       code-pane(language="javascript" name="ES5" format="linenums").
 | |
|         function FriendsService() {
 | |
|           this.names = ["Aarav", "Martín", "Shannon", "Ariana", "Kai"];
 | |
|         }
 | |
| 
 | |
|     p.
 | |
|         Now replace the current list of friends in DisplayComponent by including the FriendsService in the injectables list,
 | |
|         then including the service in the constructor, and finally setting the list of
 | |
|         names in DisplayComponent to the names provided by the service you passed in.
 | |
| 
 | |
|     .callout.is-helpful
 | |
|         header ES5 Note
 | |
|         p.
 | |
|             The dependency injection syntax here is using the low-level API and is...well...not very nice.  We're
 | |
|             working on sugaring the syntax to match the way it works in Angular 1.  Expect this to change soon.
 | |
| 
 | |
|     code-tabs
 | |
|       code-pane(language="javascript" name="TypeScript" format="linenums").
 | |
|         @Component({
 | |
|           ...
 | |
|           <span class='otl'>appInjector: [FriendsService]</span>
 | |
|         })
 | |
|         class DisplayComponent {
 | |
|           myName: string;
 | |
|           names: Array<string>;
 | |
|           constructor(<span class='otl'>friendsService: FriendsService</span>) {
 | |
|             this.myName = 'Alice';
 | |
|             <span class='otl'>this.names = friendsService.names;</span>
 | |
|           }
 | |
|         }
 | |
| 
 | |
| 
 | |
|       code-pane(language="javascript" name="ES5" format="linenums").
 | |
|         //ES5
 | |
|         function DisplayComponent(<span class='otl'>friends</span>) {
 | |
|           this.myName = "Alice";
 | |
|           this.names = <span class='otl'>friends.names</span>;
 | |
|         }
 | |
|         DisplayComponent.annotations = [
 | |
|           new angular.ComponentAnnotation({
 | |
|             selector: "display",
 | |
|             <span class='otl'>appInjector: [FriendsService]</span>
 | |
|           }),
 | |
|           new angular.ViewAnnotation({
 | |
|             template: '{{ myName }} <ul> <li *for="#name of names">{{ name }}</li> </ul>',
 | |
|             directives: [angular.NgFor]
 | |
|           })
 | |
|         ];
 | |
|         <span class='otl'>DisplayComponent.parameters = [[FriendsService]];</span>
 | |
|         document.addEventListener("DOMContentLoaded", function() {
 | |
|           angular.bootstrap(DisplayComponent);
 | |
|         });
 | |
| .l-main-section
 | |
|     h2#Conditionally-displaying-data-with-NgIf Conditionally displaying data with NgIf
 | |
|     p.
 | |
|         Lastly, before we move on, let's handle showing parts of our UI conditionally with <code>NgIf</code>.  The
 | |
|         <code>NgIf</code> directive adds or removes elements from the DOM based on the expression you provide.
 | |
|     p See it in action by adding a paragraph at the end of your template
 | |
|     pre.prettyprint.lang-html
 | |
|         code.
 | |
|             <p *ng-if="names.length > 3">You have many friends!</p>
 | |
|     p You'll also need to add the <code>NgIf</code> directive so Angular knows to include it.
 | |
| 
 | |
|     code-tabs
 | |
|       code-pane(language="javascript" name="TypeScript" format="linenums").
 | |
|         //Typescript
 | |
|         import {Component, View, bootstrap, NgFor, NgIf} from 'angular2/angular2';
 | |
|         ...
 | |
|           directives: [NgFor, NgIf]
 | |
|       code-pane(language="javascript" name="ES5" format="linenums").
 | |
|         //ES5
 | |
|         directives: [angular.NgFor, angular.NgIf]
 | |
|     p.
 | |
|         As there are currently 6 items in the list, you'll see the message congratulating you on your many friends.
 | |
|         Remove three items from the list, reload your browser, and see that the message no longer displays.
 | |
| 
 | |
|     code-tabs
 | |
|       code-pane(language="javascript" name="TypeScript" format="linenums").
 | |
|         //TypeScript
 | |
|         import {Component, View, bootstrap, NgFor, NgIf} from 'angular2/angular2';
 | |
|         ...
 | |
|         @View({
 | |
|           <span class='otl'>template</span>: `
 | |
|             <p>My name: {{ myName }}</p>
 | |
|             <p>Friends:</p>
 | |
|             <ul>
 | |
|               <li *ng-for="#name of names">
 | |
|                 {{ name }}
 | |
|               </li>
 | |
|             </ul>
 | |
|             <p *ng-if="names.length > 3">You have many friends!</p>
 | |
|           `,
 | |
|           directives: [NgFor, NgIf]
 | |
|         })
 | |
|         class DisplayComponent {
 | |
|           ...
 | |
|         }
 | |
| 
 | |
|         class FriendsService {
 | |
|           names: Array<string>;
 | |
|           constructor() {
 | |
|             <span class='otl'>this.names = ["Aarav", "Martín", "Shannon"];</span>
 | |
|           }
 | |
|         }
 | |
|       code-pane(language="javascript" name="ES5" format="linenums").
 | |
|         //ES5
 | |
|         function DisplayComponent(friends) {
 | |
|           this.myName = "Alice";
 | |
|           this.names = friends.names;
 | |
|         }
 | |
|         DisplayComponent.annotations = [
 | |
|           ...
 | |
|           new angular.ViewAnnotation({
 | |
|             <span class='otl'>template</span>: '
 | |
|               '<p>My name: {{ myName }}</p>' +
 | |
|               '<p>Friends:</p>' +
 | |
|               '<ul>' +
 | |
|               '<li *ng-for="#name of names">' +
 | |
|               '{{ name }}' +
 | |
|               '</li>' +
 | |
|               '</ul>' +
 | |
|               '<p *ng-if="names.length > 3">You have many friends!</p>'',
 | |
|             directives: [angular.NgFor, angular.NgIf]
 | |
|           })
 | |
|         ];
 | |
| 
 | |
|         function FriendsService () {
 | |
|           <span class='otl'>this.names = ["Aarav", "Martín", "Shannon"];</span>
 | |
|         }
 |