2016-12-05 104 views
7

检查Angular 2中的表单中的更改我有一个包含少量数据字段和两个按钮的表单。我只想在用户对表单进行一些更改时才启用按钮。我曾尝试过使用:如何使用

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

但是,当表单加载时也会检测到更改。是否有任何其他方式来检查表单中的任何更改。我希望仅在用户对字段进行更改时才调用它,而不是在表单加载时调用。以下是我的html和打字稿代码:

profile.html:

<section> 
    <div> 
     <form [formGroup]="form"> 
      <fieldset> 
       <div class="panel-group m-l-1 m-r-1 accordion vertical-scroll" id=""> 
            <div class="form-group required no-gutter"> 
             <label for="firstname" > First Name:</label> 
             <div class="col-md-7 col-lg-6"> 
              <input type="text" class="form-control" id="firstname" placeholder="" name="firstname" title="firstname" 
               formControlName="firstname" size="128" aria-required="true" maxlength="35"> 
             </div> 
            </div> 

      </fieldset> 
      <div> 
        <button class="btn btn-primary" type="button" (click)="save()" >Save</button> 
        <button class="btn btn-primary" type="button" (click)="cancel()">Cancel</button> 
       </span> 
      </div> 
     </form> 
    </div> 
</section> 

profile.component.ts:

export class ProfileComponent implements OnInit, AfterViewInit, OnChanges { 
    public form: FormGroup; 

    constructor(private formBuilder: FormBuilder, private app: Application) { 

    } 

    loadForm(): void { 
     this.form = this.formBuilder.group({    
      firstname: [this.app.firstName, Validators.required]    
     }); 
     this.form.valueChanges.subscribe(data => console.log('form changes', data)); 

    } 

    save(): void { 

    } 

    cancel(): void { 

    }; 

    ngOnInit() { 
     this.loadForm(); 
    } 

    ngAfterViewInit() { 
     this.loadForm(); 
    } 


} 
+0

可能的重复[如何观察Angular 2中的表单变化?](http://stackoverflow.com/questions/34615425/how-to-watch-for-form-changes-in-angular-2) – blo0p3r

回答

0

我想你可以不理会第一个变化

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

提示:输入skip运行ATOR

+0

是的,我们可以使用跳过运算符,但是当我形成加载时,它会调用事件4次。因此,对于每个表格,我应该知道事件被调用的次数,并在跳过时使用该数字。 – Valla

+0

这很麻烦,但我不知道更好的方法。 –

+0

检查肮脏和感动会更好,国际海事组织。这有一个忽略程序驱动的对表单的更改的好处。 – silentsod

7

可以使用.dirty(或.pristine)值,以确定是否用户已经使用的UI改变控制值:

<button class="btn btn-primary" type="button" (click)="save()" [disabled]="!form.dirty" >Save</button> 
<button class="btn btn-primary" type="button" [disabled]="!form.dirty"(click)="cancel()">Cancel</button> 

https://angular.io/docs/ts/latest/api/forms/index/AbstractControl-class.html#!#dirty-anchor

脏:Boolean一个控制是如果用户在UI中更改了值 ,则脏。

请注意,对控件值的编程更改不会标记为脏 。

touched:boolean一旦用户有 触发模糊事件,控件被标记为触摸。

0

尝试以下,看看形式发生了变化:

ngOnChanges() { 
    if (!!this.form && this.form.dirty) { 
     console.log("The form is dirty!"); 
    } 
    else { 
     console.log("No changes yet!"); 
    }  
} 
0

您可以检查这样的变化perticular formcontrol:

this.profileForm.controls['phone'].valueChanges.subscribe(
       data => console.log('form changes', data) 

       ); 
5

与.dirty的问题和.pristine booleans,就是一旦他们改变了,即使你撤销了你所介绍的所有改变,他们也不会回头。我设法找到解决这个问题的方法,创建一个监视整个表单变化的类,并用原始表单值检查更改后的值。这样,如果用户更改被撤销,表单可以返回到原始状态,或者可选地在您可以提供和订阅的可观察对象(ReplaySubject)上发布布尔值。

的使用将是这样的:

private _formIntactChecker:FormIntactChecker; 

constructor(private _fb: FormBuilder) { 

    this._form = _fb.group({ 
     ... 
    }); 

    // from now on, you can trust the .dirty and .pristine to reset 
    // if the user undoes his changes. 
    this._formIntactChecker = new FormIntactChecker(this._form); 

} 

可替换地,代替复位.pristine/.dirty布尔值的类可以被配置成发射一个布尔每当从完整形式变成了已修改和反之亦然。一个真正的布尔表示,表单回到完好无损,而假布尔意味着表单不再完整。

这里有一个关于你将如何使用它的一个例子:

private _formIntactChecker:FormIntactChecker; 

constructor(private _fb: FormBuilder) { 

    this._form = _fb.group({ 
     ... 
    }); 

    var rs = new ReplaySubject() 

    rs.subscribe((isIntact: boolean) => { 
     if (isIntact) { 
      // do something if form went back to intact 
     } else { 
      // do something if form went dirty 
     } 
    }) 

    // When using the class with a ReplaySubject, the .pristine/.dirty 
    // will not change their behaviour, even if the user undoes his changes, 
    // but we can do whatever we want in the subject's subscription. 
    this._formChecker = new FormIntactChecker(this._form, rs); 

} 

最后,做类中的所有工作:

import { FormGroup } from '@angular/forms'; 
import { ReplaySubject } from 'rxjs'; 

export class FormIntactChecker { 

    private _originalValue:any; 
    private _lastNotify:boolean; 

    constructor(private _form: FormGroup, private _replaySubject?:ReplaySubject<boolean>) { 

     // When the form loads, changes are made for each control separately 
     // and it is hard to determine when it has actually finished initializing, 
     // To solve it, we keep updating the original value, until the form goes 
     // dirty. When it does, we no longer update the original value. 

     this._form.statusChanges.subscribe(change => { 
      if(!this._form.dirty) { 
       this._originalValue = JSON.stringify(this._form.value); 
      } 
     }) 

     // Every time the form changes, we compare it with the original value. 
     // If it is different, we emit a value to the Subject (if one was provided) 
     // If it is the same, we emit a value to the Subject (if one was provided), or 
     // we mark the form as pristine again. 

     this._form.valueChanges.subscribe(changedValue => { 

      if(this._form.dirty) { 
       var current_value = JSON.stringify(this._form.value); 

       if (this._originalValue != current_value) { 
        if(this._replaySubject && (this._lastNotify == null || this._lastNotify == true)) { 
         this._replaySubject.next(false); 
         this._lastNotify = false; 
        } 
       } else { 
        if(this._replaySubject) 
         this._replaySubject.next(true); 
        else 
         this._form.markAsPristine(); 

        this._lastNotify = true; 
       } 
      } 
     }) 
    } 

    // This method can be call to make the current values of the 
    // form, the new "orginal" values. This method is useful when 
    // you save the contents of the form but keep it on screen. From 
    // now on, the new values are to be considered the original values 
    markIntact() { 
     this._originalValue = JSON.stringify(this._form.value); 

     if(this._replaySubject) 
      this._replaySubject.next(true); 
     else 
      this._form.markAsPristine(); 

     this._lastNotify = true; 
    } 
} 

重要提示:小心与初始值

该类使用JSON.stringify()快速比较整个formGroup值对象。但是,初始化控制值时要小心。

例如,对于复选框,您必须将绑定它的值设置为布尔值。如果您使用其他类型,如“已选中”,“0”,“1”等,则比较将无法正常工作。

<input type="checkbox" ... [(ngModel)]="variable"> <!-- variable must be a boolean --> 

同去<select>,你必须将其值绑定到一个字符串,而不是一个数字:

<select ... [(ngModel)]="variable"> <!-- variable must be a string --> 

对于普通的文本输入控件,也可以使用一个字符串:

<input type="text" ... [(ngModel)]="variable"> <!-- variable must be a string --> 

这是一个例子,为什么否则它不会工作。假设你有一个文本字段,并用一个整数初始化它。原始值的字符串化将是这样的:

{字段1:34,场2:“一些文本字段”}

但是,如果用户更新FIELD1为不同的值,并可以追溯到34,新的字符串化将是:

{场:“34”,场2:“一些文本字段”}

正如你所看到的,虽然形式并没有真正改变,原来和之间的字符串比较新的价值将导致错误的,由于引号数34左右。

+1

我最近读到,你不应该使用'EventEmitter'来代替'@Output()'。我更新了代码,并用'ReplaySubject'替换了'EventEmitter'。 – kontiki

+0

我希望它具有内置的这个功能: - /尤其是'stringify'问题 –

+0

您可以为JSON.stringify设置一个'replacer'函数,它可以用来强制数字变成字符串。这应该使比较更可靠:'JSON.stringify(model,function(i,val){if(typeof(val)!=='object')return val.toString(); else return val;}) ' –

0

首先使用“NgForm”。
<form #myForm="ngForm" (ngSubmit)="onSubmit(myForm)">
然后在 “的onsubmit()” 函数做到这一点 -

onSubmit(myForm: NgForm): void { 
    let formControls = myForm.controls; 
    if(formControls.firstName.dirty) { 
    console.log("It's dirty"); 
    } 
    else { 
    console.log("not dirty"); 
    } 
} 

它肯定会工作。您可以打印整个“myForm”并自己查看所有可用选项。