首页 文章

如何在AngularJS中观察表单更改?

提问于
浏览
137

在AngularJS中,我可能有一个看起来像这样的表单:

<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);

}

这是AngularJS example on JSFiddle .

我无法弄清楚如何在Angular中完成同样的事情 . 显然,我们不再有 $scope 等......但是肯定有一种方法可以实现同样的目的吗?

这是Angular example on Plunker .

6 回答

  • 1

    UPD . 更新答案和演示以与最新的Angular保持一致 .


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

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

    在这种情况下,您需要使用FormBuilder手动构造表单 . 像这样的东西:

    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
        })
      }
    }
    

    查看 demo 中的 valueChangeshttp://plnkr.co/edit/xOz5xaQyMlRzSrgtt7Wn?p=preview

  • 107

    如果您使用 FormBuilder ,请参阅@ dfsq的答案 .

    如果您未使用 FormBuilder ,则有两种方法可以通知更改 .

    Method 1

    正如对该问题的评论中所讨论的,在每个输入元素上使用event binding . 添加到您的模板:

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

    然后在你的组件中:

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

    Forms页面提供了一些与此相关的ngModel的其他信息:

    ngModelChange不是<input>元素事件 . 它实际上是NgModel指令的事件属性 . 当Angular以[(x)]形式看到绑定目标时,它期望x指令具有x输入属性和xChange输出属性 . 另一个奇怪的是模板表达式,model.name = $ event . 我们习惯于看到来自DOM事件的$ event对象 . ngModelChange属性不会生成DOM事件;它是一个Angular EventEmitter属性,在它触发时返回输入框值 . 我们几乎总是喜欢[(ngModel)] . 如果我们必须在事件处理中执行一些特殊操作(例如去抖动或限制击键),我们可能会拆分绑定 .

    在你的情况下,我想你想做一些特别的事情 .

    Method 2

    定义本地模板变量并将其设置为 ngForm .
    在输入元素上使用ngControl .
    获取对表单's NgForm directive using @ViewChild, then subscribe to the 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的更多信息,请参见Savkin's video .

    另请参阅@ Thierry的答案,了解有关使用 valueChanges observable可以执行的操作的更多信息(例如在处理更改之前稍微进行去抖/等待) .

  • 0

    要完成更多以前的优秀答案,您需要注意表单利用observable来检测和处理值更改 . 这是非常重要和强大的东西 . Mark和dfsq都在他们的答案中描述了这个方面 .

    Observable不仅允许使用 subscribe 方法(类似于Angular 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));
    

    您甚至可以将返回的observable直接链接到组件的属性:

    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中以不同方式处理表单的方式(一种更强大的方式;-)) .

    希望它对你有帮助,蒂埃里

  • 59

    扩展Mark的建议......

    Method 3

    在模型上实施"deep"更改检测 . 优点主要涉及避免将用户界面方面结合到组件中;这也捕获了对模型进行的程序化更改 . 也就是说,如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];
          }
        }
      }
    

    Try in Plunker

  • 0

    我考虑使用(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));
        }
    }
    
  • 174

    对于角 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)
            })
    }
    

相关问题