Simple Login And Registration In A NativeScript With Angular Mobile App

Most, not all, mobile applications have some sense of users having accounts. This means that users need to register and sign into most mobile applications to get all the features the application has to offer. So how do you create an attractive and functional login and registration screen for your Android and iOS application?

We’re going to see how to create a registration and login screen with NativeScript and Angular that will get you to an end result of a protected page within your application.

In most scenarios the login and registration aspect will be dependent on some remote web service. In our example we’re going to step over the remote aspect, but maintain the same realistic flow.

Take the above animated image for example.

Before we try to sign into the application we navigate to a registration page and provide various information. In our example the information is stored within key-value localstorage. When back at the login screen we can sign into the application using the stored information.

The application will remain signed in until we choose to log out and the protected page won’t be accessible unless we are signed in.

So what is necessary to make this example a reality?

Create a NativeScript with Angular Project

To keep things easy to understand, we’re going to create a new NativeScript project that uses Angular and TypeScript. With the NativeScript CLI installed and configured, execute the following:

tns create login-project --ng

The --ng flag in the above command indicates that we are going to be using Angular rather than NativeScript Core.

To make the project a bit more attractive, we are going to include some rich notifications based on form actions. While you could use dialogs, fancy alerts, or Toast notifications, we’re going to be using Snackbar notifications.

Execute the following to install the NativeScript Snackbar plugin into your project:

tns plugin add nativescript-snackbar

At this point we can start developing our project.

Create a Login Screen as the Default Application Screen

The first screen your user is going to see when they sign into their application will be a screen asking them to sign in. This should give us a starting point for things to come.

If you’re on a Mac or Linux computer, execute the following commands:

mkdir -p app/components/login touch app/components/login/login.component.ts touch app/components/login/login.component.html

The above commands will create the essentials for the login component. If you’re on a Windows machine, create those files and directory however you see fit.

We’re going to start developing this component by working with our TypeScript logic. Open the project’s app/components/login/login.component.ts file and include the following code:

import { Component, OnInit } from "@angular/core"; import { RouterExtensions } from "nativescript-angular/router"; import { SnackBar } from "nativescript-snackbar"; import * as ApplicationSettings from "application-settings"; @Component({ moduleId: module.id, selector: "ns-login", templateUrl: "login.component.html", }) export class LoginComponent implements OnInit { public input: any; public constructor(private router: RouterExtensions) { this.input = { "email": "", "password": "" } } public ngOnInit() { if(ApplicationSettings.getBoolean("authenticated", false)) { this.router.navigate(["/secure"], { clearHistory: true }); } } public login() { if(this.input.email && this.input.password) { let account = JSON.parse(ApplicationSettings.getString("account", "{}")); if(this.input.email == account.email && this.input.password == account.password) { ApplicationSettings.setBoolean("authenticated", true); this.router.navigate(["/secure"], { clearHistory: true }); } else { (new SnackBar()).simple("Incorrect Credentials!"); } } else { (new SnackBar()).simple("All Fields Required!"); } } }

There is quite a bit happening in the above code so we’re going to break it down.

While we haven’t created our UI yet, we know there will be two input fields. These fields must be initialized in the constructor method and we’re going to make them part of the public input object.

If we’re already signed in, we want to be able to bypass the login screen:

public ngOnInit() { if(ApplicationSettings.getBoolean("authenticated", false)) { this.router.navigate(["/secure"], { clearHistory: true }); } }

During the OnInit lifecycle event, we check local storage and if authenticated is true, we navigate to our protected page and clear the navigation history. This prevents the back button from taking us back to the login screen.

If the user chooses to login, our login function is executed:

public login() { if(this.input.email && this.input.password) { let account = JSON.parse(ApplicationSettings.getString("account", "{}")); if(this.input.email == account.email && this.input.password == account.password) { ApplicationSettings.setBoolean("authenticated", true); this.router.navigate(["/secure"], { clearHistory: true }); } else { (new SnackBar()).simple("Incorrect Credentials!"); } } else { (new SnackBar()).simple("All Fields Required!"); } }

As long as both input fields have data we can obtain the account information from our local storage. Then we can compare the user input against what was stored. If everything matches, we can set authenticated to true and navigate to the protected page. Otherwise we’ll show our Snackbar.

In a realistic example, you’ll be doing an HTTP with the credentials and navigating based on the response.

Now let’s look at the HTML that goes with our TypeScript logic. Open the project’s app/components/login/login.component.html file and include the following markup:

<ActionBar title="{N} Login Example"></ActionBar> <GridLayout backgroundColor="#CCCCCC"> <ScrollView> <StackLayout margin="10" verticalAlignment="center"> <StackLayout class="form" padding="15" backgroundColor="#FFFFFF"> <StackLayout class="input-field"> <Label text="Email" class="label font-weight-bold m-b-5"></Label> <TextField class="input" [(ngModel)]="input.email"></TextField> <StackLayout class="hr-light"></StackLayout> </StackLayout> <StackLayout class="input-field"> <Label text="Password" class="label font-weight-bold m-b-5"></Label> <TextField secure="true" class="input" [(ngModel)]="input.password"></TextField> <StackLayout class="hr-light"></StackLayout> </StackLayout> <Button class="btn btn-primary w-full" text="Login" (tap)="login()"></Button> <Label [nsRouterLink]="['/register']" text="Not a member? Register here." class="text-center footnote"></Label> </StackLayout> </StackLayout> </ScrollView> </GridLayout>

Using NativeScript XML markup we can create a center positioned form with two input elements, a button, and a label that also acts as a button.

Each input field is bound to the TypeScript logic using the [(ngModel)] Angular attribute. The button will call the login function and the label will navigate us directly to the registration page.

Create a Registration Screen for New Accounts

The next screen the user will see in our flow will be the registration screen. The user won’t get very far until they have an account saved within the application.

From the Terminal, execute the following to create a registration component:

mkdir -p app/components/register touch app/components/register/register.component.ts touch app/components/register/register.component.html

Just like with the login component, if you’re not using Mac or Linux, create those files and directory however you see fit.

The registration screen will follow the same rules as the login rules. Open the project’s app/components/register/register.component.ts file and include the following TypeScript code:

import { Component } from "@angular/core"; import { Location } from "@angular/common"; import { SnackBar } from "nativescript-snackbar"; import * as ApplicationSettings from "application-settings"; @Component({ moduleId: module.id, selector: "ns-register", templateUrl: "register.component.html", }) export class RegisterComponent { public input: any; public constructor(private location: Location) { this.input = { "firstname": "", "lastname": "", "email": "", "password": "" } } public register() { if(this.input.firstname && this.input.lastname && this.input.email && this.input.password) { ApplicationSettings.setString("account", JSON.stringify(this.input)); this.location.back(); } else { (new SnackBar()).simple("All Fields Required!"); } } public goBack() { this.location.back(); } }

In the register function we check to make sure all the fields are filled, but this time instead of comparing against previously stored information, we are just serializing the input object and storing it.

If you’re interested in validating any of the form fields or enforcing a certain password strength, consider checking out a previous tutorial I wrote titled, Test Password Strength Using Regular Expressions in a NativeScript Mobile Application.

The NativeScript XML that goes with this logic is similar to what we’ve already seen. Open the project’s app/components/register/register.component.html file and include the following HTML markup:

<ActionBar title="{N} Login Example"> <NavigationButton text="Back" ios.position="left"></NavigationButton> </ActionBar> <GridLayout backgroundColor="#CCCCCC"> <ScrollView> <StackLayout margin="10" verticalAlignment="center"> <StackLayout class="form" padding="15" backgroundColor="#FFFFFF"> <StackLayout class="input-field"> <Label text="First Name" class="label font-weight-bold m-b-5"></Label> <TextField class="input" [(ngModel)]="input.firstname"></TextField> <StackLayout class="hr-light"></StackLayout> </StackLayout> <StackLayout class="input-field"> <Label text="Last Name" class="label font-weight-bold m-b-5"></Label> <TextField class="input" [(ngModel)]="input.lastname"></TextField> <StackLayout class="hr-light"></StackLayout> </StackLayout> <StackLayout class="input-field"> <Label text="Email" class="label font-weight-bold m-b-5"></Label> <TextField class="input" [(ngModel)]="input.email"></TextField> <StackLayout class="hr-light"></StackLayout> </StackLayout> <StackLayout class="input-field"> <Label text="Password" class="label font-weight-bold m-b-5"></Label> <TextField secure="true" class="input" [(ngModel)]="input.password"></TextField> <StackLayout class="hr-light"></StackLayout> </StackLayout> <Button class="btn btn-primary w-full" text="Register" (tap)="register()"></Button> <Label (tap)="goBack()" text="Already a member? Login here." class="text-center footnote"></Label> </StackLayout> </StackLayout> </ScrollView> </GridLayout>

Everything we see in the above XML was in the XML from the login screen. We just swapped out some text, added some more fields, and changed the functions up. Nothing major happened.

Now we can focus on our destination page.

Create a Secure Screen that Requires Authentication

For simplicity we want to create a very basic protected page with the emphasis that it is still protected. The goal is to bounce people away if they are not truly authenticated.

Create the following directory and files to represent the Angular component:

mkdir -p app/components/secure touch app/components/secure/secure.component.ts touch app/components/secure/secure.component.html

If you’re using Windows, create the above however you want.

The most complex part of this screen will be the TypeScript logic. Open the project’s app/components/secure/secure.component.ts file and include the following code:

import { Component, OnInit } from "@angular/core"; import { RouterExtensions } from "nativescript-angular/router"; import * as ApplicationSettings from "application-settings"; @Component({ moduleId: module.id, selector: "ns-secure", templateUrl: "secure.component.html", }) export class SecureComponent implements OnInit { public constructor(private router: RouterExtensions) { } public ngOnInit() { if(!ApplicationSettings.getBoolean("authenticated", false)) { this.router.navigate(["/login"], { clearHistory: true }); } } public logout() { ApplicationSettings.remove("authenticated"); this.router.navigate(["/login"], { clearHistory: true }); } }

During the page’s OnInit lifecycle event, we check to make sure we are authenticated. If we are not authenticated then we navigate back to the login screen and clear the navigation history so we can’t hit the back button.

Since the secure page will always be loaded if we are authenticated, the only way to get back to the other pages will be to clear our session and logout.

Now open the project’s app/components/secure/secure.component.html file and include the following HTML markup:

<ActionBar title="{N} Login Example"> <ActionItem text="Logout" ios.position="true" (tap)="logout()"></ActionItem> </ActionBar> <GridLayout> <Label text="Welcome to the secure page!" class="text-center"></Label> </GridLayout>

The above XML is simple. We only have an actionbar with logout button and some text. The only point we were proving is that we could make a particular screen protected behind login.

Bringing Everything Together

Each of the application pages are ready to go, but they haven’t been connected via the Angular routing system. We need to update our routes and the @NgModule block of our project.

Open the project’s app/app.routing.ts file and include the following:

import { NgModule } from "@angular/core"; import { NativeScriptRouterModule } from "nativescript-angular/router"; import { Routes } from "@angular/router"; import { LoginComponent } from "./components/login/login.component"; import { RegisterComponent } from "./components/register/register.component"; import { SecureComponent } from "./components/secure/secure.component"; const routes: Routes = [ { path: "", redirectTo: "/login", pathMatch: "full" }, { path: "login", component: LoginComponent }, { path: "register", component: RegisterComponent }, { path: "secure", component: SecureComponent } ]; @NgModule({ imports: [NativeScriptRouterModule.forRoot(routes)], exports: [NativeScriptRouterModule] }) export class AppRoutingModule { }

Much of the above is boilerplate code, created with the NativeScript CLI. What we’re actually doing is importing our components and adding them to the routes array.

After the routes are defined, we need to declare them in the @NgModule block of the project. Open the project’s app/app.module.ts file and include the following TypeScript:

import { NgModule, NO_ERRORS_SCHEMA } from "@angular/core"; import { NativeScriptModule } from "nativescript-angular/nativescript.module"; import { NativeScriptFormsModule } from "nativescript-angular/forms"; import { AppRoutingModule } from "./app.routing"; import { AppComponent } from "./app.component"; import { LoginComponent } from "./components/login/login.component"; import { RegisterComponent } from "./components/register/register.component"; import { SecureComponent } from "./components/secure/secure.component"; @NgModule({ bootstrap: [ AppComponent ], imports: [ NativeScriptModule, NativeScriptFormsModule, AppRoutingModule ], declarations: [ AppComponent, LoginComponent, RegisterComponent, SecureComponent ], providers: [], schemas: [ NO_ERRORS_SCHEMA ] }) export class AppModule { }

Again, much of the above is boilerplate code. We’re only importing our components and adding them to the declarations array of the @NgModule block.

At this point the project can be run on both Android and iOS.

Conclusion

You just saw how to implement a login and registration screen that sits in front of some protected content within your NativeScript with Angular mobile application. While we didn’t register and authenticate with a remote web service, the general concepts are in place for designing our screens, validating our data, and navigating around.

To authenticate with a remote web service, consider checking out a previous tutorial I wrote titled, Authenticate with JWT in a NativeScript Angular Mobile Application.

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.