3
votes

I'm experimenting with creating a store for managing my data, and then consuming that store in my components. App is built with Ionic2 and Angular2.

UPDATE

Here's a link to how I modeled my data store https://coryrylan.com/blog/angular-2-observable-data-services

The trouble I'm having is updating objects in an observable.

Here's the Store

export class MessageService {
    messages$: Observable<Message[]>;
    update() {
        //...code to make http call and set `this.messages$`
    }
    setMessageFlag(msg: Message) {
        //post to server, nothing more, nothing returned
    }
}

Now my component injects this service

export class Inbox OnInit {
    messages$: Observable<Messages[]>

    constructor(private $messageService: MessageService) {
        this.messages$ = this.$messageService.messages$;
    }

    flagMessage(msg: Message) {
        msg.isNew = !msg.isNew;
        this.$messageService.setMessageFlag(msg); //makes a http call to set the message flag
    }

    ngOnInit() {
        this.$messageService.update();
    }
}

Then my template consumes the observable using the async pipe

<div *ngFor="let message of messages$ | async">
    <h2 [ngClass]="{'is-read': message.isNew}">{{message.title}}</h2>
    <button (click)="flag(message)">{{message.isNew ? 'Mark as read' : 'Mark as unread'}}</button>
</div>

Now for the part I'm confused with.

flagMessage(msg: Message) {
    msg.isNew = !msg.isNew;
    this.$messageService.setMessageFlag(msg);
}

When the button is clicked, and flagMessage(message) method is called, it sets the msg.isNew, and makes the http call to the server. I see the is-new class applied to the <h2>, but then it quickly reverts back to its original value.

I don't want to make a call to fetch all the messages from the server again. That seems inefficient. I'm thinking that the Observable is returning a copy, but I'm not sure.

It's been quite a learning curve for me lately. I've jumped onto Angular2, Typescript2, Rxjs, and Ionic2, all within the last month so any help is appreciated.

1
Without code of the update method it's hard to tell - Alexander Ciesielski
@AlexanderCiesielski I'll add the code and update. - Matt
@AlexanderCiesielski I added a link to the article I modeled my data service after. I'm at work, so everything I've put down is from memory. I'll update with my code once I get home. - Matt

1 Answers

0
votes

I think ideal for this situation is ReplaySubject that can remember the last value emited and reemit it to all Observers that subscribe after the value was first emited.

import {Observable, ReplaySubject} from 'rxjs';

var results = new ReplaySubject(1);

// Simulate HTTP request
Observable.create(subscriber => {
  console.log('Observable.create');
  var data = 'response data';
  results.next(data);
}).subscribe();

results.subscribe(r => console.log(r));
results.subscribe(r => console.log(r));
results.subscribe(r => console.log(r));

// prints to console:
// Observable.create
// response data
// response data
// response data

See live demo: http://plnkr.co/edit/DOPsPlMxh06g1lWoJllQ

Even though the three Observers subscribe after "response data" was first emitted using results.next(data) they all receive the same data.

As far as I understand your use case, you'll make messages$ an instance of ReplaySubject because that's where you subscribe your Observers. Then logic that calls the HTTP request and calls messages$.next(...) can be completely independent on it. All new subscribers to messages$ should therefore receive the last response from the server which is I think what you're going for (?).