Reading How to use RxJs distinctUntilChanged? and this, it seems that distinctUntilChanged
alters the output stream to only provide distinct contiguous values.
I take that to mean that if the same value arrives in immediate succession, you are essentially filtering the stream and only getting one occurrence of that repeated value.
So if I write this:
this.myFormControl.valueChanges
.debounceTime(1000)
.distinctUntilChanged()
.subscribe(newValue => {
console.log('debounced: ', newValue);
});
I see no difference in output to this:
this.myFormControl.valueChanges
.debounceTime(1000)
.subscribe(newValue => {
console.log('debounced: ', newValue);
});
I've seen a few places recommend using distinctUntilChanged
when subscribing to valueChanges
on form inputs — but don't really get why.
It's an input, so if the user is typing it is always changing, right? The values will always be distinct, so you're just adding an extra operation to filter input for no reason.
Or am I missing something here?
EDIT
Using distinctUntilChanged
as per my first code sample, I created a form input with the value Mr Trump
and ensured it was saved in the model.
I then clicked inside the control and pasted Mr Trump
. Since the value is the same, I would have expected to not see anything logged — the control has the same value it had before, so surely the distinctUntilChanged
should have ignored it?
EDIT 2
After further looking into my test, this behaviour appears to be because I used an array of AbstractControls
:
this.itemsControl = <FormArray>this.form.controls['items'];
...
this.itemsControl.controls[index].valueChanges...
So although a bit strange that it still fires when the value of the input is the same, I am guessing I need to hookup to valueChanges
of the actual input inside this array item (a form group), and not the array item itself.
EDIT 3
So after changing the code in EDIT 2 to the following, pasting the same value that already exists into input control does not fire valueChanges
(as expected). In EDIT 2 valueChanges
was hooked to the entire formGroup
, not the individual input control (in this case called content
):
let fg = this.itemsControl.controls[index]; // get the formGroup
fg['controls'].content.valueChanges
.debounceTime(1000)
.distinctUntilChanged()
.subscribe(newValue => {...});
The ValueChanges is an event raised by the Angular forms whenever the value of the FormControl, FormGroup or FormArray changes. It returns an observable so that you can subscribe to it. The observable gets the latest value of the control. It allows us to track changes made to the value in real-time and respond to it.
distinctUntilChanged emits all items emitted by the source observable that are distinct by comparison from the previous item. By default, it uses simple comparison operator that doesn't check any keys, so object references must match for an object to be considered the same.
distinctUntilChanged
when applied to the valueChanges
observable...
...is probably never going to work!
It only works as expected on an individual value (as you said). So you'll get a new value even if nothing has changed.
To track changes for the whole form you need to write a custom comparer, the simplest of which uses JSON.stringify
to output a value that can be compared. The valueChanges
observable emits an object and distinctUntilChanges
isn't smart enough to do anything beyond a reference compare (that's a link to the RxJS source code) unless you provide a comparer function.
this.form.valueChanges.pipe(distinctUntilChanged((a, b) => JSON.stringify(a) ===
JSON.stringify(b)))
.subscribe(changes => { console.log('The form changed!'); });
distinctUntilChanged
works fine for an individual value with a primitive type because ===
is sufficient to detect changes.
If you're trying to add distinctUntilChanges
into your pipe (for the whole form) to avoid an infinite loop when you programmatically update the form value - you probably want this instead:
this.form.patchValue(address || {}, { emitEvent: false });
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With