We will implement a Angular client where the user will connect to the server and send the text to server and then server will append “Message from server: Hello ” and send to the user.
Step 1 : Add maven dependency
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-messaging</artifactId> <version>5.0.9.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-websocket</artifactId> <version>5.0.9.RELEASE</version> </dependency>
Step 2 : Enable WebSocket in Spring Boot
We need to add the @EnableWebSocketMessageBroker to enable the WebSocket capabilities. It enables WebSocket message handling, backed by a message broker.
package com.onlyfullstack.springbootwebsocketbackend.config; import org.springframework.context.annotation.Configuration; import org.springframework.messaging.simp.config.MessageBrokerRegistry; import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; import org.springframework.web.socket.config.annotation.StompEndpointRegistry; import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; @Configuration @EnableWebSocketMessageBroker public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { /** * Configure message broker options. */ @Override public void configureMessageBroker(MessageBrokerRegistry config) { config.enableSimpleBroker("/topic"); config.setApplicationDestinationPrefixes("/onlyfullstack"); // prefix for every @MessageController path } /** * Register STOMP endpoints mapping each to a specific URL and (optionally) * enabling and configuring SockJS fallback options. */ @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry .addEndpoint("/onlyfullstack-stomp-endpoint") // Where the client will connect to the STOMP server .setAllowedOrigins("http://localhost:4200") .withSockJS(); } }
Here, we can see that the method configureMessageBroker is used to configure the message broker. First, we enable an in-memory message broker to carry the messages back to the client on destinations prefixed with “/topic”.
We complete our simple configuration by designating the “/onlyfullstack” prefix to filter destinations targeting application annotated methods (via @MessageMapping).
The registerStompEndpoints method registers the “/onlyfullstack-stomp-endpoint” endpoint, enabling Spring’s STOMP support.
It also enables the SockJS fallback options, so that alternative messaging options may be used if WebSockets are not available. This is useful since WebSocket is not supported in all browsers yet and may be precluded by restrictive network proxies.
package com.onlyfullstack.springbootwebsocketbackend.model; import lombok.AllArgsConstructor; import lombok.Data; @Data @AllArgsConstructor public class Hello { private String greeting; }
package com.onlyfullstack.springbootwebsocketbackend.model; import lombok.Data; @Data public class User { private String name; }
Step 4 : Create a Message-Handling Controller with @MessageMapping method
package com.onlyfullstack.springbootwebsocketbackend.controller; import com.onlyfullstack.springbootwebsocketbackend.model.Hello; import com.onlyfullstack.springbootwebsocketbackend.model.User; import org.springframework.messaging.handler.annotation.MessageMapping; import org.springframework.messaging.handler.annotation.SendTo; import org.springframework.stereotype.Controller; @Controller public class WebSocketController { @MessageMapping("/hello") // endpoint where the client will send messages or events @SendTo("/topic/hi") // messaging queue endpoint where client will be listening public Hello greeting(User user) { return new Hello("Message from server: Hello " + user.getName()); } }
Spring’s approach to working with STOMP messaging is to associate a controller method to the configured endpoint. This is made possible through the @MessageMapping annotation.
Okay so we have successfully created the WebSocket at backend and now we will start the frontend development of Angular 6 + StompJs.
Angular 6 + StompJs + WebSocket Frontend
Step 1 : Create new project using Angular CLI
ng new WebsocketAangular6Frontend
Step 2 : Install @stomp/stompjs and socksjs-client
npm install @stomp/stompjs --save npm install sockjs-client --save
Step 3 : Add this code in the first line of polyfills.ts file:
(window as any).global = window;
Step 4 : Add required module of Angular Material and form
import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import {MatButtonModule, MatFormFieldModule, MatInputModule, MatDividerModule, MatCardModule} from '@angular/material'; import { FlexLayoutModule } from '@angular/flex-layout'; import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; @NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule, AppRoutingModule, FormsModule, MatButtonModule, MatFormFieldModule, MatInputModule, BrowserAnimationsModule, FlexLayoutModule, ReactiveFormsModule, MatDividerModule, MatCardModule, FormsModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
Step 4 : Design the HTML form
app.component.html
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>{{title}}</title> <base href="/"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"> </head> <body> <div style="color: #280861; text-align: center"> <h1 class="mat-title">WebSocket with Spring Boot + STOMP + Angular 6</h1> <a class="mat-subheading-2" href="#">Websocket with Spring Boot and Agngular 6 guide</a> <h4 class="mat-h4">Author - Saurabh Oza (Only Fullstack Developer Blog)</h4> </div> <mat-divider [inset]="true" style="margin: 2%"></mat-divider> <div fxLayout="column" fxLayoutAlign="space-evenly center" fxLayouyGap="1%"> <label>Make Connection</label> <div fxLayout="row" fxLayoutAlign="space-evenly center" style="min-width: 40%"> <button mat-raised-button color="primary" type="button" [disabled]="!disabled" (click)="connectToWebsocketWithStomp()">Connect</button> <button mat-raised-button type="submit" [disabled]="disabled" (click)="disconnect()">Disconnect</button> </div> </div> <mat-divider [inset]="true" style="margin: 2%"></mat-divider> <div fxLayout="column" fxLayoutAlign="space-evenly center" *ngIf="!disabled"> <div> <p class="mat-subheading-2">You are connected to the server, now you can send user names.</p> </div> <form [formGroup]="userForm" fxLayout="column" (submit)="submit()"> <mat-form-field class="example-full-width"> <input matInput placeholder="User's Name" formControlName="userName"> </mat-form-field> <button mat-raised-button color="primary" type="submit">Send</button> </form> <div fxLayout="column" fxLayoutGap="1%"> <mat-card *ngFor="let greeting of greetings" style="min-width: 30%"> {{greeting}} </mat-card> </div> </div> </body> </html>
Step 5 : Add some logic in our component file.
app.component.ts
import { Component, OnInit } from '@angular/core'; import * as Stomp from '@stomp/stompjs'; import * as SockJS from 'sockjs-client'; import { FormGroup, FormBuilder, Validators } from '@angular/forms'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements OnInit { greetings: string[] = []; disabled = true; private stompClient = null; userForm: FormGroup; constructor(private formBuilder: FormBuilder) { } ngOnInit(): void { this.userForm = this.formBuilder.group({ userName: ['', Validators.required] }); } /** * This method creates a WebSocket connection with server using a stomp js client */ connectToWebsocketWithStomp() { const socket = new SockJS('http://localhost:8080/onlyfullstack-stomp-endpoint'); this.stompClient = Stomp.over(socket); const _this = this; this.stompClient.connect({}, function(frame) { _this.showUserNameForm(true); console.log('Connected: ' + frame); _this.stompClient.subscribe('/topic/hi', function(hello) { _this.showGreeting(JSON.parse(hello.body).greeting); }); }); } /** * This method disconnects the WebSocket connection */ disconnect() { if (this.stompClient != null) { this.stompClient.disconnect(); } this.showUserNameForm(false); console.log('Disconnected!'); } /** * This method submits the username form aand sends the entered data to the server */ submit() { this.stompClient.send( '/onlyfullstack/hello', {}, JSON.stringify({ name: this.userForm.value.userName }) ); } showGreeting(message) { this.greetings.push(message); } showUserNameForm(connected: boolean) { this.disabled = !connected; if (connected) { this.greetings = []; } } }
Run and check the result
– Spring Boot backend project with command-lines: mvn clean install and mvn spring-boot:run.
– Angular project: npm install and npm start.
Now Open browser with url http://localhost:4200/.
Click on Connect button and send User’s Name.
Download the source code of WebSocket tutorial from below git repository :
websocket-with-spring-boot-and-angular-6
HI, I am getting this error
ERROR TypeError: _stomp_stompjs__WEBPACK_IMPORTED_MODULE_3__.over is not a function