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>
 | 
						|
        }
 |