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.