Finish the rest part

We already shows how to add ngrx/store and ngrx/effects to profile. You can try to add chat reducer and effects by yourself before reading the codes below.

src/app/chat/actions/chat.actions.ts

export class ChatActions {
  static CHAT_GET_CHANNELS = '[Chat] Get Channels';
  static CHAT_GET_CHANNELS_SUCCESS = '[Chat] Get Channels Success';
  static CHAT_GET_CHANNELS_FAIL = '[Chat] Get Channels Fail';

  static CHAT_ADD_CHANNEL = '[Chat] Add Channel';
  static CHAT_ADD_CHANNEL_SUCCESS = '[Chat] Add Channel Success';
  static CHAT_ADD_CHANNEL_FAIL = '[Chat] Add Channel Fail';

  static CHAT_SELECT_CHANNEL = '[Chat] Select Channel';

  static CHAT_GET_MESSAGES = '[Chat] Get Messages';
  static CHAT_GET_MESSAGES_SUCCESS = '[Chat] Get Messages Success';
  static CHAT_GET_MESSAGES_FAIL = '[Chat] Get Messages Fail';

  static CHAT_SEND_MESSAGE = '[Chat] Send Message';
  static CHAT_SEND_MESSAGE_SUCCESS = '[Chat] Send Message Success';
  static CHAT_SEND_MESSAGE_FAIL = '[Chat] Send Message Fail';
}

src/app/chat/services/chat.service.ts

// ...
import { Observable } from 'rxjs/Observable';
import { Message } from '../models/chat.model';
import { Channel } from '../models/channel.model';

@Injectable()
export class ChatService {
  // ...

  getChannels(): Observable<Channel[]> {
    return Observable.of(this.channelsDb);
  }

  getMessages(channelName: string): Observable<Message[]> {
    // ...
    return Observable.of(messages);
  }

  addChannel(channelName: string): Observable<Channel> {
    // ...
    return Observable.of(channel);
  }

  sendMessage(channelId: string, messageContent: string): Observable<Message> {
    // ...
    return Observable.of(message);
  }
}

src/app/chat/effects/chat.effects.ts

import { Injectable } from '@angular/core';
import { Effect, Actions, toPayload } from '@ngrx/effects';
import { Action } from '@ngrx/store';
import { Observable } from 'rxjs/Observable';

import { ChatActions } from '../actions/chat.actions';
import { ChatService } from '../services/chat.service';

@Injectable()
export class ChatEffects {
  constructor(
    private actions$: Actions,
    private chatService: ChatService
  ) {}

  @Effect() getChannels$ = this.actions$
    .ofType(ChatActions.CHAT_GET_CHANNELS)
    .switchMap(() => this.chatService.getChannels()
      .map(channels => ({ type: ChatActions.CHAT_GET_CHANNELS_SUCCESS, payload: channels }))
      .catch(error => Observable.of({ type: ChatActions.CHAT_GET_CHANNELS_FAIL, payload: error }))
    );

  @Effect() addChannel$ = this.actions$
    .ofType(ChatActions.CHAT_ADD_CHANNEL)
    .map<Action, string>(toPayload)
    .mergeMap(channelName => this.chatService.addChannel(channelName)
      .map(channel => ({ type: ChatActions.CHAT_ADD_CHANNEL_SUCCESS, payload: channel }))
      .catch(error => Observable.of({ type: ChatActions.CHAT_ADD_CHANNEL_FAIL, payload: error }))
    );

  @Effect() getMessages$ = this.actions$
    .ofType(ChatActions.CHAT_GET_MESSAGES)
    .map<Action, string>(toPayload)
    .switchMap(channelName => this.chatService.getMessages(channelName)
      .map(messages => ({ type: ChatActions.CHAT_GET_MESSAGES_SUCCESS, payload: messages }))
      .catch(error => Observable.of({ type: ChatActions.CHAT_GET_MESSAGES_FAIL, payload: error }))
    );

  @Effect() sendMessage$ = this.actions$
    .ofType(ChatActions.CHAT_SEND_MESSAGE)
    .map<Action, any>(toPayload)
    .mergeMap(({ channelId, messageContent }) => this.chatService.sendMessage(channelId, messageContent)
      .map(message => ({ type: ChatActions.CHAT_SEND_MESSAGE_SUCCESS, payload: message }))
      .catch(error => Observable.of({ type: ChatActions.CHAT_SEND_MESSAGE_FAIL, payload: error }))
    );
}

src/app/chat/reducers/chat.reducer.ts

import { ActionReducer, Action } from '@ngrx/store';

import { Channel } from '../models/channel.model';
import { Message } from '../models/message.model';
import { ChatActions } from '../actions/chat.actions';

export interface ChatState {
  channels: Channel[],
  channel?: Channel,
  messages: Message[]
}

const initialState: ChatState = {
  channels: [],
  messages: []
};

export const chatReducer: ActionReducer<ChatState> = (state = initialState, action: Action) => {
  switch (action.type) {
    case ChatActions.CHAT_GET_CHANNELS_SUCCESS: {
      return Object.assign({}, state, { channels: action.payload, channel: action.payload[0] });
    }

    case ChatActions.CHAT_ADD_CHANNEL_SUCCESS: {
      return Object.assign({}, state, { channels: [...state.channels, action.payload] });
    }

    case ChatActions.CHAT_GET_MESSAGES_SUCCESS: {
      return Object.assign({}, state, { messages: action.payload });
    }

    case ChatActions.CHAT_SEND_MESSAGE_SUCCESS: {
      return Object.assign({}, state, { messages: [...state.messages, action.payload] });
    }

    case ChatActions.CHAT_SELECT_CHANNEL: {
      return Object.assign({}, state, { channel: action.payload });
    }

    default: {
      return state;
    }
  }
};

src/app/chat/components/chat.component.ts

// ...
import { ChatState } from '../reducers/chat.reducer';
import { ChatActions } from '../actions/chat.actions';

@Component({
  // ...
  template: `
    <div class="left">
      <my-channel-header
        [name]="(profileModel$ | async)?.name"
        (goProfile)="onGoProfile()">
      </my-channel-header>
      <hr>
      <my-channel-add
        (addChannel)="onAddChannel($event)">
      </my-channel-add>
      <my-channel-list
        [channels]="(chatModel$ | async)?.channels"
        [channelId]="(chatModel$ | async)?.channel._id"
        (selectChannel)="onSelectChannel($event)">
      </my-channel-list>
    </div>

    <div class="right">
      <my-chat-area-header
        [channelName]="(chatModel$ | async)?.channel.name">
      </my-chat-area-header>
      <hr>
      <my-chat-area-dialog
        [messages]="(chatModel$ | async)?.messages">
      </my-chat-area-dialog>
      <my-chat-area-bottom
        [channelId]="(chatModel$ | async)?.channel._id"
        (sendMessage)="onSendMessage($event)">
      </my-chat-area-bottom>
    </div>
  `,
})
export class ChatComponent implements OnInit, OnDestroy {
  chatModel$: Observable<ChatState>;
  // ...

  ngOnInit() {
    this.chatModel$ = this.store.select<ChatState>('chat');
    // ...
  }

  // ...

  private onAddChannel(channelName: string) {
    this.store.dispatch({ type: ChatActions.CHAT_ADD_CHANNEL, payload: channelName });
  }

  private onSelectChannel(channel: Channel) {
    this.store.dispatch({ type: ChatActions.CHAT_SELECT_CHANNEL, payload: channel });
    this.router.navigate(['/channel', channel.name]);
  }

  private onSendMessage({ channelId, messageContent }) {
    this.store.dispatch({ type: ChatActions.CHAT_SEND_MESSAGE, payload: { channelId, messageContent } });
  }

  // ...
}

src/app/shared/models/state.model.ts

import { ChatState } from '../../chat/reducers/chat.reducer';
// ...

export interface State {
  chat: ChatState,
  // ...
}

src/app/app.module.ts

// ...
import { chatReducer } from './chat/reducers/chat.reducer';
import { ChatEffects } from './chat/effects/chat.effects';

@NgModule({
  imports: [
    // ...
    StoreModule.provideStore({
      chat: chatReducer,
      // ...
    }),

    EffectsModule.run(ChatEffects),
    // ...

Run the live example for this part.

results matching ""

    No results matching ""