Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Angular 2模板语法 #64

Open
kittencup opened this issue Jan 23, 2016 · 2 comments
Open

Angular 2模板语法 #64

kittencup opened this issue Jan 23, 2016 · 2 comments
Labels

Comments

@kittencup
Copy link
Owner

原文地址:http://victorsavkin.com/post/119943127151/angular-2-template-syntax

属性和事件绑定

属性和事件绑定在指令中是公开的API,数据从属性绑定流入指令,从事件中流出指令

input绑定

假设我们有一个组件来呈现的Todo,我们可以在我们的模板中使用这个组件,如下所示:

<todo-cmp [model]="myTodo"></todo-cmp>

这告诉Angular,只要myTodo发生变化,当有一个新的todo时,Angular会通过模型来自动更新todo组件

output绑定

现在,让我们添加一个事件使组件更有趣。

<todo-cmp [model]="todo" (complete)="onCompletingTodo(todo)"></todo-cmp>

当事件complete触发时会告诉Angular调用onCompletingTodo方法

现在,让我们看看TodoCmp本身。

@Component({
  selector: 'todo-cmp',
  input: ['model'],
  output: ['complete']
})
class TodoCmp {
  model;
  complete = new EventEmitter(); // TypeScript 支持初始化

  onCompletedButton() {
    this.complete.next(); // 触发事件
  }
}

这个组件声明数据绑定的名字为model,事件名为complete

Angular使用Rx事件风格来处理事件的接收,EventEmitter即实现了observable,又实现了observer接口(规范),因此,我们可以用它来触发事件,并且Angular也可以用它来监听事件。

正如你所看到的,属性和事件绑定的核心语法很简单。在核心语法之上,Angular提供了一些语法糖,使表达常用的编程模式更加容易。重要的是要了解,这仅仅是语法糖,而且它不会改变语义。

双向绑定

双向数据绑定在某些情况下是很方便的,最值得注意的是处理输入,正如我刚才提到的,属性绑定用于数据从父传递给子,事件绑定用于从子到父的数据传递。因此,我们可以使用两种方法来实现双向绑定。

<input [ngModel]="todo.text" (ngModel)="todo.text=$event"></input>

虽然这可以正常工作,但这太罗嗦。因为这是一个常见的模式,所以Angular提供语法糖简化这种写法。

<input [(ngModel)]="todo.text"></input>

要完成这个例子,让我们来实现Angular 2的ng-model

@Directive({
  selector: '[ng-model]',
  input: ['ngModel'],
  output: ['ngModelChanged: ngModel'],
  host: {
    "[value]": 'ngModel',
    "(input)": "ngModelChanged.next($event.target.value)"
  }
})
class NgModelDirective {
  ngModel:any; // stored value
  ngModelChanged:EventEmitter; // an event emitter
}

这是一种比较幼稚的ngModel实现,但它说明了如何实现双向数据绑定行为:输入将在text发生更改时更新,并在输入更改时更新text字段。

请注意,于Angular 1相反,你只要看看模板就可以知道哪些绑定是“双向”,哪些是“单向”。

此外,因为只有一个方向,属性绑定,被Angular自动执行,双向的行为更可预测的。

它不会破坏任何Angular 2的语法:这只是一些语法糖,没有别的。

我想提一提,你不用去实现NgModel。Angular 2配备了一个表单处理模块,其包括NgModel的实现。

插值

<div>Hello {{name}}</div>

使用语法糖

<div [textContent]="interpolate(['Hello'], [name])"></div>

绑定直接量

<show-title title="Some Title"></show-title>

使用语法糖

<show-title [title]=" 'Some Title' "></show-title>

移除括号

你可以使用bind-,in-和bindon-在你的模板中代替所有的括号,虽然这几乎不能称为“语法糖”

<some-component [prop]="someExp" (event)="someEvent()" [(twoWayProp)]="someExp"></show-title>

于上面相同

<some-component bind-prop="someExp" on-event="someEvent()" bindon-twoWayProp="someExp"></show-title>

局部变量

有两个组件互相交互,这种情况并不少见,Angular 2在模板中支持定义的局部变量。

<video-player #player></video-player>
<button (click)="player.pause()">Pause</button>

player表示当前video-player组件自身,而没有#player的组件则可以通过player访问video-player组件

<input #i>
{{i.value}}

语法糖

<video-player #player></video-player>

相当于

<video-player var-player></video-player>

模板和 *

Angular以特殊的方式对待template元素。它们用来创建视图,你可以动态操作DOM块,*语法是一种捷径,它可以使你不用写出全部的<template>元素,让我告诉你它是如何工作。

假设我们呈现Todo列表组件。

<todo-cmp *ngFor="#t of todos; #i=index" [model]="t" [index]="t"></todo-cmp>

去除语法糖后变为

<todo-cmp template="ngFor #item of items; #i=index" [model]="t" [index]="i"></todo-cmp>

再一次去除语法糖后变为

<template ngFor #item="$implicit" [ngForOf]="items" #i="index">
  <todo-cmp [model]="t" [index]="t"></todo-cmp>
</template>

该ngFor指令在视图上会创建$implicit和$index变量并绑定到模板上,在模板元素上声明的所有变量只能在元素内提供,这就是为什么以下是不正确

<todo-cmp *ngFor="#t of todos"></todo-cmp>
{{t}} <!-- t cannot be referenced here -->

你需要重点理解的是,当你建立你自己的指令来处理视图时,你的 * syntax 语法能扩展成什么。例如,如果你看ngfor,你会发现它有ngforof属性,但是没有of属性。

为什么不对ng-for自定义语法,象angular 1一样呢?这有几个很好的理由。有自定义语法意味着你必须知道这个微语言如何操作。这也意味着在工具(例如,IDE和LInter)中不能理解你的模板而且并不能提供自动完成和重构。

web组件和原生元素

在这篇博客我谈到了一切的内容 - 局部变量,属性和事件绑定 - 对于web组件和标准的html元素来讲使用方式是完全相同

我可以用Web组件更换TodoCmp,仍然使用相同方式与它进行交互。

<todo-cmp [model]="todo" (completed)="onCompletingTodo(todo)"></todo-cmp>

我可以用Web组件更换视频播放器,仍然使用相同方式与它进行交互。

<video-player #player></video-player>
<button (click)="player.pause()">Pause</button>

这将变得极为重要,因为更多的组件库可用。当运行在非Angular组件上也很重要(这是Angular 2目标之一),否者你不得不把每一个原生组件包装成一个Angular 2组件,那真的很糟糕。Angular 2你可以直接使用任何本地组件,使用相同的语法,只有当你例如想使用依赖注入时,可将它包装为Angular组件

设计目标

我希望你能明白在Angular 2里模板语法是怎么运作的.现在,我想来讨论一下为什么它用这种方式运作?它这么设计的目的是什么?

我们的主要设计目标是使模板更加清晰,开发者在不知如果使用指令情况也能够理解和重构模板,以及指令是如何工作的。

<component [property1]="name" property2="name"></component>

不管组件元素是什么,property1是指向一个name变量的属性 property2属性只表示一个name字符串,你也需要知道,组件的property2属性无法更新,property1属性绑定更新是从父到子。

<component [(property1)]="name"></component>

在这里,我们可以看到,name可以被更新,因为我们使用[()]语法

同样,你可以告诉在模板中定义了哪些变量,来看这里的*nf-for

<todo-cmp *ngFor="#t of todos"></todo-cmp>

其次,我们希望有丰富的开发工具,在下面的列子中,工具可以静态地推断出第一个index是组件上的字段,而第2个index是ng-for输出的局部变量

{{index}}
<div *ngFor="#item of items; var index=index">
  {{index}
</div>

这就是其中的一个Angular 2模板的实例分析。由于IDE可以分析模板,所以他们可以提供自动完成和重构。

最后,我们要本地组件和web组件无缝集成。这意味着Angular 不能有特殊的特定事件(例如,ng-click),而必须提供通用机制,更新任何属性或监听任何事件。

@melodyJie
Copy link

melodyJie commented Aug 9, 2017

I have a problem.

<td *ngFor="let val of data | slice:30:36;let i = index;">
       <input type="number" [(ngModel)]="val" />
</td>

I want to do [(ngModel)]="val" in ngFor . but there is a problem.

@hstarorg
Copy link

hstarorg commented Aug 9, 2017

<template ngFor #item="$implicit" [ngForOf]="items" #i="index">
  <todo-cmp [model]="t" [index]="t"></todo-cmp>
</template>

That has not t in template property..

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants