Add Service

Right now, all our message and channel data are in Components. In a real-world app, we should fetch data from database through Angular 2 Service. We will explain it later.

Before we go on, let's move that part of codes in the Service.

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

import { Injectable } from '@angular/core';

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

@Injectable()
export class ChatService {
  // fake database
  private channelsDb: Channel[] = [
    { _id: '0', name: 'general' },
    { _id: '1', name: 'channel1' }
  ];

  // fake database
  private messagesDb: Message[] = [
    { _id: '0', channelId: '0', userId: '0', userName: 'Jack', content: 'Hello!', timestamp: new Date() },
    { _id: '1', channelId: '0', userId: '0', userName: 'Jack', content: 'Hi!', timestamp: new Date() },
    { _id: '2', channelId: '1', userId: '0', userName: 'Jack', content: 'Hi again!', timestamp: new Date() }
  ];

  channels: Channel[] = [];
  messages: Message[] = [];

  channel: Channel;

  getChannels(): void {
    // fake API, get channel data from the database
    this.channels = this.channelsDb;

    this.channel = this.channels[0];
  }

  getMessages(channelName: string): void {
    // fake API, get message data from the database
    let channelId = '';
    this.channelsDb.map(channel => {
      if (channel.name === channelName) {
        channelId = channel._id;
      }
    });

    const messages = [];
    this.messagesDb.map(message => {
      if (message.channelId === channelId) {
        messages.push(message);
      }
    });

    this.messages = messages;
  }

  selectChannel(channel: Channel): void {
    this.channel = channel;
    this.getMessages(channel.name);
  }

  sendMessage(channelId: string, messageContent: string): void {
    const message = {
      _id: String(this.messagesDb.length),
      channelId,
      userId: '0',
      userName: 'Jack',
      content: messageContent,
      timestamp: new Date()
    };

    this.messages = [...this.messages, message];

    // fake API, write message data to the database
    this.messagesDb = [...this.messagesDb, message];
  }
}

src/app/app.module.ts

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

// ...
  providers: [ChatService],
// ...

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

  // ...
  template: `
    <div class="left">
      <ul>
        <li *ngFor="let channel of chatService.channels"
          [ngClass]="{'selected': channel._id === chatService.channel?._id}"
          (click)="onSelectChannel(channel)">
          {{channel.name}}
        </li>
      </ul>
    </div>
    <div class="right">
      <ul>
        <li *ngFor="let message of chatService.messages">
          <p><span class="name">{{message.userName}}</span> <span class="timestamp">{{message.timestamp | calendar}}</span></p>
          <p>{{message.content}}</p>
        </li>
      </ul>
      <form [formGroup]="messageForm" (ngSubmit)="onSendMessage()">
        <input formControlName="message" placeholder="Click enter to send"/>
      </form>
    </div>
  `,
})
export class ChatComponent {
  messageForm: FormGroup;

  constructor(
    private fb: FormBuilder,
    private chatService: ChatService
  ) {}

  ngOnInit() {
    this.chatService.getChannels();
    this.chatService.getMessages('general');

    this.messageForm = this.fb.group({
      message: ['', Validators.required]
    });
  }

  onSelectChannel(channel: Channel) {
    this.chatService.selectChannel(channel);
  }

  onSendMessage() {
    if (!this.messageForm.valid) return;

    this.chatService.sendMessage(this.chatService.channel._id, this.messageForm.value.message);

    this.messageForm.reset();
  }
}

Let's explain the codes. In the ChatComponent, we add private chatService: ChatService in the constructor. This way is called Dependency Injection.

There are some benefits when we put those codes in Service and then inject them in Component.

  • We can change our Service with fake data for testing without rewriting our Component codes.
  • We can call same function in different Components.

If you want to learn more about Dependency Injection, please check Dependency Injection in Angular 2.

Now we can use chatService:

  • In the template, we change to chatService.channels, chatService.messages, chatService.channel?._id.
  • In the class, now we can use this.chatService.getChannels(); and this.chatService.getMessages('general'); to get data from database.

? in chatService.channel?._id is called safe navigation operator. It won't throw error when channel is undefined.

getMessages takes an parameter which is the channel name. And note we are using getMessages in selectChannel, so now the app can show different messages based on channel!


Run the live example for this part.

results matching ""

    No results matching ""