Angular_文档_02_数据绑定_模板语法

Angular_文档_02_数据绑定_模板语法_第1张图片

Angular官方文档学习笔记第2篇_数据绑定_模板语法


Displaying Data

You can display data by 

binding controls in an HTML template 

to properties of an Angular component.


In this page, you'll create a component with a list of girls. 

You'll display the list of girl names 

and conditionally show a message below the list.


The final UI looks like this:


Angular_文档_02_数据绑定_模板语法_第2张图片

The live example / download example demonstrates all of the syntax and code snippets described in this page.

Showing component properties with interpolation

The easiest way to display a component property 

is to bind the property name through interpolation. 


With interpolation, you put the property name in the view template, 

enclosed in double curly braces: {{myGirl}}.


Follow the quickstart instructions for creating a new project named displaying-data.

ng create displaying-data

如图所示:(可能要花几分钟)

Angular_文档_02_数据绑定_模板语法_第3张图片

Angular_文档_02_数据绑定_模板语法_第4张图片


然后cd displaying-data

执行ng serve启动项目

Angular_文档_02_数据绑定_模板语法_第5张图片

打开浏览器http://localhost:4200可以访问项目的首页

Angular_文档_02_数据绑定_模板语法_第6张图片

Delete the app.component.html file. It is not needed for this example.


Then modify the app.component.ts file 

by changing the template and the body of the component.

意思就是: 去掉templateUrl: './app.component.html'

换成

template: `

未闻花名

`


When you're done, it should look like this:

src/app/app.component.ts
  1. import { Component } from '@angular/core';
  2.  
  3. @Component({
  4. selector: 'app-root',
  5. template: `
  6. {{title}}

  7. My favorite girl is: {{myGirl}}

  8. `
  9. })
  10. export class AppComponent {
  11. title = '未闻花名';
  12. myGirl = '面码';
  13. }


You added two properties to the formerly ( 之前 )  empty component: title and myHero.

The template displays the two component properties using double curly brace interpolation:

src/app/app.component.ts (template)
template: `
  

{{title}}

My favorite girl is: {{myGirl}}

`

The template is a multi-line string within ECMAScript 2015 backticks (`). 

The backtick (`)—which is not the same character as a single quote (')—

allows you to compose a string over several lines, which makes the HTML more readable.

Angular_文档_02_数据绑定_模板语法_第7张图片

Angular automatically pulls the value of the title and myHero properties from the component 

and inserts those values into the browser. 

Angular 自动 updates the display when these properties change.

More precisely, the redisplay occurs after some kind of asynchronous event related to the view, 

such as a keystroke

a timer completion, 

or a response to an HTTP request.


Notice that you don't call new to create an instance of the AppComponent class. 

Angular is creating an instance for you. How?

The CSS selector in the @Component decorator specifies an element named 

That element is a placeholder in the body of your index.html file:

src/index.html (body)

  

When you bootstrap with the AppComponent class (in main.ts), 

Angular looks for a  in the index.html

finds it, instantiates an instance of AppComponent

and renders it inside the  tag.


Now run the app. It should display the title and hero name:

Angular_文档_02_数据绑定_模板语法_第8张图片


The next few sections review some of the coding choices in the app.

这句话什么意思???


Template inline or template file?

You can store your component's template in one of two places. 

You can define it inline using the template property, 

or you can define the template in a separate HTML file 

and link to it in the component metadata using the @Component  decorator's templateUrl property.


The choice between inline and separate HTML is a matter of taste, circumstances, and organization policy. 

Here the app uses inline HTML because the template is small and the demo is simpler without the additional HTML file.


In either style, the template data bindings have the same access to the component's properties.

By default, the Angular CLI generates components with a template file. You can override that with:

什么意思???

-it 表示的是使用内联模板

inline template的缩写

ng generate component hero -it

Angular_文档_02_数据绑定_模板语法_第9张图片

Constructor or variable initialization?

Although this example uses variable assignment to initialize the components, 

you could instead declare and initialize the properties using a constructor:

export class AppCtorComponent {
  title: string;
  myGirl: string;

  constructor() {
    this.title = '未闻花名';
    this.myGirl = '面码';
  }
}

This app uses more terse ( 简短的 ) "variable assignment" style simply for brevity.


Showing an array property with *ngFor

To display a list of heroes, 

begin by adding an array of hero names to the component 

and redefine myHero to be the first name in the array.

src/app/app.component.ts (class)
export class AppComponent {
  title = '世界萌王';
  heroes = ['面码', '逢坂大河', '艾拉', '古手梨花'];
  myHero = this.heroes[0];
}

Now use the Angular ngFor  directive in the template 

to display each item in the heroes list.

src/app/app.component.ts (template)
template: `
  

{{title}}

My favorite hero is: {{myHero}}

Heroes:

  • ngFor="let hero of heroes"> {{ hero }}
`

This UI uses the HTML unordered list with 

     and 
  •  tags. 

    The *ngFor in the 

  •  element is the Angular "repeater" directive. 

    It marks that 

  •  element (and its children) as the "repeater template":

    src/app/app.component.ts (li)
     *ngFor="let hero of heroes">
      {{ hero }}
    
  • Don't forget the leading asterisk (*) in *ngFor

    It is an essential part of the syntax. 

    For more information, see the Template Syntax page.

    Notice the hero in the ngFor  double-quoted instruction; 

    it is an example of a template input variable. 

    Read more about template input variables in the microsyntax ( 微语法 ) section of the Template Syntax page.


    Angular duplicates the 

  •  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.

    In this case, ngFor  is displaying an array, 

    but ngFor  can repeat items for any iterable object.

    Now the girls appear in an unordered list.

    Angular_文档_02_数据绑定_模板语法_第10张图片


    Creating a class for the data

    The app's code defines the data directly inside the component, 

    which isn't best practice. 

    In a simple demo, however, it's fine.

    At the moment, the binding is to an array of strings. 


    In real applications, most bindings are to more specialized objects.

    To convert this binding to use specialized objects, 

    turn the array of hero names into an array of Hero objects. 

    For that you'll need a Girl class:

    ng generate class girl

    Angular_文档_02_数据绑定_模板语法_第11张图片

    With the following code:

    src/app/hero.ts
    export class Hero {
      constructor(
        public id: number,
        public name: string) { }
    }

    You've defined a class with a constructor and two properties: id and name.

    It might not look like the class has properties, but it does. 

    The declaration of the constructor parameters takes advantage of a TypeScript shortcut.

    Consider the first parameter:

    src/app/hero.ts (id)
    public id: number,

    That brief syntax does a lot:

    • Declares a constructor parameter and its type.
    • Declares a public property of the same name.
    • Initializes that property with the corresponding argument when creating an instance of the class.


    Using the Hero class

    After importing the Hero class, the AppComponent.heroes property can return a typed array of Hero objects:

    src/app/app.component.ts (heroes)
    heroes = [
      new Hero(1, 'Windstorm'),
      new Hero(13, 'Bombasto'),
      new Hero(15, 'Magneta'),
      new Hero(20, 'Tornado')
    ];
    myHero = this.heroes[0];


    Next, update the template. 

    At the moment it displays the hero's id and name

    Fix that to display only the hero's name property.

    src/app/app.component.ts (template)
    template: `
      

    {{title}}

    My favorite hero is: {{myHero.name}}

    Heroes:

    • ngFor="let hero of heroes"> {{ hero.name }}
    `

    The display looks the same, but the code is clearer.


    Conditional display with NgIf

    Sometimes an app needs to display a view or a portion of a view only under specific circumstances.

    Let's change the example to display a message if there are more than three heroes.


    The Angular ngIf   directive inserts or removes an element based on a truthy/falsy condition. 

    To see it in action, add the following paragraph at the bottom of the template:

    src/app/app.component.ts (message)
     *ngIf="heroes.length > 3">There are many heroes!

    Don't forget the leading asterisk (*) in *ngIf

    It is an essential part of the syntax. 

    Read more about ngIf  and *in the ngIf section of the Template Syntax page.

    The template expression inside the double quotes, 

    *ngIf="heroes.length > 3", looks and behaves much like TypeScript. 

    When the component's list of heroes has more than three items, 

    Angular adds the paragraph to the DOM and the message appears. 


    If there are three or fewer items, 

    Angular omits the paragraph, so no message appears. 

    For more information, see the template expressions section of the Template Syntax page.

    Angular isn't showing and hiding the message. 

    划重点! Vue中指令有v-show和v-if

    It is adding and removing the paragraph element from the DOM

    That improves performance, especially in larger projects 

    when conditionally including or excluding big chunks of HTML with many data bindings.

    Angular_文档_02_数据绑定_模板语法_第12张图片

    Try it out. 

    Because the array has four items, the message should appear. 

    Go back into app.component.ts and delete or comment out one of the elements from the hero array. 

    The browser should refresh automatically and the message should disappear.


    Summary

    Now you know how to use:

    • Interpolation with double curly braces to display a component property.
    • ngFor to display an array of items.
    • A TypeScript class to shape the model data for your component and display properties of that model.
    • ngIf to conditionally display a chunk of HTML based on a boolean expression.

    Here's the final code:

    Model 类 girl.ts

    export class Girl {
        constructor(public id: number, public name: string){}
    }
    

    App Module ts文件

    import { BrowserModule } from '@angular/platform-browser';
    import { NgModule } from '@angular/core';
    
    import { AppComponent } from './app.component';
    
    
    @NgModule({
      declarations: [
        AppComponent
      ],
      imports: [
        BrowserModule
      ],
      providers: [],
      bootstrap: [AppComponent]
    })
    export class AppModule { }
    

    App Component ts文件

    import { Component } from '@angular/core';
    import { Girl } from './girl'
    @Component({
      selector: 'app-root',
      template: `
      	

    {{title}}

    My favorite girl is: {{firstGirl.name}}

    • {{ girl.name }}

    There are many girls!

    `, styleUrls: ['./app.component.css'] }) export class AppComponent { // title: string // girlName: string title = '世界萌王' girls = [ new Girl(1,'面码'), new Girl(2,'逢坂大河'), new Girl(3,'艾拉'), new Girl(4,'古手梨花') ] // 必须使用this firstGirl = this.girls[0] constructor(){ // this.title = '未闻花名'; // this.girlName = '面码'; } }

    效果如下:

    Angular_文档_02_数据绑定_模板语法_第13张图片





    Angular_文档_02_数据绑定_模板语法_第14张图片

    Template Syntax

    The Angular application manages what the user sees and can do, 

    achieving this through the interaction of a component class instance (the component) and its user-facing template.


    You may be familiar with the component/template duality 

    from your experience with model-view-controller (MVC) 

    or model-view-viewmodel (MVVM). 


    In Angular, the component plays the part of the controller/viewmodel ( ViewModel 驱动视图的数据 ), 

    and the template represents the view.


    This page is a comprehensive technical reference to the Angular template language. 

    It explains basic principles of the template language 

    and describes most of the syntax that you'll encounter elsewhere in the documentation.


    Many code snippets illustrate the points and concepts, 

    all of them available in the Template Syntax Live Code / download example.


    HTML in templates

    HTML is the language of the Angular template. 

    Almost all HTML syntax is valid template syntax. 


    The Syntax';

    Fortunately, Angular data binding is on alert for dangerous HTML. 

    It sanitizes the values before displaying them. 

    It will not allow HTML with script tags to leak into the browser, 

    neither with interpolation nor property binding.


    src/app/app.component.html

    
    

    "{{evilTitle}}" is the interpolated evil title.

    " [innerHTML]="evilTitle">" is the property bound evil title.


    Interpolation handles the script tags differently than property binding but 

    both approaches render the content harmlessly.


    效果如下:

    evil title made safe

    Attribute, class, and style bindings

    The template syntax provides specialized one-way bindings for 

    scenarios ( 场景 ) less well suited to property binding.


    Attribute binding

    You can set the value of an attribute directly with an attribute binding.

    This is the only exception to the rule that a binding sets a target property. 

    This is the only binding that creates and sets an attribute.

    This guide stresses repeatedly that setting an element property with a property binding 

    is always preferred to ( 优于 ) setting the attribute with a string. 

    Why does Angular offer attribute binding?


    You must use attribute binding when there is no element property to bind.

    Consider the ARIASVG, and table span attributes. 

    ARIA: Accessible Rich Internet Applications

    They are pure attributes. 

    They do not correspond to element properties, 

    and they do not set element properties. 

    There are no property targets to bind to.


    This fact becomes painfully obvious when you write something like this.

     colspan="{{1 + 1}}">Three-Four


    And you get this error:

    Template parse errors:
    Can't bind to 'colspan' since it isn't a known native property

    Angular_文档_02_数据绑定_模板语法_第28张图片


    As the message says, the  element does not have a colspan property. 

    It has the "colspan" attribute

    but interpolation and property binding can set only properties, not attributes.


    You need attribute bindings to create and bind to such attributes.

    注意: property binding 与 attribute binding的区别

    Attribute binding syntax resembles property binding

    Instead of an element property between brackets, 

    1. start with the prefix attr

    2. followed by a dot (.

    3. and the name of the attribute.

    You then set the attribute value, using an expression that resolves to a string.

    Bind [attr.colspan] to a calculated value:

    src/app/app.component.html
    content_copy border=1>
      
       [attr.colspan]="1 + 1">One-Two
    
      
    
      FiveSix
    

    Here's how the table renders:

    Angular_文档_02_数据绑定_模板语法_第29张图片

    Angular_文档_02_数据绑定_模板语法_第30张图片

    Angular_文档_02_数据绑定_模板语法_第31张图片


    One of the primary use cases for attribute binding 

    is to set ARIA attributes, as in this example:


    Accessible Rich Internet Applications (ARIA) 

    规定了能够让 Web 内容和 Web 应用

    (特别是那些由 Ajax 和 JavaScript 开发的)

    对于残障人士更易使用的各种机制。


    src/app/app.component.html

    
     [attr.aria-label]="actionName">{{actionName}} with Aria


    Class binding

    You can add and remove CSS class names 

    from an element's class attribute with a class binding.


    Class binding syntax resembles property binding. 

    Instead of an element property between brackets, 

    1. start with the prefix class

    2. optionally followed by a dot (.

    3. and the name of a CSS class: [class.class-name].

    2和3为可选的


    The following examples show 

    how to add and remove the application's "special" class with class bindings. 


    Here's how to set the attribute without binding:

    src/app/app.component.html
    
     class="bad curly special">Bad curly special


You can replace that with a binding to a string of the desired class names; 

this is an all-or-nothing, replacement binding.

src/app/app.component.html

 class="bad curly special"
     [class]="badCurly">Bad curly

Angular_文档_02_数据绑定_模板语法_第32张图片


Finally, you can bind to a specific class name. 

Angular adds the class when the template expression evaluates to truthy. 

It removes the class when the expression is falsy.

src/app/app.component.html

 [class.special]="isSpecial">The class binding is special
class="special" [class.special]="!isSpecial">This one is not so special

While 尽管 this is a fine way to toggle a single class name, 

但是, the NgClass directive is usually preferred 

when managing multiple class names at the same time.

Angular_文档_02_数据绑定_模板语法_第33张图片



Style binding

You can set inline styles with a style binding.

Style binding syntax resembles property binding.  (resemble类似)


Instead of an element property between brackets, 

start with the prefix style , followed by a dot (.

and the name of a CSS style property: 

例如:  [style.style-property]


src/app/app.component.html
 [style.color]="isSpecial ? 'red': 'green'">Red
 [style.background-color]="canSave ? 'cyan': 'grey'" >Save


Some style binding styles have a unit extension. (带单位)

The following example conditionally sets the font size in “em” and “%” units .

例如:  [style.font-size.px]="isTitle? 24: 16"

src/app/app.component.html
 [style.font-size.em]="isSpecial ? 3 : 1" >Big
 [style.font-size.%]="!isSpecial ? 150 : 50" >Small

While(尽管) this is a fine way to set a single style, 

(但是) the NgStyle directive is generally preferred 

when setting several inline styles at the same time.

Note that :

style property name can be written in either dash-case, as shown above, 

or camelCase, such as fontSize.



Event binding ( (event) 或者 (click) )

The bindings directives you've met so far 

flow data in one direction: from a component to an element.


Users don't just stare at the screen. 

They enter text into input boxes. 

They pick items from lists. 

They click buttons. 

Such user actions may result in : 

a flow of data in the opposite direction: from an element to a component.

Angular_文档_02_数据绑定_模板语法_第34张图片


The only way to know about a user action is to listen for certain events 

such as keystrokes, mouse movements, clicks, and touches. 

You declare your interest in user actions through Angular event binding.


Event binding syntax consists of a target event name within parentheses (小括号) on the left of an equal sign(等号), 

and a quoted template statement on the right. 


The following event binding listens for the button's click events, 

calling the component's onSave() method whenever a click occurs:

src/app/app.component.html
 (click)="onSave()">Save


Target event

A name between parentheses — for example, (click) — 

identifies the target event. 


In the following example, the target is the button's click event.

src/app/app.component.html
 (click)="onSave()">Save

Angular_文档_02_数据绑定_模板语法_第35张图片

Some people prefer the on- prefix alternative, known as the canonical form (规范形式):

src/app/app.component.html
 on-click="onSave()">On Save


Element events may be the more common targets, 

but Angular looks first to see if the name matches an event property of a known directive

as it does in the following example:


src/app/app.component.html


 (myClick)="clickMessage=$event" clickable>click with myClick

The myClick directive is further described in the section on aliasing input/output properties.

If the name fails to match an element event 

or an output property of a known directive

Angular reports an “unknown directive” error.


$event and event handling statements

In an event binding, 

Angular sets up an event handler for the target event.


When the event is raised, the handler executes the template statement. 

The template statement typically involves a receiver, 

which performs an action in response to the event, 

such as storing a value from the HTML control into a model.


The binding conveys information about the event, including data values, 

through an event object named $event.


The shape of the event object is determined by the target event. 

If the target event is a native DOM element event, 

then $event is a DOM event object, with properties such as target  and target.value.


Consider this example:

src/app/app.component.html
 [value]="currentHero.name"
       (input)="currentHero.name=$event.target.value" >

This code sets the input box value property by binding to the name property. (单向绑定) 


To listen for changes to the value, 

the code binds to the input box's input event. 

When the user makes changes, the input event is raised, 

and the binding executes the statement within a context that includes the DOM event object, $event.


To update the name property, the changed text is retrieved by following the path $event.target.value.


If the event belongs to a directive (recall 回想一下 that components are directives 组件是指令的一种), 

$event has whatever shape the directive decides to produce.


Custom events with EventEmitter

Directives typically raise custom events with an Angular EventEmitter

The directive creates an EventEmitter and exposes it as a property. 


The directive calls EventEmitter.emit(payload) to fire an event, 

passing in a message payload, which can be anything. 


Parent directives listen for the event by binding to this property 

and accessing the payload through the $event object.


Consider a HeroDetailComponent that presents hero information and responds to user actions. 

Although the HeroDetailComponent has a delete button it doesn't know how to delete the hero itself. 

The best it can do is raise an event reporting the user's delete request.


Here are the pertinent excerpts from that HeroDetailComponent:

src/app/hero-detail.component.ts (template)
template: `
{{prefix}} {{hero?.name}}
`
src/app/hero-detail.component.ts (deleteRequest)
// This component makes a request but it can't actually delete a hero.// 注意: 左边的就是事件名 (子组件内部发生的事件名,类似click)
deleteRequest = new EventEmitter<Hero>();

deleteBtnClicked() {
  this.deleteRequest.emit(this.hero);
}

The component defines a deleteRequest property that returns an EventEmitter. 

When the user clicks delete, the component invokes the delete() method, 

then telling the EventEmitter to emit a Hero object.


Now imagine a hosting parent component 

that binds to the HeroDetailComponent's deleteRequest event.

src/app/app.component.html (event-binding-to-component)
 (deleteRequest)="deleteHero($event)" [hero]="currentHero">父组件中, 监听子组件的 deleteRequest事件, 并执行父组件中的deleteHero方法,参数是$event; 父组件给子组件单向绑定了一个currentHero给子组件的hero属性

When the deleteRequest event fires, 

Angular calls the parent component's deleteHero method, 

passing the hero-to-delete (emitted by HeroDetailComponent) in the $event variable.


Template statements have side effects (副作用)

The deleteHero method has a side effect: it deletes a hero. 

Template statement side effects are not just OK, but expected (不只是OK,还恰恰是被期望的).


Deleting the hero updates the model, 

perhaps triggering other changes including queries and saves to a remote server. 


These changes percolate through the system 

and are ultimately displayed in this and other views.


Two-way binding ( [(...)] ) 双向绑定

You often want to both display a data property 

and update that property when the user makes changes.


On the element side (在元素层面上) that takes a combination of setting a specific element property 

and listening for an element change event.


Angular offers a special two-way data binding syntax for this purpose, [(x)]

The [(x)] syntax combines the brackets of property binding[x]

with the parentheses of event binding(x).

    [( )] = BANANA IN A BOX

Visualize a banana in a box to remember 

that the parentheses go inside the brackets.

The [(x)] syntax is easy to demonstrate 

when the element has a settable property called x 

and a corresponding event named xChange


Here's a SizerComponent that fits the pattern. 

It has a size value property and a companion sizeChange event:

src/app/sizer.component.ts
  1. import { Component, EventEmitter, Input, Output } from '@angular/core';
  2.  
  3. @Component({
  4. selector: 'app-sizer',
  5. template: `
`
  • })
  • export class SizerComponent {
  • @Input() size: number | string;
  • // 注意: 左边的就是子组件内部发生的事件名,类似于click
  • @Output() sizeChange = new EventEmitter();
  •  
  • dec() { this.resize(-1); }
  • inc() { this.resize(+1); }
  •  
  •     resize(delta: number) {
  •         // 范围: 8 ~ 40 牛B!
  •         this.size = Math.min(40, Math.max(8, +this.size + delta));
  •         this.sizeChange.emit(this.size);
  •     }
  • }


  • The initial size is an input value from a property binding. 

    Clicking the buttons increases or decreases the size

    within min/max values constraints, 

    and then raises (emits) the sizeChange event (事件名 就是 定义的属性名) with the adjusted size.


    Here's an example in which the AppComponent.fontSizePx is two-way bound to the SizerComponent:

    src/app/app.component.html (two-way-1)
     [(size)]="fontSizePx">
     [style.font-size.px]="fontSizePx">Resizable Text

    The AppComponent.fontSizePx establishes the initial SizerComponent.size value. 

    Clicking the buttons updates the AppComponent.fontSizePx via the two-way binding. 

    The revised AppComponent.fontSizePx value flows through to the style binding, making the displayed text bigger or smaller.

    The two-way binding syntax is really just syntactic sugar (语法糖)

    for a property binding and an event binding

    Angular_文档_02_数据绑定_模板语法_第36张图片

    Angular desugars the SizerComponent binding into this:

    src/app/app.component.html (two-way-2)  牛B!
     [size]="fontSizePx" (sizeChange)="fontSizePx=$event">

    The $event variable contains the payload of the SizerComponent.sizeChange event. 

    Angular assigns the $event value to the AppComponent.fontSizePx when the user clicks the buttons.


    Clearly the two-way binding syntax is a great convenience compared to separate property and event bindings.

    It would be convenient to use two-way binding with HTML form elements like  and  element's value property and input event.


    src/app/app.component.html

     [value]="currentHero.name"
           (input)="currentHero.name=$event.target.value" >

    That's cumbersome. 

    Who can remember which element property to set and which element event emits user changes? 

    How do you extract the currently displayed text from the input box so you can update the data property? 

    Who wants to look that up each time?

    Angular_文档_02_数据绑定_模板语法_第37张图片

    That ngModel directive hides these onerous details behind its own ngModel input 

    and ngModelChange output properties.

    src/app/app.component.html
    
      [ngModel]="currentHero.name"
      (ngModelChange)="currentHero.name=$event">

    The ngModel data property sets the element's value property 

    and the ngModelChange event property listens for changes to the element's value.


    The details are specific to each kind of element 

    and therefore the NgModel  directive only works for an element supported by a ControlValueAccessor 

    that adapts an element to this protocol. 

    The  box is one of those elements. 


    Angular provides value accessors for all of the basic HTML form elements 

    and the Forms guide shows how to bind to them.


    You can't apply [(ngModel)] to a non-form native element or a third-party custom component 

    until you write a suitable value accessor, a technique that is beyond the scope of this guide.


    You don't need a value accessor for an Angular component that you write 

    because you can name the value and event properties to suit Angular's basic two-way binding syntax 

    and skip NgModel  altogether. 


    The sizer shown above is an example of this technique.


    尽管 Separate ngModel  bindings is an improvement over binding to the element's native properties. 

    但是, You can do better.


    You shouldn't have to mention the data property twice. 

    Angular should be able to capture the component's data property 

    and set it with a single declaration, 

    which it can with the [(ngModel)] syntax:

    src/app/app.component.html
     [(ngModel)]="currentHero.name">

    Is [(ngModel)] all you need? 

    Is there ever a reason to fall back to its expanded form? 有什么理由再回退到原始的展开形式???


    The [(ngModel)] syntax can only set a data-bound property. 

    If you need to do something more or something different, you can write the expanded form.


    The following contrived example forces the input value to uppercase:

    src/app/app.component.html
    
      [ngModel]="currentHero.name"
      (ngModelChange)="setUppercaseName($event)">

    Here are all variations in action, including the uppercase version:

    Angular_文档_02_数据绑定_模板语法_第38张图片

    Built-in structural directives

    Structural directives are responsible for HTML layout. 

    They shape or reshape the DOM's structure

    typically by adding, removing, and manipulating the host elements to which they are attached.


    The deep details of structural directives are covered in the Structural Directives guide where you'll learn:


    This section is an introduction to the common structural directives:


    NgIf

    You can add or remove an element from the DOM 

    by applying an NgIf directive to that element (called the host element). 


    Bind the directive to a condition expression like isActive in this example.

    src/app/app.component.html
     *ngIf="isActive">

    Don't forget the asterisk (*) in front of ngIf 


    When the isActive expression returns a truthy value,

    NgIf adds the HeroDetailComponent to the DOM. 

    When the expression is falsy,

     NgIf removes the HeroDetailComponent from the DOM, 

    destroying that component and all of its sub-components.


    Show/hide is not the same thing

    You can control the visibility of an element with a class or style binding:

    src/app/app.component.html
    
     [class.hidden]="!isSpecial">Show with class
    [class.hidden]="isSpecial">Hide with class
    [class.hidden]="isSpecial"> [style.display]="isSpecial ? 'block' : 'none'">Show with style [style.display]="isSpecial ? 'none' : 'block'">Hide with style

    Hiding an element is quite different from removing an element with  NgIf (类似于v-if 和 v-show)

    When you hide an element, that element and all of its descendents remain in the DOM. 

    All components for those elements stay in memory 

    and Angular may continue to check for changes. 


    You could be holding onto considerable computing resources 

    and degrading performance, for something the user can't see.


    When NgIf is false, Angular removes the element and its descendents from the DOM. 

    It destroys their components, 

    potentially (潜在地) freeing up substantial resources, 

    resulting in a more responsive user experience.


    The show/hide technique is fine for a few elements with few children. 

    You should be wary when hiding large component trees; 

    NgIf may be the safer choice.


    Guard against null

    The ngIf directive is often used to guard against null. 

    Show/hide is useless as a guard. 

    Angular will throw an error if a nested expression tries to access a property of null.


    Here we see NgIf guarding two 

    s. 

    The currentHero name will appear only when there is a currentHero

    The nullHero will never be displayed.

    src/app/app.component.html
     *ngIf="currentHero">Hello, {{currentHero.name}}
    *ngIf="nullHero">Hello, {{nullHero.name}}

    See also the safe navigation operator described below.


    NgForOf  

    NgForOf is a repeater directive — a way to present a list of items. 

    You define a block of HTML that defines how a single item should be displayed. 

    Then You tell Angular to use that block as a template for rendering each item in the list.


    *ngFor="let girl of girlArr"


    Here is an example of NgForOf applied to a simple 

    :

    src/app/app.component.html
     *ngFor="let hero of heroes">{{hero.name}}

    You can also apply an NgForOf  to a component element, as in this example:

    src/app/app.component.html
     *ngFor="let hero of heroes" [hero]="hero">

    Don't forget the asterisk (*) in front of ngFor.


    The text(文本) assigned to *ngFor is the instruction that guides the repeater process.


    *ngFor microsyntax

    The string assigned to *ngFor is not a template expression

    It's a microsyntax — a little language of its own that Angular interprets. 


    The string "let hero of heroes" means:

    1. Take each hero in the heroes array, 

    2. store it in the local hero looping variable, 

    3. and make it available to the templated HTML for each iteration.


    Angular translates this instruction into a  around the host element, 

    then uses this template repeatedly to create a new set of elements 

    and bindings for each hero in the list.


    Learn about the microsyntax in the Structural Directives guide.


    Template input variables

    The let keyword before hero creates template input variable called hero

    The NgForOf directive iterates over the heroes array returned by the parent component's heroes property 

    and sets hero to the current item from the array during each iteration.


    You reference the hero input variable 

    within the NgForOf host element (and within its descendants) 

    to access the hero's properties. 


    Here it is referenced first in an interpolation 

    and then passed in a binding to the hero property of the  component.

    src/app/app.component.html
     *ngFor="let hero of heroes">{{hero.name}}
     *ngFor="let hero of heroes" [hero]="hero">

    Learn more about template input variables in the Structural Directives guide.


    *ngFor with index

    The index property of the NgForOf directive context 

    returns the zero-based index of the item in each iteration. 


    You can capture the index in a template input variable and use it in the template.

    The next example captures the index in a variable named i and displays it with the hero name like this.

    src/app/app.component.html
     *ngFor="let hero of heroes; let i=index">{{i + 1}} - {{hero.name}}

    NgFor is implemented by the NgForOf directive. 

    Read more about the other NgForOf context values 

    such as last,even, and odd in the NgForOf API reference.

    Angular_文档_02_数据绑定_模板语法_第39张图片
    *ngFor with trackBy

    The NgForOf directive may perform poorly, especially with large lists. 

    A small change to one item, an item removed, or an item added can trigger a cascade (级联) of DOM manipulations.


    For example, re-querying the server could reset the list with all new hero objects.

    Most, if not all, are previously displayed heroes. 

    You know this because the id of each hero hasn't changed. 

    But Angular sees only a fresh list of new object references. 

    It has no choice but to tear down the old DOM elements and insert all new DOM elements.

    Angular_文档_02_数据绑定_模板语法_第40张图片

    Angular can avoid this churn (折腾) with trackBy

    Add a method to the component 

    that returns the value NgForOf should track. 

    In this case, that value is the hero's id.

    src/app/app.component.ts
    trackByHeroes(index: number, hero: Hero): number { return hero.id; }

    In the microsyntax expression, set trackBy to this method.

    src/app/app.component.html
     *ngFor="let hero of heroes; trackBy: trackByHeroes">
      ({{hero.id}}) {{hero.name}}
    

    Here is an illustration of the trackBy effect. 

    "Reset heroes" creates new heroes with the same hero.ids. 

    "Change ids" creates new heroes with new hero.ids.

    Angular_文档_02_数据绑定_模板语法_第41张图片

    The NgSwitch directives   

    NgSwitch is like the JavaScript switch statement. 

    It can display one element from among several possible elements, 

    based on a switch condition


    Angular puts only the selected element into the DOM.

    NgSwitch is actually a set of three, cooperating directives: 

    1. NgSwitch,

    2. NgSwitchCase, 

    3. and NgSwitchDefault as seen in this example.

    Angular_文档_02_数据绑定_模板语法_第42张图片


    Angular_文档_02_数据绑定_模板语法_第43张图片

    src/app/app.component.html

     [ngSwitch]="currentHero.emotion">
          *ngSwitchCase="'happy'"    [hero]="currentHero">
            *ngSwitchCase="'sad'"      [hero]="currentHero">
       *ngSwitchCase="'confused'" [hero]="currentHero">
        *ngSwitchDefault           [hero]="currentHero">
    

    NgSwitch is the controller directive. 

    Bind it to an expression that returns the switch value

    The emotion value in this example is a string, but the switch value can be of any type.


    Bind to [ngSwitch]

    You'll get an error if you try to set *ngSwitch 

    because NgSwtich is an attribute directive, not a structural directive. 


    It changes the behavior of its companion directives. 

    It doesn't touch the DOM directly.


    Bind to *ngSwitchCase and *ngSwitchDefault

    The NgSwitchCase and NgSwitchDefault directives are structural directives 

    because they add or remove elements from the DOM.


    The switch directives are particularly useful for adding and removing component elements


    This example switches among four "emotional hero" components defined in the hero-switch.components.ts file. 

    Each component has a hero input property which is bound to the currentHero of the parent component.

    Switch directives work as well with native elements and web components too. 

    For example, you could replace the  switch case with the following.

    src/app/app.component.html
     *ngSwitchCase="'confused'">Are you as confused as {{currentHero.name}}?

    Template reference variables ( #var )  

    A template reference variable is often a reference to a DOM element within a template. 

    It can also be a reference to an Angular component or directive or a web component.

    Use the hash symbol (#) to declare a reference variable. 


    The #phone declares a phone variable on an  element.

    src/app/app.component.html
     #phoneNode placeholder="phone number">

    You can refer to a template reference variable anywhere in the template. 

    The phoneNode variable declared on this  is consumed in a 


    How a reference variable gets its value

    In most cases, Angular sets the reference variable's value to the element on which it was declared. 

    In the previous example, phoneNode refers to the phone number  box. 

    The phone button click handler passes the input value to the component's callPhone method. 


    But a directive can change that behavior and set the value to something else, 

    such as itself. The NgForm  directive does that.

    The following is a simplified version of the form example in the Forms guide.

    src/app/hero-form.component.html
     (ngSubmit)="onSubmit(heroForm)" #heroForm="ngForm">
       class="form-group">
         for="name">Name
           class="form-control" name="name" required [(ngModel)]="hero.name">
        
      
       type="submit" [disabled]="!heroForm.form.valid">Submit
    
     [hidden]="!heroForm.form.valid">
      {{submitMessage}}
    

    A template reference variable, heroForm, appears three times in this example, 

    separated by a large amount of HTML. What is the value of heroForm?

    下面这句话有点绕:

    如果Angular没有接管表单的话,那么它将是 HTMLFormElement

    但是, 一旦你导入了FormsModule的话, Angular将接管表单 (意思是这时候它就不是HTMLFormElement了)

    If Angular hadn't taken it over when you imported the FormsModule, it would be the HTMLFormElement


    The heroForm is actually a reference to an Angular NgForm directive 

    with the ability to track the value and validity of every control in the form.


    The native 

     element doesn't have a form property. 

    But the NgForm directive does, 

    which explains how you can disable the submit button if the heroForm.form.valid is invalid 

    and pass the entire form control tree to the parent component's onSubmit method.


    Template reference variable warning notes


    A template reference variable (#phoneNode

    is not the same as a template input variable (let phone) such as you might see in an *ngFor

    Learn the difference in the Structural Directives guide.


    The scope of a reference variable is the entire template (整个模板)

    Do not define the same variable name more than once in the same template. 

    The runtime value will be unpredictable.


    You can use the ref- prefix alternative to #

    This example declares the cupNode variable as ref-cupNode instead of #cupNode.

    src/app/app.component.html
     ref-cupNode placeholder="girl's cup">
     (click)="showGirlCup(cupNode.value)">show girl's cup

    Input and Output properties

    An Input property is a settable property annotated with an @Input decorator. 

    Values flow into the property when it is data bound with a property binding


    An Output property is an observable property annotated with an @Output decorator. 

    The property almost always returns an Angular EventEmitter

    Values flow out of the component as events bound with an event binding.


    You can only bind to another component or directive through its Input and Output properties.

    Remember that all components are directives.

    The following discussion refers to components for brevity 

    and because this topic is mostly a concern for component authors.


    Discussion

    You are usually binding a template to its own component class

    In such binding expressions, the component's property or method is to the right of the (=).

    src/app/app.component.html
     [src]="iconUrl"/>
     (click)="onSave()">Save

    The iconUrl and onSave are members of the AppComponent class. 

    They are not decorated with @Input() or @Output

    下面这句有点叼 ( Angular 不 在 乎)

    Angular does not object. 


    You can always bind to a public property of a component in its own template. 

    It doesn't have to be an Input or Output property


    A component's class and template are closely coupled. 

    They are both parts of the same thing. 

    Together they are the component. 

    Exchanges between a component class and its template are internal implementation details.


    Binding to a different component

    You can also bind to a property of a different component. 

    In such bindings, the other component's property is to the left of the (=).


    In the following example, 

    the AppComponent template binds AppComponent class members to properties of the HeroDetailComponent 

    whose selector is 'app-hero-detail'.


    src/app/app.component.html

    说明: 监听来自app-hero-detail组件内部发出的事件:deleteRequestFromDetailComponent

    事件处理函数 deleteHeroHandlerInAppComponent 位于app.component

     [hero]="currentHero" (deleteRequestFromDetailComponent)="deleteHeroHandlerInAppComponent($event)">
    

    The Angular compiler may reject these bindings with errors like this one:

    Uncaught Error: Template parse errors:
    Can't bind to 'hero' since it isn't a known property of 'app-hero-detail'


    You know that HeroDetailComponent has hero and deleteRequestFromDetailComponent properties. 

    But the Angular compiler refuses to recognize them.

    这是因为,默认情况下:

    The Angular compiler won't bind to properties of a different component 

    unless they are Input or Output properties.


    There's a good reason for this rule.


    It's OK for a component to bind to its own properties. 

    The component author is in complete control of those bindings.


    But other components shouldn't have that kind of unrestricted access. 

    You would have a hard time supporting your component if anyone could bind to any of its properties. 


    Outside components should only be able to bind to the component's public binding API.


    Angular asks you to be explicit about that API. 

    It's up to you to decide which properties are available for binding by external components.


    TypeScript public doesn't matter

    You can't use the TypeScript public and private access modifiers 

    to shape the component's public binding API.


    All data bound properties must be TypeScript public properties. 

    Angular never binds to a TypeScript private property.


    Angular requires some other way to identify properties 

    that outside components are allowed to bind to. 

    That other way is the @Input() and @Output() decorators.


    Declaring Input and Output properties

    In the sample for this guide, 

    the bindings to HeroDetailComponent do not fail 

    because the data bound properties are annotated with @Input() and @Output() decorators.

    src/app/hero-detail.component.ts
    @Input()  hero: Hero;
    @Output() deleteRequestFromDetailComponent = new EventEmitter<Hero>();

    Alternatively, you can identify members in the inputs and outputs arrays of the directive metadata, 

    as in this example:

    src/app/hero-detail.component.ts
    @Component({
      inputs: ['hero'],
      outputs: ['deleteRequestFromDetailComponent'],
    })


    Input or output?

    Input properties usually receive data values. 

    Output properties expose event producers, such as EventEmitter objects.

    下面这句翻译有点绕:

    输入和输出这两个词  是从目标指令的角度来说的 (不能说点人话么?)

    The terms input and output reflect the perspective of the target directive.


    HeroDetailComponent.hero is an input property 

    from the perspective of HeroDetailComponent 

    because data flows into that property from a template binding expression.


    HeroDetailComponent.deleteRequestFromDetailComponent is an output property 

    from the perspective of HeroDetailComponent 

    because events stream out of that property and toward the handler in a template binding statement.


    Aliasing input/output properties 

    Sometimes the public name of an input/output property 

    should be different from the internal name.


    This is frequently the case with attribute directives

    Directive consumers expect to bind to the name of the directive. 


    For example, when you apply a directive with a myClick selector to a 

     tag, 

    you expect to bind to an event property that is also called myClick.


    src/app/app.component.html

     (myClick)="clickMessage=$event" clickable>    click with myClick

    However, the directive name is often a poor (不好的) choice 

    for the name of a property within the directive class. 


    The directive name rarely describes what the property does. 

    The myClick directive name is not a good name for a property that emits click messages.


    Fortunately, you can have a public name for the property that meets (满足) conventional expectations, 

    while using a different name internally. 


    In the example immediately above, 

    you are actually binding through the myClick alias to the directive's own clicks property.


    You can specify the alias for the property name 

    by passing it into the input/output decorator like this:


    src/app/click.directive.ts

    @Output('myClick') clickEmitter = new EventEmitter(); //  @Output(alias) propertyName = ...

    You can also alias property names in the inputs and outputs arrays. 

    You write a colon-delimited (:) string with the directive property name on the left 

    and the public alias on the right:


    src/app/click.directive.ts
    @Directive({
      outputs: ['clickEmitter:myClick']    // propertyName:alias
    })

    Angular_文档_02_数据绑定_模板语法_第44张图片

    Template expression operators

    The template expression language 

    employs(使用了) a subset(子集) of JavaScript syntax 

    supplemented with a few special operators for specific scenarios(场景). 


    The next sections cover two of these operators: pipe and safe navigation operator.


    The pipe operator ( | )

    The result of an expression might require some transformation before you're ready to use it in a binding. 


    For example, you might display a number as a currency, force text to uppercase, or filter a list and sort it.


    Angular pipes are a good choice for small transformations such as these. 

    Pipes are simple functions that accept an input value 

    and return a transformed value. 


    They're easy to apply within template expressions, using the pipe operator (|):

    src/app/app.component.html
        through uppercase pipe: {{title |uppercase}}


    The pipe operator passes the result of an expression on the left 

    to a pipe function on the right.


    You can chain expressions through multiple pipes:

    src/app/app.component.html
    
    
    Title through a pipe chain: {{title |uppercase |lowercase}}

    And you can also apply parameters to a pipe:

    src/app/app.component.html
    
    
    Birthdate: {{currentHero?.birthdate | date:'longDate'}}


    The json pipe is particularly helpful for debugging bindings:

    src/app/app.component.html (pipes-json)
    {{currentHero | json}}


    The generated output would look something like this

    { "id": 0, "name": "未闻花名", "description": "あの日見た花の名前を僕達はまだ知らない",
      "pubdate": "2011-04-14T08:00:00.000Z",
      "url": "https://www.baidu.com/s?wd=anohana",
      "rate": 9.5 }


    The safe navigation operator ( ?. ) and null property paths

    The Angular safe navigation operator (?.) is a fluent and convenient way 

    to guard against null and undefined values in property paths. 

    牛X!

    Here it is, protecting against a view render failure if the currentHero is null.

    src/app/app.component.html

    The current hero's name is {{currentHero?.name}}


    What happens when the following data bound title property is null?

    src/app/app.component.html
    The title is {{title}}


    The view still renders but the displayed value is blank; 

    you see only "The title is" with nothing after it. 

    That is reasonable behavior. At least the app doesn't crash.


    Suppose the template expression involves a property path, 

    as in this next example that displays the name of a null hero.

    The null hero's name is {{nullHero.name}}

    JavaScript throws a null reference error, and so does Angular:

    TypeError: Cannot read property 'name' of null in [null].


    Worse, the entire view disappears.

    This would be reasonable behavior if the hero property could never be null. 

    If it must never be null and yet it is null, 

    that's a programming error 

    that should be caught and fixed. 

    Throwing an exception is the right thing to do.


    On the other hand, 

    null values in the property path may be OK from time to time, 

    especially when the data are null now and will arrive eventually.


    While waiting for data, 

    the view should render without complaint(够拟人化), 

    and the null property path should display as blank just as the title property does.

    Unfortunately, the app crashes when the currentHero is null.


    You could code around that problem with *ngIf.

    src/app/app.component.html
    
     *ngIf="nullHero">The null hero's name is {{nullHero.name}}

    Angular_文档_02_数据绑定_模板语法_第45张图片

    You could try to chain parts of the property path with &&

    knowing that the expression bails out when it encounters the first null.


    src/app/app.component.html

    The null hero's name is {{nullHero && nullHero.name}}


    These approaches have merit but can be cumbersome, 

    especially if the property path is long. 


    Imagine guarding against a null somewhere in a long property path such as a.b.c.d.


    The Angular safe navigation operator (?.) is a more fluent and convenient way 

    to guard against nulls in property paths. 


    The expression bails out when it hits the first null value. 

    The display is blank, but the app keeps rolling without errors.

    src/app/app.component.html
    
    The null hero's name is {{nullHero?.name}}

    It works perfectly with long property paths such as a?.b?.c?.d.


    Angular_文档_02_数据绑定_模板语法_第46张图片

    The non-null assertion operator ( ! ) 

    As of Typescript 2.0, 

    you can enforce strict null checking with the --strictNullChecks flag. 


    TypeScript 就会确保不存在意料之外的 null 或 undefined。

    TypeScript then ensures that no variable is unintentionally (意料之外的) null or undefined.


    In this mode, typed variables disallow null and undefined by default. 


    The type checker throws an error 

    if you leave a variable unassigned or try to assign null 

    or undefined to a variable whose type disallows null and undefined.


    The type checker also throws an error 

    if it can't determine whether a variable will be null or undefined at runtime. 


    尽管 You may know that can't happen 

    但是 but the type checker doesn't know. 


    You tell the type checker that it can't happen 

    by applying the post-fix non-null assertion operator (!) 非空断言操作符.


    The Angular non-null assertion operator (!)  

    serves the same purpose in an Angular template.


    For example, 

    after you use *ngIf to check that hero is defined, 

    you can assert that hero properties are also defined.


    src/app/app.component.html

    
     *ngIf="hero">
      The hero's name is {{hero!.name}}  告诉检查器: 我知道这个hero肯定不是空的,所以请别报错了
    

    When the Angular compiler turns your template into TypeScript code, 

    it prevents TypeScript from reporting that hero.name might be null or undefined.


    Unlike the safe navigation operator

    the non-null assertion operator does not guard against null or undefined. 


    Rather it tells the TypeScript type checker to suspend strict null checks for a specific property expression.

    You'll need this template operator when you turn on strict null checks. It's optional otherwise.

    非空检查断言操作符: 适用于 打开了非空检查严格模式的同时, 又不想对个别属性进行非空检查时的情形


    The $any type cast function ($any( ))

    Sometimes a binding expression will be reported as a type error 

    and it is not possible or difficult to fully specify the type. 


    To silence the error, you can use the $any cast function to cast the expression to the any type.

    src/app/app.component.html
    
    
    The hero's marker is {{$any(hero).marker}}

    In this example, 

    when the Angular compiler turns your template into TypeScript code, 

    it prevents TypeScript from reporting that marker is not a member of the Hero interface.

    因为$any(hero) 函数 已经把hero转成了any类型了

    所以就不会报错:说remark不是hero对象的一个属性了


    The $any cast function can be used in conjunction with this 

    to allow access to undeclared members of the component.


    $any(this).member

    还可以这样子用!!!

    src/app/app.component.html

    
    
    Undeclared members is {{$any(this).member}}

    The $any cast function can be used anywhere 

    in a binding expression where a method call is valid.

    $any()转换函数 可以用在 任何可进行方法调用的地方


    Summary

    You've completed this survey of template syntax. 

    Now it's time to put that knowledge to work on your own components and directives.

     










































    未完待续,下一章节,つづく

    你可能感兴趣的:(#,Angular2,Angular,数据绑定,模板语法)