Как проверить изменения формы в 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>
                </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();
    }
}

Ответы

Ответ 1

Вы можете использовать значения .dirty (или .pristine), чтобы определить, использовал ли пользователь пользовательский интерфейс для изменения контрольного значения:

<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 Элемент управления загрязнен, если пользователь изменил значение в пользовательском интерфейсе.

Обратите внимание, что программные изменения контрольного значения не будут отмечать его загрязнение.

touched: boolean Элемент управления отмечен касанием после того, как пользователь вызвал на нем событие размытия.

Ответ 2

Проблема с.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 booleans, класс может быть настроен на испускание логического значения, когда форма изменяется от целостного до измененного и наоборот. Истинное логическое означает, что форма вернулась к целостности, а ложное логическое означает, что форма больше не целенаправлена.

Вот пример того, как вы его используете:

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 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() чтобы быстро сравнить весь объект value groupGroup. Однако будьте осторожны при инициализации контрольных значений.

Например, для флажков вы должны установить значение, связывающее его с логическим. Если вы используете другие типы, такие как "checked", "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: "некоторое текстовое поле"}

Однако, если пользователь обновляет поле1 до другого значения и возвращается к 34, новая строка будет выглядеть так:

{поле: "34", поле2: "некоторое текстовое поле"}

Как вы можете видеть, хотя форма не изменилась, сравнение строк между оригиналом и новым значением приведет к ложному из-за котировок вокруг номера 34.

Ответ 3

в первую очередь используйте "NgForm".
<form #myForm="ngForm" (ngSubmit)="onSubmit(myForm)">
Затем в функции onSubmit() делаем это -

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

Это определенно сработает. Вы можете распечатать весь "myForm" и убедиться, что все опции доступны для использования.

Ответ 4

Попробуйте следующее, чтобы увидеть, изменилась ли форма:

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

Ответ 5

Мне удается работать с этим путем изменения переменной:

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

Ответ 6

Я думаю, вы можете просто игнорировать первое изменение

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

Подсказка: импортируйте оператор skip

Ответ 7

вы можете проверить наличие изменений в структуре perticular formcontrol следующим образом:

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

                );

Ответ 8

Я использую некоторые хитрости для своего кода, я думаю, что это не лучшее решение, но каким-то образом оно работает для меня.

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

Вы можете сравнить свой объект с результатом формы при отправке

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