Angular2 双向数据绑定

23

我知道Angular2没有双向数据绑定,但是否有办法模仿Angular1.x的双向数据绑定行为呢?


双向数据绑定被淘汰的原因是它本质上存在缺陷,所以虽然你可以这样做,但你不想这样做。 - unobf
关于循环和性能的问题?但是假设我确实希望将其用于特定情况,我该怎么做? - Ronald Wildenberg
1
我在页面上有一个标签和一个文本框,可以更改标签。如果我在文本框中输入,标签应该会改变。这实际上并不是双向的,现在想起来...但我仍然不知道如何实现这个... - Ronald Wildenberg
8个回答

20

注意 - 向下滚动答案以获取ng-model绑定

你实际上可以做到这一点,只需要调用内部changlistener tick(类似于digest),以在区域中更新绑定,你可以添加一个(keyup)事件来完成。同样,您也可以使用指令绑定,并使用组件设置的properties字典。

例:

<input #label (keyup)> 
<!-- variable #label represented as the element itself and accessible as property on controller instance 
 You can even bind keyup to a function or another another function and pass value from the label property-->

显示为:

<p>{{label.value}}</P>

父组件有一个文本框和一个标签。

import { Component, bootstrap} from '@angular/core';
import {Display} from 'display';

@Component({
  selector: 'my-app',
  template: `<p><b>Parent Component:</b><p><input #label (keyup) (change)="handleChange(label.value)">
        <p>{{label.value}}</P> <display [text]="label"></display></p></p>`,
  directives: [Display]
})

class MainComponent {
  label: any;

  constructor() {

  }

  handleChange(label){
    this.label = label;
    console.log(this.label);
  }

}

现在也在子组件中显示:

@Component({
  selector: 'edit',
  template: `<p><b>Child Component:</b></p>{{text.value}}`
})

export class Edit {
    @Input() text:any;
}

演示



更新 - 用于双向绑定的ng-model

尽管Angular2默认是一次性绑定,但引入了ngModel语法糖以实现双向绑定。例如:

<input ngControl="name" [(ngModel)]="name">

这里使用方括号([..])表示属性绑定,圆括号((..))表示事件绑定。基本上当您使用ng-model时,您启用了绑定的两个方面,其中ngModel更多的是一个事件。在幕后,它创建了一个可观察事件(EventEmitter)来跟踪绑定元素中value的更改,并相应地更新绑定的属性。

例如:

包含表单指令:

 import {FORM_DIRECTIVES} from '@angular/common';

并且和表单一起使用

   <form (ngSubmit)="onSubmit()" let-f="form">
      <input ngControl="name" [(ngModel)]="name">
      <button>Click me and check console</button>
   </form>

没有表单

  <input  [(ngModel)]="name">
  <button (click)="onSubmit()">Click me and check console</button>

不再必要

在视图注释中包含formDirectives依赖项。

@Component({
  template: .....,
  directives: [FORM_DIRECTIVES]
})

演示

此外,阅读Victor Savkin的精彩文章,了解在angular2中通过创建ng-model事件以及其工作原理来实现双向绑定。


对于较新的Angular(例如v5),您必须导入FormsModule - zed
更新Victor Savkin文章的URL:https://vsavkin.com/angular-2-template-syntax-5f2ee9f13c6a - BlackICE
我认为子组件的选择器名称应该是“display”。 - Alessandro_Russo

15
现在你可以使用以下语法,通过ngModel轻松实现此功能:
<input [(ngModel)]="myProp" />

方括号和圆括号的组合表示“双向绑定”。 请查看此处的plunk here

11

在Angular2中确实有双向绑定。请参见此处:https://angular.io/docs/ts/latest/guide/template-syntax.html#!#ngModel

那么,在自定义组件中如何使用它呢?

我喜欢做的事情是这样的:

private currentSelectedItem: MachineItem;
@Output() selectedItemChange: EventEmitter<MachineItem> = new EventEmitter<MachineItem>();

@Input() set selectedItem(machineItem: MachineItem) {
    this.currentSelectedItem = machineItem;
    this.selectedItemChange.emit(machineItem); 
}

get selectedItem(): MachineItem {
    return this.currentSelectedItem; 
}

然后像这样使用它

<admin-item-list [(selectedItem)]="selectedItem"></admin-item-list>

你也可以在值被更改时直接发出新值。但我觉得在设置器方法中全局完成这个操作非常方便,这样在将其直接绑定到视图时就不必再费心了。


这是我目前为止找到的最明智的答案,但不幸的是,如果一开始有一个 selectedItem,我无法为其设置初始值,selectedItem 将不会被设置。 - Tolga E
不知道我是否理解了问题,但是你不能在构造函数中设置currentSelectedItem(事件不会被触发)或者设置selectedItem吗? - Mario Eis
我之前尝试过那个,但不幸的是事件确实被触发了,所以你会陷入无限循环。 - Tolga E
当前选定项为xyz触发了事件吗? - Mario Eis
1
请注意,输出发射器的名称很重要。它必须是“selectedItemChange”,才能正常工作。规则是,如果您的输入是“x”,则输出发射器的名称应命名为“xChange”,以使双向绑定语法正常工作。在我看到提到这一事实的文章之前,我一直在苦苦思索。https://bytes.vokal.io/20160509-angular-data-binding/ - Yohan Liyanage
没错,就是这样。这正是 Angular 文档中所描述的(请参见我提供的链接),位于“[(ngModel)] 内部”一节下面。 - Mario Eis

6
您可以通过附加到输入字段上的事件并更新内部值来实现此操作,就像在此示例中所做的那样:

http://plnkr.co/edit/lOFzuWtUMq1hCnrm9tGA?p=preview

创建一个组件,它具有内部属性来保存标签this.label和回调changeLabel,该回调期望一个事件对象。
@Component({
  selector: 'app',
  templateUrl: 'bound.html'
})
class App {
  label: string;
  constructor() {
    this.label = 'default label'
  }
  changeLabel(event) {
    this.label = event.target.value;
  }
}

bootstrap(App);

创建您的模板并将回调附加到适当的事件上(您可以将其附加到“keypress”事件,但那样您可能需要一个超时。出于简单起见,我将其附加到“change”事件上(这意味着您可能需要切换输入以查看更新)。
<label for="myinput">{{label}}</label>
<input id="myinput" type="text"/>
<p></p>You can change the label above by typing something below</p>
<label for="labeltext">New Label Text</label>
<input type="text" id="labeltext" (change)="changeLabel($event)"/>

5

有另一种方法可以欺骗Angular2实现双向绑定。不要传递属性,而是将对象传递给组件。如果通过单向绑定传递对象,则其所有属性实际上都是双向绑定的。这使得组件变得不够灵活,因为它需要知道对象,但在许多情况下仍然很有用。

我有一个看起来像这样的组件:

import { Component, Input }    from "@angular/core";
import { NgSwitch, NgSwitchWhen, NgSwitchDefault }    from "@angular/common";

export class Movie
{
    public Title: string;
    public Rating: number;
    public Seen: boolean;
}

@Component
({
    selector: "hh-image-checkbox",
    template: `
        <div [ngSwitch]="movie.Seen"> 
            <div *ngSwitchWhen="true">
                <img src="/Content/res/CheckTrue.png" (click)="onClick()"> 
            </div> 
            <div *ngSwitchDefault> 
                <img src="/Content/res/CheckFalse.png" (click)="onClick()"> 
            </div> 
        </div>
        `,
    directives: [NgSwitch, NgSwitchWhen, NgSwitchDefault]
})

export class ImageCheckboxComponent
{
    @Input() movie: Movie;

    public onClick()
    {
        this.movie.Seen = !this.movie.Seen;
    }
}

它的调用方式如下所示:
<hh-image-checkbox [movie]="movie"></hh-image-checkbox>

电影对象本身是单向绑定的,但它的所有属性都可以用于双向绑定。

不需要导入NgSwitch,NgSwitchWhen,NgSwitchDefault并将它们添加到directives中。它们由PLATFORM_DIRECTIVES全局提供。 - Günter Zöchbauer

2
这是一个简单的 plunker 示例,展示了根据 Angular2 2.0.0-beta.17 的方式实现单向、双向和事件驱动方法。

http://plnkr.co/eXZMoU

双向事件和属性
<input [(ngModel)]="name" />

一次性财产
<input [value]="name" />

事件驱动
<input (input)="name=$event.target.value">

我们可以查看 Angular 文档以获取 更多 信息。 [更新于2020年1月26日] 由于 Angular2 beta 库已从项目 CDN 中删除!上面的 plnkr 链接不再有效。
请使用下面的新 plnkr Angular 6+ 页面,我将之前的页面移植到了 NPMJS 上,采用了新的 Angular 版本和新的 plnkr!

http://next.plnkr.co/edit/4okdOSgw3SMvdktR?preview


1

来自文档:

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.

更多信息请参见


0

很简单,试试这个:

<input [(ngModel)]="property" placeholder="property Value"/>
<h1>{{property}}</h1>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接