Создавая форму с контролем, значение относится к другим значениям управления, я заметил, что это довольно легко использовать комбинация Оператор от RXJS. Я слушаю только для данного контроля по имена. Кроме того, я могу установить исходные значения для каждого из них.
Причина
Я работал над угловым пользовательским контролем, который содержал другие элементы управления. Особенность, которую мне нужно было рассчитать значение для одного из элементов управления на основе других элементов управления. Вот как я это решил.
Установить потоки
Я хочу рассчитать значение только тогда, когда были изменены определенные элементы управления, поэтому я устанавливаю массив с именем элементов управления и запуска.
const nameWithStarters = [
{ name: 'quantityKR', value: 0 },
{ name: 'quantity', value: 0 },
{ name: 'priceKR', value: 0 },
{ name: 'hbtPercentage', value: 100 },
];
const valueChangers$ = nameWithStarters.map(({ name, value }) =>
this.form.get(name).valueChanges.pipe(startWith(value))
);
И я слушаю изменения, используя Имя Для управления выбором и значением для начального значения. начало Оператор От RXJS гарантирует, что каждый из моих контролей будет иметь значение по подписке.
Расчет
Чтобы вызвать расчет, я использую комбинация функция . Он излучает, когда любой из данных потоков испускает и пропускает значения для моего простого Калькулятетоталкр функция. В конце концов, он устанавливает значение для моего контроля вашего результата. Я добавляю его к моему Подписка Используя Добавить Способ иметь возможность отписаться, когда компонент разрушен (избегая утечек памяти).
const sub = combineLatest(valueChangers$)
.pipe(map((values: ValuesTuple) => calculateTotalKr(values)))
.subscribe(value => {
this.form.get('totalKR').setValue(value);
});
this.subscription.add(sub);
Полный код
import {
Component,
EventEmitter,
Input,
OnDestroy,
OnInit,
Output,
} from '@angular/core';
import {
ControlValueAccessor,
FormBuilder,
NG_VALUE_ACCESSOR,
} from '@angular/forms';
import { map, startWith } from 'rxjs/operators';
import { Benefit } from 'src/app/models/benefit';
import { combineLatest, Subscription } from 'rxjs';
type ValuesTuple = [number, number, number, number];
@Component({
selector: '[app-lines-table-row]',
templateUrl: './lines-table-row.component.html',
styleUrls: ['./lines-table-row.component.scss'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
multi: true,
useExisting: LinesTableRowComponent,
},
],
})
export class LinesTableRowComponent
implements ControlValueAccessor, OnInit, OnDestroy {
@Input() benefitList: Benefit[] = [];
@Output() benefitRemove = new EventEmitter();
form = this.formBuilder.group({
date: [null],
type: [null],
performance: [null],
performanceName: [null],
quantity: [null],
quantityKR: [null],
priceKR: [null],
hbtPercentage: [100],
totalKR: [0],
included: [null],
});
private subscription = new Subscription();
onChange = (value: any) => {};
onTouched = () => {};
constructor(private readonly formBuilder: FormBuilder) {}
ngOnInit(): void {
this.form.get('performance').valueChanges.subscribe(value => {
this.selectBenefit(value);
});
const sub = this.form.valueChanges.subscribe(value => this.onChange(value));
this.subscription.add(sub);
this.setCalculateTotalKRValue();
}
ngOnDestroy(): void {
this.subscription.unsubscribe();
}
writeValue(value: any): void {
this.form.patchValue(value || null);
}
registerOnChange(onChange: any) {
this.onChange = onChange;
}
registerOnTouched(onTouched: any) {
this.onTouched = onTouched;
}
onBenefitRemove(): void {
this.benefitRemove.emit();
}
private selectBenefit(benefitValue: string): void {
const selectedBenefit = this.benefitList.find(
({ value }) => value === benefitValue
);
this.form.patchValue({
type: selectedBenefit.extraField === 'OrdinaryBenefit' ? 'AHT' : 'UHT',
performanceName: selectedBenefit.text,
});
}
private setCalculateTotalKRValue(): void {
const nameWithStarters = [
{ name: 'quantityKR', value: 0 },
{ name: 'quantity', value: 0 },
{ name: 'priceKR', value: 0 },
{ name: 'hbtPercentage', value: 100 },
];
const valueChangers$ = nameWithStarters.map(({ name, value }) =>
this.form.get(name).valueChanges.pipe(startWith(value))
);
const sub = combineLatest(valueChangers$)
.pipe(map((values: ValuesTuple) => calculateTotalKr(values)))
.subscribe(value => {
this.form.get('totalKR').setValue(value);
});
this.subscription.add(sub);
}
}
function calculateTotalKr([
quantityKR,
quantity,
priceKR,
hbtPercentage,
]: ValuesTuple): number {
return (quantityKR * quantity - priceKR) * (hbtPercentage / 100);
}
Оригинал: “https://dev.to/tomwebwalker/set-value-of-one-control-by-other-controls-in-angular-reactiveforms-18d1”