Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RxJS Subjects and Angular 2 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 Subjects 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!

like image 689
serlingpa Avatar asked Apr 22 '16 16:04

serlingpa


2 Answers

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.

like image 93
Günter Zöchbauer Avatar answered Sep 23 '22 20:09

Günter Zöchbauer


I think observables store values of a variable in the browser memory only until the session is cleared, just like how you would set up a variable to get a value from an input box and upon hitting refresh on the browser the values is emptied. In the above scenario i would think like most SPAs the browser is prevented from default behavior of refresh/reload and you get some sort of persistence so when you do an operation like

...  _.uniq(tags.concat(tag))).subscribe(this._tagUpdates);...

the values continue to pile up as they're concated by the create event but would disappear when a manual refresh on the page occurs and clear the session

EDIT

Maybe i got the question wrong but what i mean is this: _resourceTags starts empty, a value is created by the create event, now _resourceTags is [newValue#1] angular detects change and rerenders _resourceTags using ngFor, after a new create event _resourceTags is now [newValue#1, newValue#2] angular rerenders showing both now. If the stream is not being persisted to an online database like firebase or offline one like websql it would mean upon reloading the browser _resourceTags will go back to being empty

like image 45
Michael Gikaru Avatar answered Sep 25 '22 20:09

Michael Gikaru