Communicate With Websockets In A NativeScript Angular Application

I’ve been playing around with sockets and websockets recently. Not too long ago I wrote about creating a real-time chat application using Golang and Angular that made use of websockets. In that example we created a chat server using the Go programming language and a client facing web application using Angular. The communication between the two used websockets to keep things real-time.

What if we wanted to create a native mobile application for Android and iOS that communicated via websockets to our server or any other websocket server? Using NativeScript, it is very possible to create a mobile client that works with the Golang with Angular example as seen previously.

The example we’re going to see is a simple chat application. The Android and iOS application will connect to a server using websockets and communicate to it in real-time.

NativeScript Websocket Example

The focus of this example will not be creating a websocket server, but instead a NativeScript websocket client. If you want a working and easy to use Golang websocket server, check out my previous article on the topic.

Creating a NativeScript Project with Websockets

To make the NativeScript application easy to understand, we’re going to start with a fresh project. With NativeScript installed, execute the following using the CLI:

tns create WebsocketProject --ng
cd WebsocketProject
tns platform add ios
tns platform add android

In the above commands, we’re creating an Angular with TypeScript project by making use of the --ng tag. While we’re adding the iOS build platform, we can’t actually build for iOS unless we’re using a Mac with Xcode installed.

Because NativeScript applications are native applications and not web applications, the concept of websockets don’t quite exist. To make this possible, we need to install the nifty nativescript-websockets plugin by Nathanael Anderson. To do this, execute the following within your project:

tns plugin add nativescript-websockets

At this point the application is created and ready to use websocket communications.

Adding the Angular and TypeScript Logic

For simplicity, this chat application will only be a single page application. This means all of our logic will reside in a single file. Open the project’s app/app.component.ts file and include the following TypeScript code:

import { Component, OnInit, OnDestroy, NgZone } from "@angular/core";
require("nativescript-websockets");

@Component({
    selector: "my-app",
    templateUrl: "app.component.html",
})
export class AppComponent implements OnInit, OnDestroy {

    private socket: any;
    public messages: Array<any>;
    public chatBox: string;

    public constructor(private zone: NgZone) {
        this.socket = new WebSocket("ws://192.168.57.1:12345/ws", []);
        this.messages = [];
        this.chatBox = "";
    }

    public ngOnInit() {
        this.socket.addEventListener('open', event => {
            this.zone.run(() => {
                this.messages.push({content: "Welcome to the chat!"});
            });
        });
        this.socket.addEventListener('message', event => {
            this.zone.run(() => {
                this.messages.push(JSON.parse(event.data));
            });
        });
        this.socket.addEventListener('close', event => {
            this.zone.run(() => {
                this.messages.push({content: "You have been disconnected"});
            });
        });
        this.socket.addEventListener('error', event => {
            console.log("The socket had an error", event.error);
        });
    }

    public ngOnDestroy() {
        this.socket.close();
    }

    public send() {
        if(this.chatBox) {
            this.socket.send(this.chatBox);
            this.chatBox = "";
        }
    }

}

There is a lot going on in the above so we’re going to break it down.

We need to first import a few Angular components as well as the websocket plugin:

import { Component, OnInit, OnDestroy, NgZone } from "@angular/core";
require("nativescript-websockets");

The websocket listeners will start in the ngOnInit which is part of the OnInit interface. The socket will be gracefully destroyed in the ngOnDestroy which is part of OnDestroy. Listeners with Angular UIs tend to be a little wonky so we need to use NgZone for updating the UI within a listen event.

Inside the AppComponent class we have a few public and private variables:

private socket: any;
public messages: Array<any>;
public chatBox: string;

The socket variable will hold our websocket while messages will hold all chat messages that will be bound to the UI. The chatBox variable is bound to a form within the UI.

Inside the constructor method we can initialize our variables and establish a connection to the server via websockets:

public constructor(private zone: NgZone) {
    this.socket = new WebSocket("ws://192.168.57.1:12345/ws", []);
    this.messages = [];
    this.chatBox = "";
}

The websocket address is that of which I created in the Golang example. You can simply replace the address with that of your actual websocket server.

The ngOnInit method is where all the magic happens:

public ngOnInit() {
    this.socket.addEventListener('open', event => {
        this.zone.run(() => {
            this.messages.push({content: "Welcome to the chat!"});
        });
    });
    this.socket.addEventListener('message', event => {
        this.zone.run(() => {
            this.messages.push(JSON.parse(event.data));
        });
    });
    this.socket.addEventListener('close', event => {
        this.zone.run(() => {
            this.messages.push({content: "You have been disconnected"});
        });
    });
    this.socket.addEventListener('error', event => {
        console.log("The socket had an error", event.error);
    });
}

With websockets there are four possible events. When the connection is established, when a data load or message was received, when the socket connection was closed, and when there was an error. Any time we need to present something on the screen, we wrap it in a zone.run and everything will be great.

To avoid confusion, the data we’re expecting is based once again on the Golang application that I’ve been mentioning. In that application, the data payload is JSON in the following format:

{
    "sender": string,
    "content": string,
    "recipient": string,
    "timestamp": string
}

With the format known, we can see that we’re parsing it into a JSON object.

When it comes to actually sending data to the other end, we have our send method:

public send() {
    if(this.chatBox) {
        this.socket.send(this.chatBox);
        this.chatBox = "";
    }
}

If the form is not empty, send the content to the socket and clear the form. Depending on if the server relays the message back to the client, the message listener might trigger.

Because data will be bound to forms within the UI, a certain Angular module must be imported. Open the project’s app/app.module.ts file and make it look similar to the following:

import { NgModule, NO_ERRORS_SCHEMA } from "@angular/core";
import { NativeScriptModule } from "nativescript-angular/platform";
import { NativeScriptFormsModule } from "nativescript-angular/forms";

import { AppComponent } from "./app.component";

@NgModule({
    declarations: [AppComponent],
    bootstrap: [AppComponent],
    imports: [NativeScriptModule, NativeScriptFormsModule],
    schemas: [NO_ERRORS_SCHEMA]
})
export class AppModule { }

Essentially we’ve just imported NativeScriptFormsModule and added it to the imports array of the @NgModule block. At this point we can focus on the UI of the application.

Designing the User Interface

When building the UI we’re going to need to play around with CSS and XML markup. Starting with the project’s app/app.component.html file, open it and include the following:

<ActionBar title="Websocket Example"></ActionBar>
<GridLayout rows="*, auto" columns="*">
    <ListView [items]="messages" class="list-group" row="0" col="0">
        <Template let-message="item">
            <GridLayout class="list-group-item">
                <Label text="{{ message.content }}"></Label>
            </GridLayout>
        </Template>
    </ListView>
    <StackLayout class="chat-area" row="1" col="0">
        <GridLayout rows="auto" columns="*, auto">
            <TextField [(ngModel)]="chatBox" class="input" row="0" col="0"></TextField>
            <Button text="Send" (tap)="send()" class="btn btn-primary" row="0" col="1"></Button>
        </GridLayout>
    </StackLayout>
</GridLayout>

The above UI is composed of a navigation bar, and split content. At the bottom, in the format of a footer, we have a text field and in the middle we have a list with all the data found in the messages array.

The chat-area class name can be defined in the project’s app/app.css file like so:

@import 'nativescript-theme-core/css/core.light.css';

.chat-area {
    background-color: #F0F0F0;
}

Not a whole lot is going on in the UI, but with a little imagination it could receive a bit more complexity. That is a project for another day though.

Conclusion

You just saw how to create a NativeScript websocket client application. While I referenced a previous Golang websocket server example throughout this guide, it is by no means a requirement towards the success of using websockets. In this example we send and receive message data in real-time, just like you’d see in a chat application. Websockets can be used far beyond chat applications with a bit of imagination.

Nic Raboy

Nic Raboy

Nic Raboy is an advocate of modern web and mobile development technologies. He has experience in Java, JavaScript, Golang and a variety of frameworks such as Angular, NativeScript, and Apache Cordova. Nic writes about his development experiences related to making web and mobile development easier to understand.

Search

Follow Us

Subscribe

Subscribe to my newsletter for monthly tips and tricks on subjects such as mobile, web, and game development.

Subscribe on YouTube

Support This Site