ToDo Mobile App using NativeScript and Angular
Tags: nativescriptangular, mobileapp
Published: 2019-07-13 09:05:11

NativeScript is an open source cross-platform and hybrid mobile framework for building native iOS and Android mobile apps with Angular, Vue.js, TypeScript, or JavaScript. 

The goal of this post is to build and run a simple NativeScript Angular application in TypeScript, using the Angular CLI. 

You can download the source code for the NativeScript Angular Todo mobile app from GitHub repository [nativescript-todo-app](https://github.com/sanjay-patel/nativescript-todo-app)

**1. GETTING STARTED WITH NATIVESCRIPT**

You need to set up your development environment before you can do anything.

Install [Node.js® and npm](https://nodejs.org/en/download/) if they are not already on your machine.

If you don’t have NativeScript installed you can install it by installing Node.js.

To install NativeScript, run:

`$ npm install -g nativescript`

The final step is to install the development tool for each platform that you want to deploy to. For Android, this is the Android SDK. For iOS, it’s XCode. You can follow the [installation guide](http://docs.nativescript.org/angular/start/quick-setup) on the NativeScript website for more detailed instructions on how to set up the necessary software for your development environment.

Once you’ve set up your environment, execute:

`$ tns doctor`

**2. CREATE A NEW APP FOR ANDROID AND IOS**

Before we can start coding we need to create a new project. This can be done by executing the following from the Command Prompt (Windows) or Terminal (Linux and Mac):

`$ tns create TodoApp $ cd TodoApp`
`$ tns platform add android $ tns platform add ios` (If you're not using a Mac, you cannot add and build for the iOS platform.)

**3. INSTALL THE NECESSARY NATIVE PLUGINS**

[RadSideDrawer](http://docs.telerik.com/devtools/nativescript-ui/Controls/Angular/SideDrawer/overview) is a component can show a hidden view that contains navigation UI or common settings. A popular application that uses the drawer UI is the Android Play store app.

[RadListView](http://docs.telerik.com/devtools/nativescript-ui/Controls/Angular/ListView/overview) for NativeScript is a virtualizing list component that provides the most popular features associated with scenarios where a list of items is used. All these features are embedded in one control with the idea to save developer time and provide a better experience. The control’s main features include:

*item animations
*different layouts and orientations
*swipe, reorder, drag, refreshing the list on swipe or loading more items only when needed.

[RadDataForm](https://docs.nativescript.org/ui/professional-ui-components/DataForm/dataform-overview) plugin provides an easy and versatile approach for building mobile forms based on a provided data object’s public members. Use DataForm to:

*Bind a form to a data object with a single line of code
*Take advantage of more than 15 built-in editors (or provide your own custom editor)
*Create groups of editors, allow them to be collapsed and style them
*Display a form in ReadOnly mode
*Take control over collected data with built-in data validation – use a provided validator or create a custom one

The application we are creating will be making use of persisted storage, that will save our data even after our application has been closed. After all, we don’t want to lose our todo list data after we restart our application.

In this application, we are use the [NativeScript SQLite](https://www.npmjs.com/package/nativescript-sqlite). This can be done by executing the following from the Command Prompt (Windows) or Terminal (Linux and Mac):

`$ tns plugin add nativescript-ui-sidedrawer $ tns plugin add nativescript-ui-listview $ tns plugin add nativescript-ui-dataform $ tns plugin add nativescript-sqlite`

**4. CONFIGURING THE ANGULAR ROUTER**

**src/app/app-routing.module.ts**

import { NgModule } from '@angular/core';
import { Routes } from '@angular/router';
import { NativeScriptRouterModule } from 'nativescript-angular/router';

const routes: Routes = [
  {
    path: '',
    redirectTo: 'todo-list',
    pathMatch: 'full'
  },  
  {
    path: 'todo-list',
    loadChildren: './todo-list/todo-list.module#TodoListModule'
  },    
  {
    path: 'todo-detail',
    loadChildren: './todo-detail/todo-detail.module#TodoDetailModule'
  }
];

@NgModule({
  imports: [NativeScriptRouterModule.forRoot(routes)],
  exports: [NativeScriptRouterModule],
})
export class AppRoutingModule { }`

**5. CREATE TODOLISTMODULE FEATURE MODULE**

**src/app/todo-list/todo-list.module.ts**

import { NgModule, NO_ERRORS_SCHEMA} from '@angular/core';
import { NativeScriptCommonModule } from 'nativescript-angular/common';
import { NativeScriptUIListViewModule } from 'nativescript-ui-listview/angular';
import { TodoListRoutingModule } from './todo-list-routing.module';
import { TodoListComponent } from './todo-list.component';
import { TodoService } from '../services/todo.service';

@NgModule({
  imports: [
    NativeScriptCommonModule, 
    TodoListRoutingModule],
    NativeScriptUIListViewModule
  ],
  declarations: [TodoListComponent],
  schemas: [NO_ERRORS_SCHEMA],
  providers: [TodoService]
})
export class TodoListModule { }

**6. CONFIGURE THE FEATURE MODULE’S ROUTES**

**src/app/todo-list/todo-list-routing.module.ts**


import { NgModule } from '@angular/core';
import { Routes } from '@angular/router';
import { NativeScriptRouterModule } from 'nativescript-angular/router';
import { TodoListComponent } from './todo-list.component';

const routes: Routes = [
  {
    path: '',
    component: 'TodoListComponent'
  }
];

@NgModule({
  imports: [NativeScriptRouterModule.forRoot(routes)],
  exports: [NativeScriptRouterModule]  
})
export class TodoListRoutingModule { }

**7. CREATING THE TODOLIST COMPONENT**

**src/app/todo-list/todo-list.component.ts**

import { Component, OnInit, ViewChild } from '@angular/core';
import { RouterExtensions } from 'nativescript-angular/router';
import { ObservableArray } from 'tns-core-modules/data/observable-array';
import { DataItem } from './dataItem';
import * as app from 'application';
import { RadSideDrawer } from 'nativescript-ui-sidedrawer';
import { TodoService } from '../services/todo.service';
import { ListViewEventData, RadListView } from 'nativescript-ui-listview';
import { RadListViewComponent } from 'nativescript-ui-listview/angular';
import { View } from 'tns-core-modules/ui/core/view';
import * as dialogs from 'ui/dialogs';

@Component({
  selector: 'todo-list',
  moduleId: module.id,
  templateUrl: './todo-list.component.html',
  styleUrls: ['todo-list.component.css']
})

export class TodoListComponent implements OnInit {
  private _dataItems: ObservableArray<DataItem>;
  public data: DataItem[] = [];
  @ViewChild('myListView') listViewComponent: RadListViewComponent;

  constructor( private _routerExtensions: RouterExtensions , private todoService: TodoService ) { }

  get dataItems(): ObservableArray<DataItem> {
    return this._dataItems;
  }

  ngOnInit(): void {
    this.loadTodoList();
  }

  loadTodoList() {
    this.todoService.fetch().then((res) => {
     this._dataItems = new ObservableArray(res);
    });
  }

  onDrawerButtonTap(): void {
    const sideDrawer = <RadSideDrawer>app.getRootView();
    sideDrawer.showDrawer();
  }

  create(): void {
    this._routerExtensions.navigate(['/todo-detail'],
    {
      animated: true,
      transition: {
        name:'slide',
        duration: 200,
        curve:'ease'
      }
    });
  }

  public onCellSwiping(args :ListViewEventData) {
    var swipeLimits = args.data.swipeLimits;
    var currentItemView = args.object;
    var currentView;

    if (args.data.x > 200) {
      console.log('Notify perform left action');
    } else if (args.data.x < -200) {
      console.log('Notify perform right action');
    }
  }

  public onSwipeCellStarted(args :ListViewEventData) {
    var swipeLimits = args.data.swipeLimits;
    var swipeView = args['object'];
    var leftItem = swipeView.getViewById<View>('edit-view');
    var rightItem = swipeView.getViewById<View>('delete-view');
    swipeLimits.left = leftItem.getMeasuredWidth();
    swipeLimits.right = rightItem.getMeasuredWidth();
    swipeLimits.threshold = leftItem.getMeasuredWidth() / 2;
  }

  public onLeftSwipeClick(args :ListViewEventData) {  
    var id = args.object.bindingContext.id;
    this.listViewComponent.listView.notifySwipeToExecuteFinished();  
    this._routerExtensions.navigate(['/todo-detail'],
    {
      queryParams:{id: id },
      animated: true,
      transition: {
        name:'slide',
        duration: 200,
        curve:'ease'
      }
    });
  }

  public onRightSwipeClick(args) {
    var id = args.object.bindingContext.id;
    dialogs.confirm({
      title: 'Confirm Delete',
      message: 'Are you sure you want to delete this item?',
      okButtonText: 'Ok',
      cancelButtonText: 'Cancel'
    }).then(result => {
      if (result) {
        this.todoService.deleteRecord(id).then((res) => {
          if (res.success) {
            this._dataItems.splice(this._dataItems.indexOf(args.object.bindingContext), 1);
          }
        });
      }
    });  
  }
}  

**8. RUNNING AND DEBUGGING THE APP**

You can run the app on your device by executing:

`$ tns run`

and then the platform where you want to deploy. Here’s an example for android:

`$ tns run android`

This automatically installs the Android platform for you if it hasn’t already been installed and then runs the app on your [android device](https://developer.android.com/studio/install) once it’s installed.

Once the app is running, you can execute tns livesync android –watch to automatically refresh the app every time you make changes to the source files.

NativeScript allows developers to debug their app. This is done with the Chrome dev tools. There are two ways of doing this:

If have an app already running, you can open a new terminal window and execute
`$ tns debug android --start`
If you don’t have an app running yet, use
`$ tns debug android --debug-brk`