Build A Simple Mobile App With NativeScript And Angular

I’ve been building NativeScript applications for a while now using vanilla JavaScript and TypeScript. Even though NativeScript has been working with Angular for a while now, I’ve been holding off until it was more stable. Not stable from an Angular perspective, but more stable from a NativeScript and Angular perspective.

A few weeks ago at the AngularJS conference, ng-conf, Telerik announced that NativeScript was in a more compatible state when it comes to Angular. This was all the information I needed to start giving it a try.

We’re going to take a look at building a simple todo-like NativeScript Android and iOS application using Telerik NativeScript and Angular.

Let me start by saying that as of right now, May 2016, Angular is in release candidate. It has not yet become stable. This is just Angular that I’m talking about.

The first thing we should do is create a new NativeScript project. Using the Command Prompt (Windows) or Terminal (Mac and Linux), execute the following:

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

A few important things to note in the above commands. First you’ll notice we are using the --ng tag. This is to use the Angular template for NativeScript. Second, you won’t be able to add and build for the iOS platform unless you’re using a Mac computer. Lastly, being that this is an Angular project, we will be using TypeScript rather than vanilla JavaScript.

Before we start coding, there are a few directories and files that we need to create. These files and directories will represent different screens in our application. Create the following in the AngularProject directory:

mkdir app/components mkdir app/components/create mkdir app/components/list touch app/components/create/create.component.ts touch app/components/create/create.component.html touch app/components/list/list.component.ts touch app/components/list/list.component.html

Based on the above, we’ll have a screen that contains a list of data and a screen that contains a form for creating data. These screens will further be referred to as components.

Creating the List Component

The list component will consist of a TypeScript logic file and an HTML file for UI. We’re going to start by creating the logic file, working our way to the UI.

Open the project’s app/components/list/list.component.ts file and include the following code. We’re going to break it down after.

import {Component} from "@angular/core"; import {Location} from "@angular/common"; import {Router} from "@angular/router"; import * as applicationSettings from "application-settings"; @Component({ selector: "list", templateUrl: "./components/list/list.component.html", }) export class ListComponent { router: Router; personList: Array<Object>; constructor(router: Router, location: Location) { this.router = router; this.personList = JSON.parse(applicationSettings.getString("people", "[]")); location.subscribe((path) => { this.personList = JSON.parse(applicationSettings.getString("people", "[]")); }); } create() { this.router.navigate(["/create"]); } }

Starting with the imports, we include Location which will allow us to track navigations and the Router which will allow us to navigate. For simplicity we are going to use application-settings for storing data. If you wanted, you could easily convert the storage to SQLite or Couchbase NoSQL based on my previous tutorials.

This brings us to the following chunk of code:

@Component({ selector: "list", templateUrl: "./components/list/list.component.html", })

This is where we are defining the UI that is bound to this particular component logic. The UI will exist in an HTML file that we create soon.

Inside the components constructor method we see the following code:

constructor(router: Router, location: Location) { this.router = router; this.personList = JSON.parse(applicationSettings.getString("people", "[]")); location.subscribe((path) => { this.personList = JSON.parse(applicationSettings.getString("people", "[]")); }); }

All data will be stored as serialized JSON data within the application settings storage. When the component loads, the personList will be loaded. This variable is actually bound to the UI, but we’ll get to that soon. What is really interesting here is the subscribe that is happening to the Location object. When an item is popped from the navigation stack, a notification will be sent to this subscription. To sum this up, when the user comes to the list component after hitting back or save, the subscription will trigger and the data can be refreshed.

Finally we have a create function that will allow us to navigate to another component.

This brings us to the UI for the list component. You’ll notice that although we’re working with an HTML file, we are using NativeScript XML markup instead. Inside your project’s app/components/list/list.component.html file, add the following code:

<ActionBar title="Person List"> <ActionItem text="Create" (tap)="create()" ios.position="right"></ActionItem> </ActionBar> <GridLayout> <ListView [items]="personList"> <template let-item="item"> <Label [text]="item.firstname + ' ' + item.lastname"></Label> </template> </ListView> </GridLayout>

An action bar with a single button will exist in this component. The button will navigate us to the next component. Where most of the magic happens is in the <GridLayout> tags.

The <ListView> is bound to the personList that we created and populated in the TypeScript file. Every array item will be displayed in a <Label> within the list rows.

Building the Create Component

Now we can look at creating data to be displayed in our list component. Just like with the list component we’re going to focus on the TypeScript logic first.

Open your project’s app/components/create/create.component.ts file and include the following code. Don’t worry, we’re going break it down after.

import {Component} from "@angular/core"; import {Location} from "@angular/common"; import * as applicationSettings from "application-settings"; @Component({ selector: "create", templateUrl: "./components/create/create.component.html", }) export class CreateComponent { location: Location; firstname: string; lastname: string; constructor(location: Location) { this.location = location; this.firstname = ""; this.lastname = ""; } save() { if(this.firstname != "" && this.lastname != "") { var people: Array<Object> = JSON.parse(applicationSettings.getString("people", "[]")); people.push({firstname: this.firstname, lastname: this.lastname}); applicationSettings.setString("people", JSON.stringify(people)); this.location.back(); } } }

The first few lines aren’t much different than what we saw in the list component. What we are going to focus on are the constructor and the save methods.

constructor(location: Location) { this.location = location; this.firstname = ""; this.lastname = ""; }

In the constructor method we are initializing the variables that are bound to the UI. Most of the work happens in the save method.

save() { if(this.firstname != "" && this.lastname != "") { var people: Array<Object> = JSON.parse(applicationSettings.getString("people", "[]")); people.push({firstname: this.firstname, lastname: this.lastname}); applicationSettings.setString("people", JSON.stringify(people)); this.location.back(); } }

When the user calls the save function we want to make sure that the firstname and lastname properties are not empty. If they are not empty, we retrieve the serialized JSON data and add a new array item to it. After we re-save the data to the application settings storage.

After the data has been saved we can pop the current component from the navigation stack which will take us back to the list component and trigger the location subscription so we can reload the data in the list view.

Now we can look at the UI of the create component. Again this will be an HTML file, but NativeScript XML markup. Open your project’s app/components/create/create.component.html file and add the following code:

<ActionBar title="Create"> <NavigationButton text="Back" android.systemIcon="ic_menu_back"></NavigationButton> <ActionItem text="Save" (tap)="save()" ios.position="right"></ActionItem> </ActionBar> <StackLayout> <TextField hint="First Name" [(ngModel)]="firstname"></TextField> <TextField hint="Last Name" [(ngModel)]="lastname"></TextField> </StackLayout>

This time we have an action bar with two buttons, a button for saving and a button for navigating to the previous component. In our <StackLayout> we have two input fields, both bound with an Angular ngModel . This creates a two way data binding, but for our use-case, it allows us to use the data when we call save .

Configuring the Angular Router

As of right now we have two components, but neither of them are linked to the actual application. Not yet at least. Now we need to configure the Angular router so navigation can happen.

Open your project’s app/app.component.ts file and include the following code. We’ll break down what is happening after.

import {Component} from "@angular/core"; import {NS_ROUTER_DIRECTIVES} from "nativescript-angular/router"; @Component({ selector: "my-app", directives: [NS_ROUTER_DIRECTIVES], template: "<page-router-outlet></page-router-outlet>" }) export class AppComponent { }

Jump down to the @Component section of the file. This particular TypeScript file will act as our main driver, so every component we make will pass through it.

@Component({ selector: "my-app", directives: [NS_ROUTER_DIRECTIVES], template: "<page-router-outlet></page-router-outlet>" })

In the above chunk of code, what is particularly interesting is the template that we set. If you’re familiar with AngularJS 1, the <page-router-outlet> is like the <div ui-view> . All components will be rendered within that tag.

Finally we need to add our possible components and link them together. This is done via the project’s app/main.ts file. Open it and include the following code:

// this import should be first in order to load some required settings (like globals and reflect-metadata) import {nativeScriptBootstrap} from "nativescript-angular/application"; import {nsProvideRouter} from "nativescript-angular/router"; import {RouterConfig} from "@angular/router"; import {AppComponent} from "./app.component"; import {ListComponent} from "./components/list/list.component"; import {CreateComponent} from "./components/create/create.component"; export const AppRoutes: RouterConfig = [ { path: "", component: ListComponent }, { path: "create", component: CreateComponent } ] nativeScriptBootstrap(AppComponent, [[nsProvideRouter(AppRoutes, {})]]);

Here we define the routes, which is default, and their names so we know what to navigate to. The default route is any route that has an empty path.

Conclusion

You just created your first NativeScript Android and iOS application using the Angular JavaScript framework. This is huge because now NativeScript becomes superior to similar Angular frameworks like Ionic 2. This is because of NativeScript being able to build native applications, eliminating a common web view.

Although I chose to application-settings for saving data in this tutorial, you can take a look at my SQLite tutorial or Couchbase NoSQL tutorial and use those instead.

A video version of this article can be seen below.

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.