ng2-stompjs with Angular 7

7 minute read

This step by step guide will create a new Angular application and demonstrate usage of ng2-stompjs.

While preparing this guide Angular 7.0.4 and @stomp/ng2-stompjs 7.0.0 are used.

For the impatient, final code from this tutorial is at: https://github.com/stomp-js/ng2-stompjs-angular7

Pre requisites

You need to have Node.js and npm installed.

You must have basic familiarity with Angular and Typescript. If you are unsure, please go through the famous Tour of Heroes.

Instructions

Create a new Angular Application

Install latest Angular CLI as below:

$ npm i @angular/cli -g

Change to the folder where you want to create the application and create your new application:

$ ng new ng2-stompjs-angular7 --skip-install --defaults

Change to new created folder and install all dependencies:

$ cd ng2-stompjs-angular7/
$ npm i

To run the application locally execute the following and point your browser to http://localhost:4200/.

$ ng serve

You can keep it running in a terminal and keep the browser tab open. It will detect changes, recompile and reload the browser tab.

At this stage you can use favorite IDE and be ready to edit code.

Add and Inject @stomp/ng2-stompjs

$ npm i @stomp/ng2-stompjs

We will need to define our configuration. This configuration will get injected by Angular Dependency Injection mechanism while creating instance of RxStompService.

Create my-rx-stomp.config.ts file inside src/app/ with the following content:

import { InjectableRxStompConfig } from '@stomp/ng2-stompjs';

export const myRxStompConfig: InjectableRxStompConfig = {
  // Which server?
  brokerURL: 'ws://127.0.0.1:15674/ws',

  // Headers
  // Typical keys: login, passcode, host
  connectHeaders: {
    login: 'guest',
    passcode: 'guest',
  },

  // How often to heartbeat?
  // Interval in milliseconds, set to 0 to disable
  heartbeatIncoming: 0, // Typical value 0 - disabled
  heartbeatOutgoing: 20000, // Typical value 20000 - every 20 seconds

  // Wait in milliseconds before attempting auto reconnect
  // Set to 0 to disable
  // Typical value 500 (500 milli seconds)
  reconnectDelay: 200,

  // Will log diagnostics on console
  // It can be quite verbose, not recommended in production
  // Skip this key to stop logging to console
  debug: (msg: string): void => {
    console.log(new Date(), msg);
  },
};

The above should work for a out of the box installation of RabbitMQ broker. Please change as per your broker configuration.

Next we need to configure the configuration to get injected. Open src/app/app.module.ts Add the following to the providers array of your @NgModule:

providers: [
  {
    provide: InjectableRxStompConfig,
    useValue: myRxStompConfig,
  },
  {
    provide: RxStompService,
    useFactory: rxStompServiceFactory,
    deps: [InjectableRxStompConfig],
  },
];

Also add appropriate import lines towards the top of this file (after existing import statements):

import {
  InjectableRxStompConfig,
  RxStompService,
  rxStompServiceFactory,
} from '@stomp/ng2-stompjs';

import { myRxStompConfig } from './my-rx-stomp.config';

Messages

We will create a component that will do the following:

  • It will have a click-able button to send a message.
  • It will subscribe and keep listing all received messages.

Skeleton

Create the Messages component:

$ ng generate component messages

Inspect the files generated in src/app/messages/.

Now we will create the basic HTML in src/app/messages/messages.component.html - put the following content:

<div id="messages">
  <button class="btn btn-primary">Send Test Message</button>
  <h2>Received messages</h2>
  <ol>
    <!-- we will use Angular binding to populate list of messages -->
    <li class="message">message</li>
  </ol>
</div>

We will add this component to the main UI by editing src/app/app.component.html. In this process we will remove most of the default HTML generated by Angular CLI. Edit src/app/app.component.html to look like the following:

<div style="text-align:center">
  <h1>Welcome to !</h1>
</div>
<app-messages></app-messages>

This is a great time to check the browser tab to see that display has changed and it should show the HTML that we have added to src/app/messages/messages.component.html.

If you find an empty screen, please check the terminal where you executed ng serve, if there are compilation errors you would see it here. Also, see the browser Javascript console, sometimes you may notice errors here.

Sending messages

We will now inject RxStompService as a dependency in MessageComponent. To that we will add it to the constructor in src/app/messages/messages.component.ts, which should look like the following:

  constructor(private rxStompService: RxStompService) { }

We will now add code to send message:

  onSendMessage() {
    const message = `Message generated at ${new Date}`;
    this.rxStompService.publish({destination: '/topic/demo', body: message});
  }

Please see RxStomp#publish. Keep this page open as we would be using more methods from this class soon.

Full content of src/app/messages/messages.component.ts should look like the following:

import { Component, OnInit } from '@angular/core';
import { RxStompService } from '@stomp/ng2-stompjs';

@Component({
  selector: 'app-messages',
  templateUrl: './messages.component.html',
  styleUrls: ['./messages.component.css'],
})
export class MessagesComponent implements OnInit {
  constructor(private rxStompService: RxStompService) {}

  ngOnInit() {}

  onSendMessage() {
    const message = `Message generated at ${new Date()}`;
    this.rxStompService.publish({ destination: '/topic/demo', body: message });
  }
}

We will attach onSendMessage to the button in the html. Edit src/app/messages/messages.component.html and add (click)="onSendMessage()" to the button. The file should look like the following now:

<div id="messages">
  <button class="btn btn-primary" (click)="onSendMessage()">
    Send Message
  </button>
  <h2>Received messages</h2>
  <ol>
    <!-- we will use Angular binding to populate list of messages -->
    <li class="message">message</li>
  </ol>
</div>

At this stage you should go back to the browser tab and open the web console. When you click on the Send Message button, you can see in the console that message is being sent to the broker.

Receiving messages

The RxStomp#watch method initiates a subscription with the broker. = this.rxStompService.watch('/topic/demo') will initiate a subscription with the broker for topic /topic/demo and returns an RxJS Observable. Typically we will subscribe this Observable to receive actual messages.

  ngOnInit() {
    this.rxStompService.watch('/topic/demo').subscribe((message: Message) => {
      this.receivedMessages.push(message.body);
    });
  }

We will need to add a declaration for receivedMessages and import Message from @stomp/stompjs. There are Message classes exposed by few other modules as well, so, you need to be careful.

If you are coming from @stomp/stompjs, please notice that you do not need to subscribe within callback of stomp getting connected. This library internally ensures that actual subscription is carried out when the broker is actually connected. It also keep tracking of broker re-connections and automatically resubscribes.

Now is the time to link the HTML template to received messages. We will use ngFor to bind list of messages to <li>. Edit src/app/messages/messages.component.html:

<li class="message" *ngFor="let message of receivedMessages"></li>

Now your src/app/messages/messages.component.ts and src/app/messages/messages.component.html should look like:

import { Component, OnInit } from '@angular/core';
import { RxStompService } from '@stomp/ng2-stompjs';
import { Message } from '@stomp/stompjs';

@Component({
  selector: 'app-messages',
  templateUrl: './messages.component.html',
  styleUrls: ['./messages.component.css'],
})
export class MessagesComponent implements OnInit {
  public receivedMessages: string[] = [];

  constructor(private rxStompService: RxStompService) {}

  ngOnInit() {
    this.rxStompService.watch('/topic/demo').subscribe((message: Message) => {
      this.receivedMessages.push(message.body);
    });
  }

  onSendMessage() {
    const message = `Message generated at ${new Date()}`;
    this.rxStompService.publish({ destination: '/topic/demo', body: message });
  }
}
<div id="messages">
  <button class="btn btn-primary" (click)="onSendMessage()">
    Send Message
  </button>
  <h2>Received messages</h2>
  <ol>
    <!-- we will use Angular binding to populate list of messages -->
    <li class="message" *ngFor="let message of receivedMessages">
      
    </li>
  </ol>
</div>

Check your application in the browser now. Try sending few messages. Open another browser window/tab and see messages being received in both.

Stopping the watch

We are almost done, we just need to add stopping the watch when the component is destroyed. For this we need to call unsubscribe on the RxJS subscription when we are done. MessagesComponent will need to implement OnDestroy. For this we will need to do the following:

  • Add OnDestroy to the implements list (by default OnInit will be there).
  • Implement ngOnDestroy method.
  • Add OnDestroy to the imports.

Once the above is done we will modify code in ngOnInit to store the RxJS subscription in a member variable and call unsusbscribe in ngOnDestroy.

  ngOnInit() {
    this.topicSubscription = this.rxStompService.watch('/topic/demo').subscribe((message: Message) => {
      this.receivedMessages.push(message.body);
    });
  }

  ngOnDestroy() {
    this.topicSubscription.unsubscribe();
  }

Type of topicSubscription will be Subscription from rxjs. Your src/app/messages/messages.component.ts should look like the following:

import { Component, OnDestroy, OnInit } from '@angular/core';
import { RxStompService } from '@stomp/ng2-stompjs';
import { Message } from '@stomp/stompjs';
import { Subscription } from 'rxjs';

@Component({
  selector: 'app-messages',
  templateUrl: './messages.component.html',
  styleUrls: ['./messages.component.css'],
})
export class MessagesComponent implements OnInit, OnDestroy {
  public receivedMessages: string[] = [];
  private topicSubscription: Subscription;

  constructor(private rxStompService: RxStompService) {}

  ngOnInit() {
    this.topicSubscription = this.rxStompService
      .watch('/topic/demo')
      .subscribe((message: Message) => {
        this.receivedMessages.push(message.body);
      });
  }

  ngOnDestroy() {
    this.topicSubscription.unsubscribe();
  }

  onSendMessage() {
    const message = `Message generated at ${new Date()}`;
    this.rxStompService.publish({ destination: '/topic/demo', body: message });
  }
}

Where next

I will be writing more tutorials in following days to cover additional topics:

  • Manual control in the configuration and connection establishment. (In this sample, STOMP broker is connected during the Angular DI class initialization phase.)

Updated: