I’m a huge fan of mobile Android and iOS application development and an even bigger fan of using Angular to develop those applications. A while back I wrote about using Couchbase in a NativeScript application as well as using Couchbase in a NativeScript Angular application. Both were very simple todo-list scenarios.

We’re going to take a step back and break down what Couchbase can do in your mobile application piece by piece. For example, Couchbase is a document database that can be managed as key-value or by the document properties. In this example we’re going to work with keys and values to save and load imaginary profile data.

In the above example we can add a first an last name and choose from a list of available profile avatars. When saving, the data is stored in Couchbase, and the screen is reset. When loading, the data is loaded via the previously saved key.

The Requirements

This project has a few basic requirements in order to function:

NativeScript CLI 2.0+

Android SDK and / or Xcode

The NativeScript CLI, obtainable through the Node Package Manager (NPM) is required for creating and building projects. To complete the building process, either the Android SDK or Xcode is required, depending on which platform you wish to build for.

Starting a New NativeScript with Angular Project

To keep things easy to understand, we’re going to create a fresh NativeScript with Angular project for Android and iOS. From the command line, execute the following:

tns create ProfileProject --ng cd ProfileProject tns platform add ios tns platform add android 1 2 3 4 tns create ProfileProject -- ng cd ProfileProject tns platform add ios tns platform add android

If you’re not using a Mac with Xcode installed, you can only build for the Android platform. iOS is a Mac only thing.

This project will be using Couchbase, so we must install the nativescript-couchbase plugin. From the CLI, execute:

tns plugin add nativescript-couchbase 1 tns plugin add nativescript - couchbase

The above command will install the Couchbase plugin for whatever platforms are available in the project. At this point we are ready for the development of our application.

Defining the Core Application Logic

The project, while single page, will have two core pieces of application logic. There will be the logic that appears on the default page and the logic that appears in the modal dialog. We’re going to start with the logic that appears in the default page.

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

import { Component, ViewContainerRef } from "@angular/core"; import { ModalDialogService } from "nativescript-angular/directives/dialogs"; import { Couchbase } from "nativescript-couchbase"; import { ModalComponent } from "./app.modal"; @Component({ selector: "my-app", templateUrl: "app.component.html", }) export class AppComponent { public profile: any; private database: any; public constructor(private modal: ModalDialogService, private vcRef: ViewContainerRef) { } public showModal(fullscreen: boolean) { } public save() { } public load() { } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import { Component , ViewContainerRef } from "@angular/core" ; import { ModalDialogService } from "nativescript-angular/directives/dialogs" ; import { Couchbase } from "nativescript-couchbase" ; import { ModalComponent } from "./app.modal" ; @ Component ( { selector : "my-app" , templateUrl : "app.component.html" , } ) export class AppComponent { public profile : any ; private database : any ; public constructor ( private modal : ModalDialogService , private vcRef : ViewContainerRef ) { } public showModal ( fullscreen : boolean ) { } public save ( ) { } public load ( ) { } }

In the above code we are importing various Angular components, the NativeScript modal service, Couchbase, and the soon to be created modal dialog.

Within the AppComponent class there is a public and private variable. The private database variable will hold the open Couchbase instance and the public profile variable will hold information such as person name and profile picture.

The constructor method will inject both the ModalDialogService and ViewContainerRef services to be used throughout the page.

public constructor(private modal: ModalDialogService, private vcRef: ViewContainerRef) { this.profile = { photo: "~/kitten1.jpg", firstname: "", lastname: "" } this.database = new Couchbase("data"); } 1 2 3 4 5 6 7 8 public constructor ( private modal : ModalDialogService , private vcRef : ViewContainerRef ) { this . profile = { photo : "~/kitten1.jpg" , firstname : "" , lastname : "" } this . database = new Couchbase ( "data" ) ; }

In addition we’ll be initializing the profile variable and opening a database called data . You might notice ~/kitten1.jpg and be wondering what it is.

In this application we have a few avatars available to us called kitten1.jpg, kitten2.jpg, and kitten3.jpg all found within the project’s app directory.

I didn’t take these pictures, but instead found them on the internet. Feel free to use whatever images you’d like for this example.

The profile.photo property will hold the path to the photo we wish to use.

public save() { let document = this.database.getDocument("mydockey"); if(document) { this.database.updateDocument("mydockey", this.profile); } else { this.database.createDocument(this.profile, 'mydockey'); } this.profile = { photo: "~/kitten1.jpg", firstname: "", lastname: "" } } 1 2 3 4 5 6 7 8 9 10 11 12 13 public save ( ) { let document = this . database . getDocument ( "mydockey" ) ; if ( document ) { this . database . updateDocument ( "mydockey" , this . profile ) ; } else { this . database . createDocument ( this . profile , 'mydockey' ) ; } this . profile = { photo : "~/kitten1.jpg" , firstname : "" , lastname : "" } }

When we wish to save to Couchbase, we first want to see if the document already exists in Couchbase. Remember this is a single page single profile application. If the document exists we need to update the document with whatever is in the profile variable, otherwise create it. Once saved we can reset the form.

public load() { this.profile = this.database.getDocument("mydockey"); } 1 2 3 public load ( ) { this . profile = this . database . getDocument ( "mydockey" ) ; }

Since we know the name of the document key, we can retrieve the document when we wish to reload the form. The data stored and the data retrieved are both in JSON format, which is great for NativeScript applications.

Finally we have the showModal method:

public showModal(fullscreen: boolean) { let options = { context: { promptMsg: "Pick your avatar!" }, fullscreen: fullscreen, viewContainerRef: this.vcRef }; this.modal.showModal(ModalComponent, options).then((res: string) => { this.profile.photo = res || "~/kitten1.jpg"; }); } 1 2 3 4 5 6 7 8 9 10 public showModal ( fullscreen : boolean ) { let options = { context : { promptMsg : "Pick your avatar!" } , fullscreen : fullscreen , viewContainerRef : this . vcRef } ; this . modal . showModal ( ModalComponent , options ) . then ( ( res : string ) = > { this . profile . photo = res | | "~/kitten1.jpg" ; } ) ; }

The above is a variation of something I found in the NativeScript documentation. When called, it will launch the soon to be created ModalComponent with various options. When the modal is closed, the value returned will be loaded into the profile.photo because the value returned should be an image path.

Now how do we create that modal dialog?

Creating a Modal Dialog for Image Selection

Creating a modal is pretty much the same as creating a NativeScript page. However, other bootstrapping must be done to prove that it is a modal and not a page.

Create a file app/app.modal.ts and include the following code:

import { Component, Input } from "@angular/core"; import { ModalDialogParams } from "nativescript-angular/directives/dialogs"; @Component({ selector: "my-modal", template: ` <StackLayout margin="24" horizontalAlignment="center" verticalAlignment="center"> <Label [text]="prompt"></Label> <StackLayout orientation="horizontal" marginTop="12"> <Image src="~/kitten1.jpg" width="75" height="75" (tap)="close('~/kitten1.jpg')"></Image> <Image src="~/kitten2.jpg" width="75" height="75" (tap)="close('~/kitten2.jpg')"></Image> <Image src="~/kitten3.jpg" width="75" height="75" (tap)="close('~/kitten3.jpg')"></Image> </StackLayout> </StackLayout> `, }) export class ModalComponent { @Input() public prompt: string; constructor(private params: ModalDialogParams) { this.prompt = params.context.promptMsg; } public close(res: string) { this.params.closeCallback(res); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 import { Component , Input } from "@angular/core" ; import { ModalDialogParams } from "nativescript-angular/directives/dialogs" ; @ Component ( { selector : "my-modal" , template : ` < StackLayout margin = "24" horizontalAlignment = "center" verticalAlignment = "center" > < Label [ text ] = "prompt" > < / Label > < StackLayout orientation = "horizontal" marginTop = "12" > < Image src = "~/kitten1.jpg" width = "75" height = "75" ( tap ) = "close('~/kitten1.jpg')" > < / Image > < Image src = "~/kitten2.jpg" width = "75" height = "75" ( tap ) = "close('~/kitten2.jpg')" > < / Image > < Image src = "~/kitten3.jpg" width = "75" height = "75" ( tap ) = "close('~/kitten3.jpg')" > < / Image > < / StackLayout > < / StackLayout > ` , } ) export class ModalComponent { @ Input ( ) public prompt : string ; constructor ( private params : ModalDialogParams ) { this . prompt = params . context . promptMsg ; } public close ( res : string ) { this . params . closeCallback ( res ) ; } }



You’ll notice that I’m using template instead of templateUrl here. It is because I got lazy and didn’t want to create another HTML file. The template has three images and a tap event for each image. When tapped, the close method will be called passing the value to the previous page.

Before the modal can be used, it must be bootstrapped in the project’s app/app.module.ts file:

import { NgModule, NO_ERRORS_SCHEMA } from "@angular/core"; import { NativeScriptModule } from "nativescript-angular/platform"; import { NativeScriptFormsModule } from "nativescript-angular/forms"; import { ModalDialogService } from "nativescript-angular/modal-dialog"; import { AppComponent } from "./app.component"; import { ModalComponent } from "./app.modal"; @NgModule({ declarations: [AppComponent, ModalComponent], entryComponents: [ModalComponent], bootstrap: [AppComponent], imports: [NativeScriptModule, NativeScriptFormsModule], providers: [ModalDialogService], schemas: [NO_ERRORS_SCHEMA] }) export class AppModule { } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import { NgModule , NO_ERRORS_SCHEMA } from "@angular/core" ; import { NativeScriptModule } from "nativescript-angular/platform" ; import { NativeScriptFormsModule } from "nativescript-angular/forms" ; import { ModalDialogService } from "nativescript-angular/modal-dialog" ; import { AppComponent } from "./app.component" ; import { ModalComponent } from "./app.modal" ; @ NgModule ( { declarations : [ AppComponent , ModalComponent ] , entryComponents : [ ModalComponent ] , bootstrap : [ AppComponent ] , imports : [ NativeScriptModule , NativeScriptFormsModule ] , providers : [ ModalDialogService ] , schemas : [ NO_ERRORS_SCHEMA ] } ) export class AppModule { }

Notice in the above we are importing the ModalDialogService as well as the ModalComponent ? We are injecting both into the @NgModule block.

Now the modal can be launched within the application.

Developing the Core Application UI

So how about the UI for the default NativeScript application page? Open the project’s app/app.component.html file and include the following XML markup:

<ActionBar title="{N} Profile"> <ActionItem text="Load" ios.position="right" (tap)="load()"></ActionItem> </ActionBar> <GridLayout rows="*, *" cols="*"> <Image [src]="profile.photo" (tap)="showModal(true)" width="150" height="150" class="img-rounded" row="0" col="0"></Image> <StackLayout class="form" row="1" col="0"> <StackLayout class="input-field"> <Label text="First Name" class="label"></Label> <TextField [(ngModel)]="profile.firstname" class="input input-border"></TextField> </StackLayout> <StackLayout class="input-field"> <Label text="Last Name" class="label"></Label> <TextField [(ngModel)]="profile.lastname" class="input input-border"></TextField> </StackLayout> <StackLayout class="input-field"> <Button text="Save" (tap)="save()" class="btn btn-primary w-full"></Button> </StackLayout> </StackLayout> </GridLayout> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 < ActionBar title = "{N} Profile" > < ActionItem text = "Load" ios . position = "right" ( tap ) = "load()" > < / ActionItem > < / ActionBar > < GridLayout rows = "*, *" cols = "*" > < Image [ src ] = "profile.photo" ( tap ) = "showModal(true)" width = "150" height = "150" class = "img-rounded" row = "0" col = "0" > < / Image > < StackLayout class = "form" row = "1" col = "0" > < StackLayout class = "input-field" > < Label text = "First Name" class = "label" > < / Label > < TextField [ ( ngModel ) ] = "profile.firstname" class = "input input-border" > < / TextField > < / StackLayout > < StackLayout class = "input-field" > < Label text = "Last Name" class = "label" > < / Label > < TextField [ ( ngModel ) ] = "profile.lastname" class = "input input-border" > < / TextField > < / StackLayout > < StackLayout class = "input-field" > < Button text = "Save" ( tap ) = "save()" class = "btn btn-primary w-full" > < / Button > < / StackLayout > < / StackLayout > < / GridLayout >



In the above layout we have an action bar and content split into two rows. The action bar has a button, that when pressed, will call the load method in our TypeScript code.

The tag will load the photo stored in the path of the profile.photo variable. When tapped the modal will launch to allow us to choose a new avatar.

The second row of the UI has two input fields and a button. The input fields are bound using the Angular [(ngModel)] tags which allow data to be shared between the XML and the TypeScript. When the button button is pressed, the save method will be triggered, saving the data into Couchbase.

Conclusion

You just saw how to use Couchbase as key-value storage within a NativeScript Android and iOS application built with Angular. Next time we’ll see how to save more than one document in Couchbase and query for those documents making it much more powerful than just key-value storage.