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