Reputation: 696
I'm working with Typescript 3.4.5 and Angular 8.
Consider the following interface:
// This is an interface which represents a piece of data, which will be partially updated
export interface TextBrick {
kind: 'text';
content: string;
}
export class TestService {
test$ = new BehaviorSubject<TextBrick>({kind: 'text', content: 'initial'});
get test(): TextBrick {
return this.test$.value;
}
set test(v: TextBrick) {
console.log('set', v);
this.test$.next(v);
}
}
The idea is to subscribe to the test$
BehaviorSubject to watch the test.content
change.
Consider now the following test:
test('test', () => {
const instance = new TestService();
// Watch subscription
instance.test$.subscribe((v) => console.log('sub', v));
// 1) Full replace, subscription triggered
instance.test = {kind: 'text', content: 'fullreplace'};
// 2) Partial replace, subscription not triggered
instance.test.content = 'aa';
// But the value of the BehaviorSubject was updated! WTF?!
console.log('end', instance.test);
});
The console output is the following:
sub { kind: 'text', content: 'intitial' } // Expected behavior
set { kind: 'text', content: 'fullreplace' } // Expected behavior
sub { kind: 'text', content: 'fullreplace' } // Expected behavior
end { kind: 'text', content: 'aa' } // Sounds really weird!
There is obviously a problem with "partial setter" (I don't know how to name that), when I set the instance.test.content
. I carefully read the Typescript documentation on setters, but this use case is not mentioned.
My first hypothesis was that the set test()
was not called, which make sense because when I add a console.log in the setter, I don't see the "partial" set of 'aa'
. But how can be the value of the behavior subject updated without triggering my subscription callback?
Any help or ressource will be greatly appreciated!
Upvotes: 0
Views: 1851
Reputation: 21638
instance.test = {kind: 'text', content: 'fullreplace'};
This line calls the setter function
instance.test.content = 'aa';
This line calls the getter function and then mutates the contents of the behavior subject, you should not mutate the contents of the behavior subject.
Get the value and then update the value with a new object, we don't mutate objects in the world of reactive programming.
const value = instance.test;
instance.test = { ...value, content: 'aa' };
Upvotes: 4