如何观察形状在角度上的变化

在 Angular 中,我可能有一个这样的形式:

<ng-form>
<label>First Name</label>
<input type="text" ng-model="model.first_name">


<label>Last Name</label>
<input type="text" ng-model="model.last_name">
</ng-form>

在相应的控制器中,我可以很容易地观察表单内容的变化,如下所示:

function($scope) {


$scope.model = {};


$scope.$watch('model', () => {
// Model has updated
}, true);


}

这是 JSFiddle 上的角度示例

我不知道如何用角度来做同样的事情。显然,我们不再有 $scope,$rootScope。肯定有办法可以完成同样的事情吧?

这是 Plunker 上的角度例子

135555 次浏览

如果使用 FormBuilder,请参见@dfsq 的答案。

如果您没有使用 FormBuilder,有两种方式可以获得更改的通知。

方法1

正如在问题的评论中所讨论的,在每个输入元素上使用 事件绑定。添加到模板中:

<input type="text" class="form-control" required [ngModel]="model.first_name"
(ngModelChange)="doSomething($event)">

然后在你的部分:

doSomething(newValue) {
model.first_name = newValue;
console.log(newValue)
}

表格页面有一些关于 ngModel 的附加信息,这些信息在这里是相关的:

ngModelChange不是 <input>元素事件。它实际上是 NgModel指令的事件属性。当 Angular 看到形式为 [(x)]的绑定目标时,它期望 x指令具有 x输入属性和 xChange输出属性。

另一个奇怪之处是模板表达式 model.name = $event。我们习惯于看到来自 DOM 事件的 $event对象。NgModelChange 属性不生成 DOM 事件; 它是一个 Angular EventEmitter属性,在触发时返回输入框值。.

我们几乎总是喜欢 [(ngModel)]。如果我们必须在事件处理中做一些特殊的事情,比如退出或者限制键击,我们可能会拆分绑定。

对你来说,我猜你想做点特别的事。

方法2

定义一个本地模板变量并将其设置为 ngForm
对输入元素使用 ngControl。
使用@ViewChild 获取对表单的 NgForm 指令的引用,然后订阅 NgForm 的 ControlGroup 以进行更改:

<form #myForm="ngForm" (ngSubmit)="onSubmit()">
....
<input type="text" ngControl="firstName" class="form-control"
required [(ngModel)]="model.first_name">
...
<input type="text" ngControl="lastName" class="form-control"
required [(ngModel)]="model.last_name">


class MyForm {
@ViewChild('myForm') form;
...
ngAfterViewInit() {
console.log(this.form)
this.form.control.valueChanges
.subscribe(values => this.doSomething(values));
}
doSomething(values) {
console.log(values);
}
}

plunker

有关方法2的更多信息,请参见 萨维金的视频

另请参阅@Thierry 的答案,了解更多关于如何使用可观察到的 valueChanges的信息(例如在处理更改之前进行一些去弹性/等待)。

UPD。 答案和演示更新,以符合最新的角度。


您可以订阅整个表单更改,因为表示表单的 表格组提供了 valueChanges属性,这是一个可观察的实例:

this.form.valueChanges.subscribe(data => console.log('Form changes', data));

在这种情况下,您需要使用 造型师手动构建表单:

export class App {
constructor(private formBuilder: FormBuilder) {
this.form = formBuilder.group({
firstName: 'Thomas',
lastName: 'Mann'
})


this.form.valueChanges.subscribe(data => {
console.log('Form changes', data)
this.output = data
})
}
}

查看 valueChanges在这个 小样: http://plnkr.co/edit/xOz5xaQyMlRzSrgtt7Wn?p=preview中的运行情况

为了完成更多以前的伟大答案,您需要知道表单利用可观察到的内容来检测和处理值更改。这是非常重要和强大的东西。Mark 和 dfsq 在他们的回答中都描述了这个方面。

可观测数据不仅允许使用 subscribe方法(类似于角度1中的 then方法)。如果需要为表单中的更新数据实现一些处理链,则可以进一步深入。

我的意思是,您可以在这个级别上用 debounceTime方法指定退出时间。这允许您在处理更改之前等待一段时间,并正确处理多个输入:

this.form.valueChanges
.debounceTime(500)
.subscribe(data => console.log('form changes', data));

还可以在更新值时直接插入要触发的处理(例如异步处理)。例如,如果您希望处理一个文本值来基于 AJAX 请求过滤列表,您可以利用 switchMap方法:

this.textValue.valueChanges
.debounceTime(500)
.switchMap(data => this.httpService.getListValues(data))
.subscribe(data => console.log('new list values', data));

您甚至可以更进一步,将返回的观察值直接链接到组件的属性:

this.list = this.textValue.valueChanges
.debounceTime(500)
.switchMap(data => this.httpService.getListValues(data))
.subscribe(data => console.log('new list values', data));

并使用 async管显示:

<ul>
<li *ngFor="#elt of (list | async)">\{\{elt.name}}</li>
</ul>

只是说,你需要思考的方式来处理形式不同的 Angular2(一个更强大的方式;)。

希望能帮到你, 蒂埃里

扩展马克的建议。

方法3

在模型上实施“深度”变化检测。优点主要包括避免将用户界面方面并入组件; 这也捕获了对模型的编程更改。也就是说,它需要额外的工作来实现 Thierry 所建议的反弹,而且这也会捕捉到 你自己的的程序变化,所以要谨慎使用。

export class App implements DoCheck {
person = { first: "Sally", last: "Jones" };
oldPerson = { ...this.person }; // ES6 shallow clone. Use lodash or something for deep cloning


ngDoCheck() {
// Simple shallow property comparison - use fancy recursive deep comparison for more complex needs
for (let prop in this.person) {
if (this.oldPerson[prop] !==  this.person[prop]) {
console.log(`person.${prop} changed: ${this.person[prop]}`);
this.oldPerson[prop] = this.person[prop];
}
}
}

去 Plunker 试试

我考虑了使用(ngModelChange)方法,然后考虑了 FormBuilder 方法,最后选定了方法3的一个变体。这样可以避免使用额外的属性来修饰模板,并自动获取对模型的更改——减少了使用方法1或2忘记某些内容的可能性。

简化方法3有点..。

oldPerson = JSON.parse(JSON.stringify(this.person));


ngDoCheck(): void {
if (JSON.stringify(this.person) !== JSON.stringify(this.oldPerson)) {
this.doSomething();
this.oldPerson = JSON.parse(JSON.stringify(this.person));
}
}

您可以添加一个超时来模拟在 x 个毫秒之后调用 doSomething ()。

oldPerson = JSON.parse(JSON.stringify(this.person));


ngDoCheck(): void {
if (JSON.stringify(this.person) !== JSON.stringify(this.oldPerson)) {
if (timeOut) clearTimeout(timeOut);
let timeOut = setTimeout(this.doSomething(), 2000);
this.oldPerson = JSON.parse(JSON.stringify(this.person));
}
}

对于角度 5+版本。放置版本有助于作为角度作出了很多变化。

ngOnInit() {


this.myForm = formBuilder.group({
firstName: 'Thomas',
lastName: 'Mann'
})
this.formControlValueChanged() // Note if you are doing an edit/fetching data from an observer this must be called only after your form is properly initialized otherwise you will get error.
}


formControlValueChanged(): void {
this.myForm.valueChanges.subscribe(value => {
console.log('value changed', value)
})
}