Accept Signatures in Your NativeScript Angular Mobile Application

Living in the San Francisco Bay Area, I run into startup businesses using payment processing applications like Square all the time. If you're unfamiliar with these applications, they are credit card processors, whereby the customer signs their name to validate the transaction. This signature ends up in your email receipt and is also likely to be sent to the processing servers for storage. So how does one accept signatures within their Android and iOS mobile application?

We're going to see how to accept user signatures within a NativeScript mobile application built with Angular. Then we're going to see how to take this signature and either store it or send it.

You can download a zip of the finished project here.

Create a Fresh NativeScript with Angular Project

The goal of this project is to accept user signatures on the screen and either submit them to some remote web server or database, or refresh the drawing pad for a new signature.

NativeScript Signature Pad

The concept of this application is simplistic, but accomplishes a very important task.

Assuming you've already installed the NativeScript CLI, execute the following command:

tns create signature-project --ng

The --ng flag in the above command indicates that we're going to be building an Angular project rather than a core NativeScript project.

Including and Initializing the Drawing Pad Plugin for Signatures

Much of this project will be powered by the NativeScript DrawingPad plugin that was created by Brad Martin and several members of the NativeScript community.

Navigate into the project with the CLI and execute the following command:

tns plugin add nativescript-drawingpad

Since the plugin was designed to work with core NativeScript, it needs to be included into the project a special way in order to be compatible with Angular.

Open the project's app/app.module.ts file and make the TypeScript code look like the following:

import { NgModule, NO_ERRORS_SCHEMA } from "@angular/core";
import { NativeScriptModule } from "nativescript-angular/nativescript.module";
import { NativeScriptHttpModule } from "nativescript-angular/http";
import { AppRoutingModule } from "./app.routing";
import { AppComponent } from "./app.component";

import { registerElement } from "nativescript-angular/element-registry";
registerElement("DrawingPad", () => require("nativescript-drawingpad").DrawingPad);

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

Essentially what we've done is added the following two lines which allows us to use the plugin via HTML markup:

import { registerElement } from "nativescript-angular/element-registry";
registerElement("DrawingPad", () => require("nativescript-drawingpad").DrawingPad);

The default NativeScript project template comes with a bunch of other stuff, but for simplicity, I've gone ahead and removed it. This project was intended to create a single page mobile application.

Designing the User Interface and Application Logic

Now we can focus on the development of the application with the plugin. From a UI perspective, there will be a row of buttons at the bottom of the screen and the remainder of the screen will be of a drawable area. This is made possible through a GridLayout in NativeScript.

Open the project's app/app.component.html file and include the following markup:

<ActionBar title="{N} Signature Example"></ActionBar>
<GridLayout rows="*, auto" columns="*, *">
    <StackLayout row="0" col="0" colspan="2" backgroundColor="#E0E0E0">
        <DrawingPad #signature penColor="#000000" penWidth="5"></DrawingPad>
    </StackLayout>
    <StackLayout row="1" col="0">
        <Button text="Reset" (tap)="reset()" class="btn btn-danger w-full"></Button>
    </StackLayout>
    <StackLayout row="1" col="1">
        <Button text="Submit" (tap)="send()" class="btn btn-primary w-full"></Button>
    </StackLayout>
</GridLayout>

Notice that this is a two column and two row GridLayout design. The first row is the signature pad defined by the signature Angular template variable. This variable will be used when accessing the signature pad from the TypeScript code.

The two buttons will either reset the drawing pad or submit the content in some fashion. More information on using a GridLayout to stretch space on the screen can be seen in a previous article I wrote on the subject.

Most of the CSS in the markup is part of the NativeScript theme with the exception of the button for resetting everything. This CSS can be added to the project's app/app.css file and it would look like this:

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

.btn {
    margin: 0;
}

.btn-danger {
    background-color: red;
    color: #FFFFFF;
}

Now we can focus on the applications TypeScript logic. We're going to see the complete code followed by a breakdown of what is happening. Open the project's app/app.component.ts file and include the following:

import { Component, ViewChild, ElementRef, OnInit } from "@angular/core";
import { Http, Headers, RequestOptions } from "@angular/http";
import * as ImageSource from "image-source";

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

    @ViewChild("signature")
    public drawingPad: ElementRef;

    private pad: any;

    public constructor(private http: Http) { }

    public ngOnInit() { }

    public ngAfterViewInit() {
        this.pad = this.drawingPad.nativeElement;
    }

    public send() {
        this.pad.getDrawing().then(data => {
            let image = ImageSource.fromNativeSource(data);
            let headers = new Headers({ "Content-Type": "application/json" });
            let options = new RequestOptions({ headers: headers });
            let body = {
                "signature": image.toBase64String("png", 70)
            };
            this.http.post("http://example.com", JSON.stringify(body), options)
                .subscribe(result => {
                    // Uploaded signature as a base64 string
                }, error => {
                    console.dir(error);
                });
        }, error => {
            console.dir(error);
        });
    }

    public reset() {
        this.pad.clearDrawing();
    }

}

Within the AppComponent class we have two variables. The public drawingPad variable is tied to the UI component that uses an Angular template variable called signature. After obtaining the instance, it will further be stored in the private pad variable.

Because we wish to work directly with UI components, we have to wait until the view is ready. By making use of the ngAfterViewInit method, we can be sure that the drawing pad is ready before we try to access it.

This is where things get interesting. The send method looks like the following:

public send() {
    this.pad.getDrawing().then(data => {
        let image = ImageSource.fromNativeSource(data);
        let headers = new Headers({ "Content-Type": "application/json" });
        let options = new RequestOptions({ headers: headers });
        let body = {
            "signature": image.toBase64String("png", 70)
        };
        this.http.post("http://example.com", JSON.stringify(body), options)
            .subscribe(result => {
                // Uploaded signature as a base64 string
            }, error => {
                console.dir(error);
            });
    }, error => {
        console.dir(error);
    });
}

First we get the signature data within the drawing pad. The data isn't in a format that is particularly useful to us so instead we'll convert it into a NativeScript ImageSource object.

The goal here is to send the signature to some remote web server and let the server handle it from there. To do a POST request we're going to need to define the header information as well as the request body. The simplest way to send the signature is to first convert it into a base64 encoded string.

Want to save this base64 encoded string into a database? Check out a previous article I wrote on the topic, "Save Captured Images in a NativeScript Angular Application to Couchbase".

Conclusion

You just saw how to accept user signatures within your Android and iOS application built with NativeScript and Angular. This is useful if you're creating an application that needs extra authorization from your user, such as, but not limited to, payment applications, real estate applications, or whatever else you can think up.

Comments