serlingpa serlingpa - 24 days ago 8
TypeScript Question

RxJS Subjects and Angular2 components

I am coming to terms with the fact that web development is changing and I need to get to grips with RxJS. I am currently developing a web app using Angular 2.0.0-beta.15.

I am following the most excellent ng-book 2 (is that kind of advertising allowed? Oh well). It covers in broad, shallow strokes some important concepts in RxJS and provides links to further reading. I have read and understood the source code and accompanying explanation, but am left a little in the dark about some details regarding

Subject
s in particular and how Angular 2 components consume these Subject streams.

I have augmented the code from the book thusly:

export class SubmitResourceComponent {
private _newResourceTags: Subject<Tag> = new Subject<Tag>();
private _resourceTags: Observable<Tag[]>;
private _tagUpdates: Subject<any> = new Subject<any>();
private _create: Subject<Tag> = new Subject<Tag>();
private _remove: Subject<Tag> = new Subject<Tag>();

constructor() {
this._resourceTags = this._tagUpdates
.scan((tags: Tag[], operation: ITagsOperation) => operation(tags), initialTags);

this._create
.map((tag: Tag): ITagsOperation => (tags: Tag[]) => _.uniq(tags.concat(tag)))
.subscribe(this._tagUpdates);

this._remove
.map((tag: Tag): ITagsOperation => (tags: Tag[]) => _.without(tags, tag))
.subscribe(this._tagUpdates);

this._newResourceTags.subscribe(this._create);
}

get resourceTags(): Observable<Tag[]> {
return this._resourceTags;
}

protected addTagToResource(tag: Tag): void {
this._create.next(tag);
}

protected removeTagFromResource(tag: Tag): void {
this._remove.next(tag);
}
}


And I am consuming the
_resourceTags
like this:

<button class="btn" *ngFor="#tag of resourceTags | async" (click)="removeTagFromResource(tag)">{{ tag.name }}</button>


What I can't grasp, even with excellent support from the gitter forums, is why the UI displays all of the tags that are pushed into the
_resourceTags
Subject
. I would imagine that the stream is like a rubber pipe: once an element has been pushed into the
Subject
and published to whatever
Observer
(in this case, the UI element), does it not then evaporate and disappear? Does it stay in the stream/pipe/
Subject
? How is the UI element subscribing to the
Subject
? Am I thinking about it in the wrong way? Do I need a complete mental restructuring/transplant?

So many questions!

Answer

Data passed to next is like an event. The subscriber gets the value and the Observable created by the Subject doesn't hold any reference to it (except you use special operators with the purpose to buffer or by other means keep emitted values.

With | async Angular subscribes to the Observable (Subject) and when it receives a value it displays it.

*ngFor only renders arrays. If you want use *ngFor with Subject, the subject needs to emit arrays - each event is not a single value but an array of values and these values are rendered by *ngFor and replaced by other values when the Observable emits a new array.

scan operator

Your code example uses the scan operator with the purpose to accumulate emitted values and forward an array that contains all values emitted so far each time it receives a new event like required by *ngFor.