2015-10-19 12:38:35 -04:00
|
|
|
include ../../../../_includes/_util-fns
|
2015-08-08 16:55:53 -04:00
|
|
|
|
2015-10-23 22:05:17 -04:00
|
|
|
<!-- http://plnkr.co/edit/x9JYbC -->
|
|
|
|
|
2015-11-10 13:31:46 -05:00
|
|
|
:marked
|
2015-10-23 22:05:17 -04:00
|
|
|
## Displaying Component Properties
|
|
|
|
|
|
|
|
We typically display data in Angular by binding controls in an HTML template
|
|
|
|
to properties of an Angular Component.
|
|
|
|
|
|
|
|
In this chapter, we'll create a component with a list of heroes. Each hero has a name.
|
|
|
|
We'll display the list of hero names and
|
|
|
|
conditionally show a selected hero in a detail area below the list.
|
|
|
|
Our UI looks like this:
|
|
|
|
|
|
|
|
figure.image-display
|
|
|
|
img(src="/resources/images/devguide/displaying-data/final.png" alt="Final UI")
|
|
|
|
|
2015-08-08 16:55:53 -04:00
|
|
|
.l-main-section
|
2015-11-10 13:31:46 -05:00
|
|
|
:marked
|
2015-10-23 22:05:17 -04:00
|
|
|
## Showing component properties with interpolation
|
|
|
|
The easiest way to display a component property
|
|
|
|
is to bind the property name through interpolation.
|
|
|
|
With interpolation, we put the property name in the view template enclosed in double curly braces: { { myHero } } .
|
2015-10-19 12:38:35 -04:00
|
|
|
|
2015-10-23 22:05:17 -04:00
|
|
|
Let's build a small illustrative example together.
|
|
|
|
|
|
|
|
Create a new project folder (`displaying-data`) and follow the steps in the [QuickStart](../quickstart.html).
|
|
|
|
|
|
|
|
Then modify the `app.ts` file by changing the template and the body of the component.
|
|
|
|
When we're done, it should look like this:
|
|
|
|
|
2015-10-31 00:09:57 -04:00
|
|
|
+makeExample('displaying-data/ts/src/app/app.1.ts')
|
2015-10-23 22:05:17 -04:00
|
|
|
|
2015-11-10 13:31:46 -05:00
|
|
|
:marked
|
2015-10-23 22:05:17 -04:00
|
|
|
We added two properties to the formerly empty component: `title` and `myHero`.
|
|
|
|
|
|
|
|
Our revised template displays the two component properties using the double curly brace
|
|
|
|
interpolation:
|
|
|
|
|
|
|
|
+makeExample('displaying-data/ts/src/app/app.1.ts', 'template')
|
|
|
|
.l-sub-section
|
2015-11-10 13:31:46 -05:00
|
|
|
:marked
|
2015-10-23 22:05:17 -04:00
|
|
|
The template is a multi-line string within ECMAScript 2015 back-tics (\`).
|
|
|
|
The back-tick (\`) is not the same character as a single quote (').
|
|
|
|
It has many nice features. The feature we're exploiting is
|
|
|
|
the ability to compose the string over several lines which
|
|
|
|
makes for much more readable HTML.
|
2015-10-19 12:38:35 -04:00
|
|
|
|
2015-11-10 13:31:46 -05:00
|
|
|
:marked
|
2015-10-23 22:05:17 -04:00
|
|
|
Angular automatically pulls the value of the `title` and `myHero` properties from the component and
|
|
|
|
inserts those values into the browser. Angular will update the display
|
|
|
|
when these properties change.
|
|
|
|
.l-sub-section
|
2015-11-10 13:31:46 -05:00
|
|
|
:marked
|
2015-10-23 22:05:17 -04:00
|
|
|
More precisely, the re-display occurs after some kind of asynchronous event related to
|
|
|
|
the view such as a keystroke, a timer completion, or an asynch `XHR` response.
|
|
|
|
We don't have those in this sample.
|
|
|
|
But then the properties aren't changing on their own either. For the moment we must operate on faith.
|
2015-11-10 13:31:46 -05:00
|
|
|
:marked
|
2015-10-23 22:05:17 -04:00
|
|
|
Notice that we haven't called **new** to create an instance of the `AppComponent` class.
|
|
|
|
Angular is creating an instance for us. How?
|
|
|
|
|
|
|
|
Notice the CSS `selector` in the `@Component` decorator that specifies an element named "my-app".
|
|
|
|
Remember back in QuickStart that we added the `<my-app>` element to the body of our `index.html`
|
|
|
|
+makeExample('displaying-data/ts/src/index.html', 'my-app')
|
|
|
|
|
2015-11-10 13:31:46 -05:00
|
|
|
:marked
|
2015-10-23 22:05:17 -04:00
|
|
|
When we bootstrap with the `AppComponent` class (see the bottom of `app.ts`), Angular looks for a `<my-app>`
|
|
|
|
in the `index.html`, finds it, instantiates an instance of `AppComponent`, and renders it
|
|
|
|
inside the `<my-app>` tag.
|
|
|
|
|
|
|
|
## Template inline or template file?
|
|
|
|
|
|
|
|
We can store our component's template in one of two places.
|
|
|
|
We can define it "inline" using the template property as we do here.
|
|
|
|
Or we can define the template in a separate HTML file and link to it in
|
|
|
|
the component metadata using the `@Component` decorator's `templateUrl` property.
|
|
|
|
|
|
|
|
We're using the *inline* style because the template is small and it makes for clearer demonstration.
|
|
|
|
The choice between them is a matter of taste, circumstances, and organization policy.
|
|
|
|
|
|
|
|
In either style, The template data bindings have the same access to the component's properties.
|
|
|
|
|
|
|
|
## Constructor or variable initialization?
|
|
|
|
|
|
|
|
We initialized our component properties using variable assignment.
|
|
|
|
This is a wonderfully concise and compact technique.
|
|
|
|
|
|
|
|
Some folks prefer to declare the properties and initialize them within a constructor like this:
|
|
|
|
+makeExample('displaying-data/ts/src/app/app-ctor.ts', 'app-ctor')
|
|
|
|
|
2015-11-10 13:31:46 -05:00
|
|
|
:marked
|
2015-10-23 22:05:17 -04:00
|
|
|
That's fine too. The choice between them is a matter of taste and organization policy.
|
|
|
|
We'll adopt the more terse "variable assignment" style in this chapter simply because
|
|
|
|
there will be less code to read.
|
|
|
|
|
|
|
|
.l-main-section
|
2015-11-10 13:31:46 -05:00
|
|
|
:marked
|
2015-10-23 22:05:17 -04:00
|
|
|
## Showing an array property with NgFor
|
|
|
|
|
|
|
|
We want to display a list of heroes. We begin by adding a mock heroes name array to the component,
|
|
|
|
just above `myHero` and redefine `myHero` to be the first name in the array.
|
|
|
|
+makeExample('displaying-data/ts/src/app/app.2.ts', 'mock-heroes')
|
|
|
|
|
2015-11-10 13:31:46 -05:00
|
|
|
:marked
|
2015-10-23 22:05:17 -04:00
|
|
|
Now we use the Angular `NgFor` "repeater" Directive in the template to display
|
|
|
|
each item in the `heroes` list.
|
|
|
|
|
|
|
|
+makeExample('displaying-data/ts/src/app/app.2.ts', 'template')
|
|
|
|
|
2015-11-10 13:31:46 -05:00
|
|
|
:marked
|
2015-10-23 22:05:17 -04:00
|
|
|
Our presentation is the familiar HTML unordered list with `<ul>` and `<li>` tags. Let's focus on the `<li>` tag.
|
|
|
|
+makeExample('displaying-data/ts/src/app/app.2.ts', 'li-repeater')
|
|
|
|
|
2015-11-10 13:31:46 -05:00
|
|
|
:marked
|
2015-10-23 22:05:17 -04:00
|
|
|
We added a somewhat mysterious `*ng-for` to the `<li>` element.
|
|
|
|
That's the Angular "repeater" directive.
|
|
|
|
It's presence on the `<li>` tag marks that `<li>` element (and its children) as the "repeater template".
|
|
|
|
|
|
|
|
.alert.is-important
|
2015-11-10 13:31:46 -05:00
|
|
|
:marked
|
2015-10-23 22:05:17 -04:00
|
|
|
Don't forget the leading asterisk (\*) in front of `*ng-for`. It is an essential part of the syntax.
|
|
|
|
Learn more about this and `NgFor` in the [Template Syntax](./template-syntax.html#ng-for) chapter.
|
|
|
|
|
2015-11-10 13:31:46 -05:00
|
|
|
:marked
|
2015-10-23 22:05:17 -04:00
|
|
|
Notice the `#hero` in the `NgFor` double-quoted instruction.
|
|
|
|
The `#hero` is a "[template local variable](./template-syntax.html#local-vars")" *declaration*.
|
|
|
|
The (#) prefix declares a local variable name named `hero`.
|
2015-10-19 12:38:35 -04:00
|
|
|
|
2015-10-23 22:05:17 -04:00
|
|
|
Angular will duplicate the `<li>` for each item in the list, setting the `hero` variable
|
|
|
|
to the item (the hero) in the current iteration. Angular uses that variable as the
|
|
|
|
context for the interpolation in the double curly braces.
|
|
|
|
|
|
|
|
.l-sub-section
|
2015-11-10 13:31:46 -05:00
|
|
|
:marked
|
2015-10-23 22:05:17 -04:00
|
|
|
We happened to give `NgFor` an array to display.
|
|
|
|
In fact, `NgFor` can repeat items for any [iterable](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols)
|
|
|
|
object.
|
2015-11-10 13:31:46 -05:00
|
|
|
:marked
|
2015-10-23 22:05:17 -04:00
|
|
|
## Register the NgFor Directive
|
|
|
|
|
|
|
|
Angular doesn't know that this template uses the `NgFor` directive.
|
|
|
|
Our application will not run right now. Angular will complain that it doesn't know what `NgFor` is.
|
|
|
|
|
|
|
|
We have to register the `NgFor` directive with the component metadata by making two changes to the app.ts file.
|
|
|
|
|
|
|
|
First, we import the `NgFor` symbol from the Angular library by extending the existing `import` statement.
|
|
|
|
Look for it in the following:
|
|
|
|
+makeExample('displaying-data/ts/src/app/app.2.ts', 'imports')
|
|
|
|
|
2015-11-10 13:31:46 -05:00
|
|
|
:marked
|
2015-10-23 22:05:17 -04:00
|
|
|
Second, we register `NgFor` as a directive accessible to the template by updating the
|
|
|
|
`@Component` decorator with a `directives` array property whose only item is `NgFor`:
|
|
|
|
|
|
|
|
+makeExample('displaying-data/ts/src/app/app.2.ts', 'directives')
|
|
|
|
|
2015-11-10 13:31:46 -05:00
|
|
|
:marked
|
2015-10-23 22:05:17 -04:00
|
|
|
Now the heroes will appear in the view as an unordered list.
|
|
|
|
|
|
|
|
figure.image-display
|
|
|
|
img(src="/resources/images/devguide/displaying-data/hero-names-list.png" alt="After ngfor")
|
|
|
|
|
|
|
|
.l-main-section
|
2015-11-10 13:31:46 -05:00
|
|
|
:marked
|
2015-10-23 22:05:17 -04:00
|
|
|
## Creating a class for the data
|
|
|
|
|
|
|
|
We are defining our data directly inside our component.
|
|
|
|
That's fine for a demo but certainly isn't a best practice. It's not even a good practice.
|
|
|
|
We won't do anything about that in this chapter.
|
|
|
|
|
|
|
|
At the moment, we're binding to an array of strings. We do that occasionally in real applications but
|
|
|
|
most of the time we're displaying objects, potentially instances of classes.
|
|
|
|
|
|
|
|
Let's turn our array of hero names into an array of `Hero` objects. For that we'll need a `Hero' class.
|
|
|
|
|
|
|
|
Create a new file called `hero.ts` and add the following short snippet to it.
|
|
|
|
+makeExample('displaying-data/ts/src/app/hero.ts')
|
|
|
|
|
2015-11-10 13:31:46 -05:00
|
|
|
:marked
|
2015-10-23 22:05:17 -04:00
|
|
|
We've defined a class with a constructor and two properties: `id` and `name`.
|
|
|
|
|
|
|
|
If we are new to TypeScript, it may not look like we have properties. But we do. We're taking
|
|
|
|
advantage of a TypeScript short-cut in our declaration of the constructor parameters.
|
|
|
|
|
|
|
|
Consider the first parameter:
|
2015-11-12 18:19:26 -05:00
|
|
|
+makeExample('displaying-data/ts/src/app/hero.ts', 'id-parameter')(format=".")
|
2015-10-23 22:05:17 -04:00
|
|
|
|
2015-11-10 13:31:46 -05:00
|
|
|
:marked
|
2015-10-23 22:05:17 -04:00
|
|
|
That brief syntax simultaneously
|
|
|
|
* declares a constructor parameter and its type
|
|
|
|
* declare a public property of the same name
|
|
|
|
* initializes that property with the corresponding argument when we "new" an instance of the class.
|
|
|
|
|
|
|
|
.l-main-section
|
2015-11-10 13:31:46 -05:00
|
|
|
:marked
|
2015-10-23 22:05:17 -04:00
|
|
|
## Use the Hero class
|
|
|
|
Let's redefine the heroes property in our component to return an array of these Heroes
|
|
|
|
and also set the `myHero` property with the first of these mock heroes.
|
|
|
|
+makeExample('displaying-data/ts/src/app/app.3.ts', 'heroes')
|
|
|
|
|
2015-11-10 13:31:46 -05:00
|
|
|
:marked
|
2015-10-23 22:05:17 -04:00
|
|
|
We'll have to update the template.
|
|
|
|
At the moment it displays the entire hero object which used to be a string value.
|
|
|
|
Let's fix that so we interpolate the `hero.name` property
|
|
|
|
+makeExample('displaying-data/ts/src/app/app.3.ts', 'template')
|
|
|
|
|
2015-11-10 13:31:46 -05:00
|
|
|
:marked
|
2015-10-23 22:05:17 -04:00
|
|
|
Our display looks the same but we know how much better it is under the hood.
|
|
|
|
|
|
|
|
.l-main-section
|
2015-11-10 13:31:46 -05:00
|
|
|
:marked
|
2015-10-23 22:05:17 -04:00
|
|
|
## Conditional display with NgIf
|
|
|
|
|
|
|
|
Sometimes the app should display a view or a portion of a view only under prescribed circumstances.
|
|
|
|
|
|
|
|
In our example, we'd like to display a message if we have a large number of heroes ... say more than 3.
|
|
|
|
|
|
|
|
The Angular `NgIf` directive will insert or remove an element based on a truthy/falsey condition.
|
|
|
|
We can see it in action by adding the following paragraph at the bottom of the template:
|
2015-11-12 18:19:26 -05:00
|
|
|
+makeExample('displaying-data/ts/src/app/app.final.ts', 'message')(format=".")
|
2015-10-23 22:05:17 -04:00
|
|
|
.alert.is-important
|
2015-11-10 13:31:46 -05:00
|
|
|
:marked
|
2015-10-23 22:05:17 -04:00
|
|
|
Don't forget the leading asterisk (\*) in front of `*ng-if`. It is an essential part of the syntax.
|
|
|
|
Learn more about this and `NgIf` in the [Template Syntax](./template-syntax.html#ng-if) chapter.
|
|
|
|
|
2015-11-10 13:31:46 -05:00
|
|
|
:marked
|
2015-10-23 22:05:17 -04:00
|
|
|
The [template expression](./template-syntax.html#template-expressions) inside the double quotes
|
|
|
|
looks much like JavaScript and it is much like JavaScript.
|
|
|
|
When the component's list of heroes has more than 3 items, Angular adds the paragraph to the DOM and the message appears.
|
2015-11-12 15:28:54 -05:00
|
|
|
If there were 3 or fewer items, Angular omits the paragraph and there is no message.
|
2015-10-23 22:05:17 -04:00
|
|
|
|
|
|
|
.alert.is-helpful
|
2015-11-10 13:31:46 -05:00
|
|
|
:marked
|
2015-10-23 22:05:17 -04:00
|
|
|
Angular isn't showing and hiding the message. It is adding and removing the paragraph element from the DOM.
|
|
|
|
That hardly matters here. It would matter a great deal from a performance perspective if
|
|
|
|
we were conditionally including or excluding a big chunk of HTML with many data bindings.
|
|
|
|
|
2015-11-10 13:31:46 -05:00
|
|
|
:marked
|
2015-10-23 22:05:17 -04:00
|
|
|
As with the `NgFor`, we must add the `NgIf` directive to the component's metadata.
|
|
|
|
|
|
|
|
We should extend our `import` statement as before ...
|
2015-11-12 18:19:26 -05:00
|
|
|
+makeExample('displaying-data/ts/src/app/app.3.ts', 'import-ng-if')(format=".")
|
2015-10-23 22:05:17 -04:00
|
|
|
|
2015-11-10 13:31:46 -05:00
|
|
|
:marked
|
2015-10-23 22:05:17 -04:00
|
|
|
... and add it to the directives array:
|
2015-11-12 18:19:26 -05:00
|
|
|
+makeExample('displaying-data/ts/src/app/app.3.ts', 'directives')(format=".")
|
2015-10-23 22:05:17 -04:00
|
|
|
|
2015-11-10 13:31:46 -05:00
|
|
|
:marked
|
2015-10-23 22:05:17 -04:00
|
|
|
Try it out. We have four items in the array so the message should appear.
|
|
|
|
Delete one of the elements from the array, refresh the browser, and the message should no longer appear.
|
|
|
|
|
|
|
|
.l-main-section
|
2015-11-10 13:31:46 -05:00
|
|
|
:marked
|
2015-10-23 22:05:17 -04:00
|
|
|
## Use the CORE_DIRECTIVES Constant
|
|
|
|
|
|
|
|
There are other core Angular directives, such as `NgClass` and `NgSwitch`,
|
|
|
|
that we often use in our apps.
|
|
|
|
Extending the `import` statement and adding to the `directives` array for each one gets old.
|
|
|
|
|
|
|
|
Fortunately, Angular provides a constant array called `CORE_DIRECTIVES`
|
|
|
|
that includes many of the directives that we use all the time.
|
|
|
|
|
|
|
|
Let's simplify our lives, discard the `NgFor` and `NgIf`, use the constant for all of them.
|
|
|
|
|
|
|
|
We'll revise our `import` statement one last time.
|
2015-11-12 18:19:26 -05:00
|
|
|
+makeExample('displaying-data/ts/src/app/app.final.ts', 'imports')(format=".")
|
2015-10-23 22:05:17 -04:00
|
|
|
|
2015-11-10 13:31:46 -05:00
|
|
|
:marked
|
2015-10-23 22:05:17 -04:00
|
|
|
and update the `directives` metadata
|
2015-11-12 18:19:26 -05:00
|
|
|
+makeExample('displaying-data/ts/src/app/app.final.ts', 'directives')(format=".")
|
2015-10-23 22:05:17 -04:00
|
|
|
|
2015-11-10 13:31:46 -05:00
|
|
|
:marked
|
2015-10-23 22:05:17 -04:00
|
|
|
Pro tip: we register this constant in almost every template we write.
|
|
|
|
|
|
|
|
.l-main-section
|
2015-11-10 13:31:46 -05:00
|
|
|
:marked
|
2015-10-23 22:05:17 -04:00
|
|
|
## Summary
|
|
|
|
Now we know how to
|
|
|
|
- use **interpolation** with the double curly braces to display a component property,
|
|
|
|
- use **`NgFor`** to display a list of items,
|
|
|
|
- use a TypeScript class to shape the model data for our component and display properties of that model,
|
|
|
|
- use **`NgIf`** to conditionally display a chunk of HTML based on a boolean expression.
|
|
|
|
- register common component directives with **`CORE_DIRECTIVES` constant**
|
|
|
|
|
|
|
|
Our final code:
|
|
|
|
|
|
|
|
+makeTabs('displaying-data/ts/src/app/app.final.ts, '+
|
|
|
|
'displaying-data/ts/src/app/hero.ts',
|
|
|
|
'final,',
|
|
|
|
'app.ts, hero.ts')
|
|
|
|
|
|
|
|
.l-main-section
|
2015-11-10 13:31:46 -05:00
|
|
|
:marked
|
2015-10-23 22:05:17 -04:00
|
|
|
## Next Steps
|
|
|
|
In addition to displaying data, most applications need to respond to user input.
|
|
|
|
Learn about that in the [User Input](./user-input.html) chapter.
|