Вы получаете эти результаты, потому что вы сравниваете 2 разные ссылки.
Вы можете визуализировать дерево элементов управления следующим образом:
// FG - FormGroup
// FC - FormControl
FG
/ \
FC FG
/ \
FC FC <-- <select formControlName="yourSelect">
Всякий раз, когда вызывается FormControl.setValue
(например, из-за изменений, исходящих от пользователя), все его предки должны быть обновлены. Под updated
я подразумеваю обновление статуса взаимодействия с пользователем (touched
, pristine
et c), вызова валидаторов и обновления статуса достоверности .
За это отвечает метод updateValueAndValidity
. Он вызывается FormControl.setValue
:
this._setInitialStatus();
this._updateValue();
if (this.enabled) {
this._cancelExistingSubscription();
(this as { errors: ValidationErrors | null }).errors = this._runValidator();
(this as { status: string }).status = this._calculateStatus();
if (this.status === VALID || this.status === PENDING) {
this._runAsyncValidator(opts.emitEvent);
}
}
if (opts.emitEvent !== false) {
(this.valueChanges as EventEmitter<any>).emit(this.value);
(this.statusChanges as EventEmitter<string>).emit(this.status);
}
if (this._parent && !opts.onlySelf) {
this._parent.updateValueAndValidity(opts);
}
Обратите внимание, что этот метод одинаков для: FormControl
, FormArray
и FormGroup
.
Как видите, это также место, где this.valueChanges
излучает. Это означает, что узел root (например, ваш formGroup
в примере) будет в конечном итоге достигнут, и вы получите накопленные значения от всех других потомков ( элементы управления формой *) 1037 *).
Интересующий вас объект присваивается this.value
, который разрешается в this._updateValue()
, то есть entity-speci c. Для FormGroup
это то, как определяется значение:
_updateValue(): void {
(this as {value: any}).value = this._reduceValue();
}
_reduceValue() {
return this._reduceChildren(
{}, (acc: { [k: string]: AbstractControl }, control: AbstractControl, name: string) => {
if (control.enabled || this.disabled) {
acc[name] = control.value;
}
return acc;
});
}
Из приведенного выше вы можете заметить {}
, который является новой ссылкой по сравнению с предыдущим объектом значения, предоставленным pairwise()
. Таким образом, это должно объяснить, почему вы всегда получаете этот результат.
Один из способов решить эту проблему - выполнить глубокое сравнение между предыдущим и текущим значениями объекта.
Другой способ будет чтобы предотвратить обновление дерева, если не введено новое значение. Это включает работу на уровне leaf
(например, FormControl
с). Вы можете создать пользовательский элемент доступа к значению элемента управления и сделать там логи c.
Однако я не думаю, что select
будет генерировать событие change
, если выбрано то же значение. Демонстрация .
Если вы хотите изучить Angular Формы, я бы рекомендовал проверить эту статью .