Bas van Dijk
Bas van Dijk

Reputation: 10713

Why does the next() method on an RxJS subject stop notifying the frontend

I stumbled on this Plunkr http://plnkr.co/edit/yMBoVkxohwhPig5COgkU?p=preview

It shows an observable data service. The part where my question is about shows:

export class TodoService {
  private _todos$: Subject<Todo[]>; 
  private baseUrl: string;
  private dataStore: {
    todos: Todo[]
  };

  constructor(private http: Http) {
    this.baseUrl  = 'http://56e05c3213da80110013eba3.mockapi.io/api';
    this.dataStore = { todos: [] };
    this._todos$ = <Subject<Todo[]>>new Subject();
  }

  get todos$() {
    return this._todos$.asObservable();
  }

  loadAll() {
    this.http.get(`${this.baseUrl}/todos`).map(response => response.json()).subscribe(data => {
      this.dataStore.todos = data;
      this._todos$.next(this.dataStore.todos);
    }, error => console.log('Could not load todos.'));
  }

The code above runs fine. However when I replace the loadAll with code which doesn't make an http call:

  loadAll() {
    this.dataStore.todos = [{ 'id': '1', 'createdOn': 1472831416, 'value': 'value 1' }, { 'id': '2', 'createdOn': 1472831360, 'value': 'value 2' }, { 'id': '4', 'createdOn': 1472831771, 'value': 'value 4' }, { 'id': '5', 'createdOn': 1472831716, 'value': 'value 5' }, { 'id': '6', 'createdOn': 1472831658, 'value': 'value 6' }];
    this._todos$.next(this.dataStore.todos);
  }

It stops notifying the frontend as shown in this Plunkr: http://plnkr.co/edit/ObiWt2cm6sGSgW2Hf5ya?p=preview

Could someone explain why this is and how I can make it work?

Upvotes: 3

Views: 4203

Answers (1)

Nypan
Nypan

Reputation: 7246

The Subject expects one item to be passed to it with on next, not an array of items. What you are doing is similar to:

let subject = new Subject<{ message: string }>();
subject.onNext([{ message: 'test one' }, { message: 'test two' }]);

This is an error and should generate an error on compilation.

As I mentioned onNext expects one item of its generic type, not an array of items. So something like this would be correct:

let subject = new Subject<{ message: string }>();
subject.onNext({ message: 'test one' });

If you want to create an observable from the items in an array you have a few options, the first one is to loop over the array and call onNext for each item in the array, like so:

let subject = new Subject<{ message: string }>();
let items = [{ message: 'test one' }, { message: 'test two'}];
items.forEach(item => subject.onNext(item));

Another way would be to create an observable from the array:

let subject = new Subject<{ message: string }>();
let items = [{ message: 'test one' }, { message: 'test two'}];

let itemsObservable = Observable.from(items);

In this example itemObservable would replace your:

get todos$() {
    return this._todos$.asObservable();
}

and become something like:

get todos$() {
    return this.itemObservable;
}

hope this clears some things up. The basic problem in your solution is basically that you are misunderstanding how a Subject works.

Upvotes: 2

Related Questions