如何使用Angular 2检查表单中的更改

时间:2016-12-05 20:31:16

标签: angular typescript

我有一个包含少量数据字段和两个按钮的表单。我想仅在用户对表单进行一些更改时才启用按钮。我尝试过使用:

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>
                </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>
            </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();
    }
}

10 个答案:

答案 0 :(得分:18)

您可以使用.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

  

dirty:boolean如果用户更改了值,则控件是脏的   在用户界面。

     

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

     

触摸:布尔值一旦用户拥有,就会标记触摸控件   触发了模糊事件。

答案 1 :(得分:11)

.dirty和.pristine布尔值的问题在于,一旦他们改变,他们就不会回头,即使你撤消了你引入的所有改变。我设法通过创建一个监视整个表单中的更改的类来找到解决此问题的方法,并将使用原始表单值检查更改的值。这样,如果撤消用户更改,表单可以返回到pristine,或者可选地在您可以提供和订阅的observable(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 -->

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

{field1:34,field2:“some text field”}

但是,如果用户将field1更新为其他值并返回34,则新的stringify将为:

{field:“34”,field2:“some text field”}

正如您所看到的,虽然表单没有真正改变,但由于34号附近的引号,原始值和新值之间的字符串比较将导致错误。

答案 2 :(得分:6)

首先使用“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”,并亲眼看看所有选项的可用性。

答案 3 :(得分:2)

尝试以下操作以查看表单是否已更改:

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

答案 4 :(得分:2)

我设法通过修改变量来解决这个问题:

<button ion-button icon-only clear type="submit" [disabled]="!modified || !editForm.valid">
    <ion-icon name="checkmark"></ion-icon>
</button>

然后在输入上设置ionChange事件的修改变量:

<ion-input type="text" (ionChange)="modified=true"></ion-input> 

答案 5 :(得分:1)

我猜你可以忽略第一次改变

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

提示:导入skip运算符

答案 6 :(得分:1)

您可以传递{ emitEvent: false }作为以下反应式方法的选项,以防止它们触发valueChanges事件

this.form.patchValue(value, { emitEvent: false })
this.form.setValue(value, { emitEvent: false })
this.form.controls.email.updateValueAndValidity({ emitEvent: false })
this.form.disable({ emitEvent: false })

是,禁用会触发valueChanges事件

PS:this.form上方是反应形式

阅读这篇出色的文章,它将回答您所有的问题,甚至可以对反应式提供一些深刻的见解:

https://netbasal.com/angular-reactive-forms-tips-and-tricks-bb0c85400b58

答案 7 :(得分:0)

你可以像这样检查特定形式控件的变化:

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

                );

答案 8 :(得分:0)

我在代码中使用了一些技巧,我认为这不是最好的解决方案,但是某种程度上对我有用。

  

profile.component.ts:

tempForm: any
ngOnInit() {
  this.loadForm();
  this.tempForm = this.form.value
}

save(): void {
  if (this.tempForm === this.form.value) {
    // Do Save
  } else {
    // Value is Same as initial
  }
}

希望这可以解决您的问题,或者只是提供一些启发。

答案 9 :(得分:0)

提交时,您可以将对象与表单结果进行比较

let changes = false;
for ( let [ key, value ] of Object.entries( this.form.value ) ) {
    const form = this.form.value;
    const record = this.record;
    if ( form[ key ] != record[ key ] ) {
        changes = true;
        break;
    }
}
if ( !changes ) {
    // No changes
} else {
    this.record = this.form.value;
    this.UpdateRecord();
}