🔥 Reactive Programming (RxJS 5 Observable) and Redux (Store, Saga) in Angular2-Meteor

UPDATED: I created Google Slides using chart to help you understand how ngrx works in an Angular2-Meteor app, check Desugar Meteor, Angular 2, RxJS 5, and ngrx . So check this slides to have a better understanding.


This “gist” will give you a guide using RxJS 5 (Observable), ngrx/store (Redux Store) and ngrx/effects (Redux Saga) in an Angular2-Meteor project.

The tutorial of Reactive Programming and Redux in Angular2-Meteor with explanation needs a while… Don’t wait, start to learn them and use today!

The most important part of codes that needs to connect Angular 2 and Meteor to use these, I already posted below. This helps you start. Have fun!

If you follow this “gist”, you can have some dev tools to help you development, check Tools for Reactive Programming and ngrx (Redux) in Angular2-Meteor .

1. Meteor Subscribe

1) Service
// chat.service.ts

  loadMessages(chatId: string): Observable<Message[]> {
    return Observable.create(observer => {
      this.subscribe('messages.private', chatId, () => {
        this.autorun(() => {
          const messages = Messages.find({ chatId }).fetch();   // If you want to return Mongo.Cursor, it is also good
          observer.next(messages);
        }, true);
      });
    });
  }

2) Actions

export class Actions {
  static CHAT_SUBSCRIBE_MESSAGES = '[Chat] Subscribe Messages';
  static CHAT_LOAD_MESSAGES = '[Chat] Load Messages';
}

3) Dispatch

 this.store.dispatch({ type: Actions.CHAT_SUBSCRIBE_MESSAGES, payload: chatId });

4) Effects
// chat.effects.ts

  @Effect() loadMessages$ = this.updates$
    .whenAction(Actions.CHAT_SUBSCRIBE_MESSAGES)
    .map<string>(toPayload)
    .switchMap(chatId => this.chatService.loadMessages(chatId))
    .map(messages => ({ type: Actions.CHAT_LOAD_MESSAGES, payload: messages }));

5) Reducer
// chat.reducer.ts

export const chatReducer: ActionReducer<ChatState> = (state = initialState, action: Action) => {
  switch (action.type) {
    case Actions.CHAT_LOAD_MESSAGES: {
      return Object.assign({}, state, { messages: action.payload });
    }
    // ... other chat reducers
    default: {
      return state;
    }
  }
};

2. Meteor Call

1) Service
// settings.service.ts

  updateEmail(email: string): Observable<string> {
    return Observable.create(observer => {
      this.call('users.updateEmail', email, error => {
        if (error) {
          observer.error(error);
          return;
        }

        observer.next(email);
      });
    });
  }

2) Actions

export class Actions {
  static SETTINGS_UPDATE_EMAIL = '[Settings] Update Email';
  static SETTINGS_UPDATE_EMAIL_SUCCESS = '[Settings] Update Email Success';
  static SETTINGS_UPDATE_EMAIL_FAIL = '[Settings] Update Email Fail';
}

3) Dispatch

this.store.dispatch({ type: Actions.SETTINGS_UPDATE_EMAIL, payload: this.email.value });

4) Effects
// settings.effects.ts

  @Effect() udpateEmail$ = this.updates$
    .whenAction(Actions.SETTINGS_UPDATE_EMAIL)
    .map<string>(toPayload)
    .switchMap(email => this.settingsService.updateEmail(email)
      .map(() => ({ type: Actions.SETTINGS_UPDATE_EMAIL_SUCCESS }))
      .catch(error => Observable.of({ type: Actions.SETTINGS_UPDATE_EMAIL_FAIL, payload: error }))
    );

5) Reducer
// settings.reducer.ts

export const settingsReducer: ActionReducer<SettingsState> = (state: SettingsState = {}, action: Action) => {
  switch (action.type) {
    case Actions.SETTINGS_UPDATE_EMAIL_SUCCESS: {
      return state;
    }
    case Actions.SETTINGS_UPDATE_EMAIL_FAIL: {
      return Object.assign({}, state, { error: action.payload });
    }
    default: {
      return state;
    }
  }
};
3 Likes

I wonder why people decided to come up with ngrx instead of just using Redux, perhaps someone can help me understand.

1 Like

I think it is because much easier (because RxJS 5) to create a new one (and more powerful one, still because RxJS 5) instead of adapting to existing Redux Store and Redux Saga (BTW, ngrx/effects is much neater than Redux Saga I feel).
The most cool thing I feel is that ngrx is RxJS 5 (Observable) powered.
Also ngrx not only has ngrx/store, ngrx/effects, it also has ngrx/db, ngrx/notify, ngrx/router (v3 router is designed based on this router).

And Redux also has a RxJS 5-based middleware, redux-observable.
For React people, these two articles may help:

  1. Why RxJS is the Better Tool for Unidirectional Data Flow
  2. Use RxJS with React

“They were making trying to make better tables when HTML came out decades ago. It’s not much different now.”

  • I can’t remember who said that.

@Hongbo_Miao, very cool. I have a question on a different tangent - is it useful in smaller and medium sized projects though? Is there not too much boilerplate code just to get started?

It is like asking what kind of projects need Redux, do we need Redux.
Small project seems a little overkill. Other size, personally I enjoy Redux.

Boilerplate demo including both Angular 2 and Meteor will have, but not recently…
But you can learn from this ngrx example (pure Angular 2, no Meteor) first, and then came back here to see how to use ngrx in Angular2-Meteor project.

@sashko oh, I found this answered by Mike Ryan

origianl link

There are subtle nuances between @ngrx/store and ng2-redux that are not particularly interesting but I’ll try to explain them anyway:

The process for creating a Redux store with middleware using the actual Redux library maps poorly to Angular 2 DI providers. Passing dependencies to middleware is a hard problem and getting the actual Redux devtools to play nicely with change detection can be tricky. @ngrx/store and ng2-redux are different reactions to this problem set.

@ngrx/store has gone the route of re-implementing the Redux pattern in an Angular 2 and Rx friendly way. Configuring the Store is done with providers, the API surface area is small, our side effect library (which fills the role of most Redux middleware you would want) works great with DI, and our devtools are universal components ready to be rendered in a webworker thread or on the server. In short, because we are so focused on making @ngrx/store work well with Angular 2 I think we provide a great out-of-the-box experience for Angular 2 developers. In doing so, however, we sacrifice compatibility with Redux’s vast ecosystem and have had to re-implement a lot of the infrastructure ourselves.

ng2-redux on the other hand uses the real Redux library under the hood and is thus compatible with much of the Redux ecosystem. This means they aren’t re-implementing Redux infrastructure and have access to some really awesome tooling. The cost, however, is that it is (and this is my opinion here) a little clunky. Configuring the store in the root app component constructor is novel and not in a particularly pleasant way, though it does solve the problems listed above.

A few months ago, the libraries were a lot more different. Both teams have worked hard to narrow the gap and I predict given enough time it will be difficult to distinguish between the two. If you are having difficulty picking, honestly the best thing to do is to build something using both implementations. It should give you a clearer idea of each’s strengths and weaknesses.

As the above explanation is long and likely to change, I’m going to go ahead and close this issue and not make additions to the README. It isn’t an “us vs them” situation and I’d like to try and keep it that way.

I guess I think this is an inexcusable thing to do, since 90% of redux value is in the ecosystem. Any kid can reimplement the Redux pattern, so that’s not important. The important thing is compatibility with the vast range of available packages and patterns.

1 Like

UPDATED: New Observable based Meteor API release! No need to wrap manually.
Check 🎤 New Observable based Meteor API (beta) release!

This explains well:

Especially the comments from Rob Wormald and Dan Abramov

https://github.com/ngrx/store/issues/16