Sort by:

Create Native Mobile and Web App With Single Codebase Using Angular2 and Nativescript


Creating a performant application for all three platforms Android, iOS and Web with single codebase is an amazing thing that we can do nowadays. I am going to show you how we can create such single codebase cross platform application by combining power of Angular 2 and Nativescript frameworks. Angular2 is a framework which can run on multiple platforms and Nativescript is a framework using which we can write truly native mobile apps for Android and iOS using web technologies. Nativescript provides a very good support of Angular framework. That means we can combine the power of both the great frameworks and create amazing cross-platform apps with single codebase.

If you want to create your own web and native mobile applications with single codebase, I have created a very simple nativescript-angular-web-starter for that. Just clone the repository and you are ready to create your awesome application. Check this preview video of the starter:



Have you noticed how changes in one file is getting reflected in both native mobile app and a web app?

I know in the preview above, it is a very simple application. So if you want to see a bigger application than this, check this Open Source Cross Platform Quotes Application.

You can find the complete source code of this Quotes Application from https://github.com/shripalsoni04/ngxp-quotes-app. This Quotes Application is made up with same architecture as that of the starter mentioned above.

Now, let’s dig into details of how to create such applications.

You may have question, if we are creating truly native mobile app with Nativescript, how can the code of the view layer be the same as web. Well, you are right. Nativescript do not have HTML tags like div, span, h1 etc. and it has its own template tags like Label, GridLayout, ListView etc.. It is very necessary first to understand what all the things can be shared between mobile & web platforms and what can/should not be shared.

What can/should be shared between mobile and web platforms?

Services

Services generally contains core business logic, code related to fetching data from api/web-services, utility functions etc. As all such code do not change based on the view layer, it should be shared among platforms.

Models

Data Models or any Business Entity Models also do not depend on view layer. So such models can also be shared.

Assets

Assets like logo image, images related to business domain, icons can be shared between platforms. Since nativescript has great support of font icons, we can use any font icon library like Font Awesome, Material Icons or your own font icon very easily on both mobile and web platforms.

Style Variables, Functions or Mixins

Nativescript has support of CSS Preprocessors like SASS and LESS. So we can share style variables, common functions etc. across the platforms. This makes changing the brand colors across the platforms very easy.

ViewModels

ViewModels contains logic related to view layer. As nativescript has angular support, we can use template syntax of angular in nativescript templates. So even if rendering engine for mobile and web platforms are different, we can share some code which will be same for the platforms. We will see this later in this tutorial.

Unit Test Cases

As we can share services, models and viewModels among the platforms, we can share their unit test cases also.

What can/should not be shared between mobile and web platforms?

Component Classes, Templates and Styles

As we know, an angular application is made up of a component hierarchy. This components are nothing but a simple ES6 class with some metadata attached to it. This metadata contains mapping of template or styles for a component. As we have different rendering engines for different platforms, it is obvious that we need to create different template and style files for mobile and web platforms.

Component class generally contains logic related to view like DOM/View manipulations, event handlers, event driven animations, view data loading etc. So, some code of the component will be same for all the platforms and some code which is related to animations, DOM/View manipulation etc. will not be the same. Now you may have question, if our component don’t do much view layer manipulation or animations, can’t we use the same component across all the platforms. The answer is yes there are ways to do that but that approach can have two limitations as mentioned below:

  1. As explained above, even if we keep the component class same, we need to have different template and style mappings in metadata of component for mobile and web platforms. That means at runtime we need to switch the metadata based on the platform in which the code is running. This can be accomplished by using CustomComponent decorator instead of using angular’s Component decorator. Unfortunately currently AOT do not work if any CustomComponent decorator is used as mentioned at this issue.

  2. We cannot use any platform specific APIs directly inside the component class because that will not be available when code runs on other platforms. Also, we need to wrap platform specific code inside conditional statements. This may create maintenance issues and complex test cases in long run.

You may have a question now, how can we share some of the component code without the limitations mentioned above? The answer is very simple. We just need to take all the code of a component which can be common across the platforms and put it inside an angular service and use that service inside the components of different platforms. We can call this service a viewModel because it contains code related to view of the component. We will see an example of such viewModel later in this tutorial.

Now, let’s look at implementation part of such application.

Application folder structure to share data between mobile and web app

Now we know what we can and cannot share between mobile and web platforms. Let’s understand what can be the application folder structure which enables us to share data between different platforms.

top-level-folder-structure-for-cross-platform-app

As shown above, the folder structure is very easy to understand.

nativescript folder contains a valid nativescript project created using nativescript-cli. You can execute any valid command of nativescript-cli in this folder.

web folder contains a valid web project created using angular-cli. You can execute any valid command of angular-cli in this folder.

tools folder contains scripts which are useful during development of the application.

x-shared folder contains all the code which can be shared across all the platforms. This folder contains only platform independent code. Just for information, here in folder name x-shared, x refers to Cross. So you can read x-shared as Cross Platform Shared.

This x-shared folder is symlinked in nativescript and web folders as shown below:

web-x-shared-folder

nativescript-x-shared-folder

As x-shared is symlinked, changes done in any file of this folder from any of the three places, will be reflected in other two places. So this provide convenience of editing cross platform shared code from within folder of mobile/web platforms.

Now, let’s examine what should be within x-shared folder.

x-shared-folder-content

The above x-shared folder structure is of Cross Platform Quotes App made with the same architecture.

Here,

app folder contains services, models, viewModels and unit test cases of various modules of the application.

assets folder contains images like logo or any business domain images or any other non-editable things.

styles folder contains style variables, functions or mixins of SASS/LESS.

typings folder contains shared type definitions of any business entity.

How to create and use shared code?

Now, we know the folder structure of such application, let’s see how to create and use such shared code across platform.

In this section, I will use code snippets from Quotes Application as mentioned above. Let’s see how to implement functionality of showing authors’ list in mobile and web applications.

Service and Models

To show authors’ list we need to have a service which fetches data from some remote service. Code of such AuthorService is as shown below.

x-shared/app/author/author.service.ts

import { Injectable } from '@angular/core';
import { FirebaseService } from '../core/firebase.service';
import { Pagination } from '../shared/models';

@Injectable()
export class AuthorService {
  private path: string = 'authors';

  private lstAuthors: any[];

  constructor(private firebaseService: FirebaseService) {

  }

  /**
   * Returns promise of authors from cache if available in memory else fetches list from firebase.
   */
  get(pagination?: Pagination) {
    return new Promise((resolve, reject) => {
      if (this.lstAuthors && this.lstAuthors.length) {
        resolve(this.lstAuthors);
      } else {
        this.firebaseService.getListOnce(this.path, pagination).then((lstAuthors) => {
          this.lstAuthors = lstAuthors;
          resolve(lstAuthors);
        }, (error) => {
          reject(error);
        });
      }
    });
  }

  ...
}

Here, notice that even though firebase implementation for both web and mobile platforms are different, we have injected a common FirebaaseService. So let’s understand how we can abstract different firebase implementation using a common service.

Angular has a very flexible dependency injection framework. In angular, we can provide different implementation with the same token. So in above example, FirebaseService is token of type abstract class. And its value will be either WebFirebaseService or NativeFirebaseService depending on the platform in which it is requested.

This different implementation is registered in module of the respective platform as shown below:

nativescript/app/core/core.module.ts

import { NgModule } from '@angular/core';
import { NativeScriptModule } from 'nativescript-angular/platform';

import { FirebaseService } from '../../x-shared/app/core';
import { NativeFirebaseService } from './native-firebase.service';

@NgModule({
  imports: [NativeScriptModule],
  providers: [
    { provide: FirebaseService, useExisting: NativeFirebaseService },
    NativeFirebaseService
  ],
})
export class CoreModule { }

web/src/app/core/core.module.ts

import { NgModule } from '@angular/core';

import { FirebaseService } from '../../x-shared/app/core';
import { WebFirebaseService } from './web-firebase.service';

@NgModule({
  providers: [
    { provide: FirebaseService, useExisting: WebFirebaseService },
    WebFirebaseService
  ],
})
export class CoreModule { }

Now, let’s take a quick look at what is the content of token class FirebaseService and its implementation classes WebFirebaseService, NativeFirebaseService.

The token class FirebaseService is nothing but an abstract class which lists methods which needs to be implemented by both the platforms as shown below:

x-shared/app/core/firebase.service.ts

import { Pagination } from '../shared/models';

export abstract class FirebaseService {
  abstract getOnce(path: string);
  abstract getListOnce(path: string, pagination?: Pagination);
  abstract filterOnce(path: string, filterBy: string, filterValue: any);
  abstract initializeFirebase(config: any): void;
}

Now, let’s see the NativeFirebaseService which contains implementation of methods defined in abstract FirebaseService class for mobile platform:

nativescript/app/modules/core/native-firebase.service.ts

import { Injectable } from '@angular/core';

import * as firebase from 'nativescript-plugin-firebase';

import { Pagination } from '../../x-shared/app/shared';
import { FirebaseService } from '../../x-shared/app/core';

@Injectable()
export class NativeFirebaseService extends FirebaseService {
  constructor() {
    super();
    this.initializeFirebase({
      persist: false
    });
  }

  ...

  getListOnce(path: string, pagination?: Pagination) {
    let options: firebase.QueryOptions = {
      ...
    };

    return new Promise((resolve, reject) => {
      firebase.query(
        (result: any) => {
          if (result.error) {
            reject(result.error);
          } else {
            resolve(this.prepareResultList(result.value));
          }
        },
        path,
        options
      );
    });
  }

  initializeFirebase(config?: any) {
    firebase.init(config);
  }

  ...
}

You can see that in nativescript project we have used nativescript-plugin-firebase plugin, which calls firebase APIs of native mobile platforms (Android, iOS) for querying data.

Similarly for web there is a different implementation of FirebaseService which uses firebase npm package for querying data as shown below:

web/src/app/core/web-firebase.service.ts

import { Injectable } from '@angular/core';
import * as firebase from 'firebase';

import { Pagination } from '../../x-shared/app/shared';
import { FirebaseService } from '../../x-shared/app/core';

@Injectable()
export class WebFirebaseService extends FirebaseService {
  constructor() {
    super();

    const config = {
      authDomain: 'ngxp-quotes-app-a975f.firebaseapp.com',
      databaseURL: 'https://ngxp-quotes-app-a975f.firebaseio.com'
    };

    this.initializeFirebase(config);
  }

  getListOnce(path: string, pagination?: Pagination) {
    let query: firebase.database.Query;

    query = firebase.database().ref(path).orderByKey()

    return query.once('value').then((snapshot) => {
      return this.prepareResultListFromSnapshot(snapshot);
    });
  }

  initializeFirebase(config: any) {
    firebase.initializeApp(config);
  }

  ...
}

So, now you know how we can abstract the different implementation using a common abstract class token and use it in shared code across the platforms.

ViewModels

As discussed before in this tutorial, we can create viewModels which contains common code related to particular view. Let’s understand how it is used for author-list screen.

In author-list screen, it is obvious that we need to call AuthorService to fetch data and then we need to set the returned author list into some variable. As we can use angular template syntax in both nativescript and web templates, we can create a common code for this as shown below:

x-shared/app/authors/authors-list-common.view-model.ts

import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';

import { AuthorService } from './author.service';

@Injectable()
export class AuthorsListCommonVM {

  lstAuthors: any[] = [];

  authors$: BehaviorSubject<any[]> = new BehaviorSubject<any[]>(this.lstAuthors);

  constructor(protected authorService: AuthorService) {}

  loadAuthorList() {
    this.authorService.get().then((lstAuthors) => {
      this.lstAuthors.length = 0;
      Array.prototype.push.apply(this.lstAuthors, lstAuthors);
      this.authors$.next(this.lstAuthors);
    });
    return this.authors$;
  }
}

As shown in above code, the viewModel is nothing but an angular service. Notice that we have injected the AuthorService in this common viewModel so that we don’t need to inject the AuthorService in components of different platforms and we just need to inject and use this viewModel inside component as shown below:

nativescript/app/modules/authors/authors-list.component.ts

import { Component, ChangeDetectionStrategy } from '@angular/core';

import { AuthorsListCommonVM } from '../../x-shared/app/authors';

@Component({
  selector: 'authors-list',
  templateUrl: 'modules/authors/authors-list.component.html',
  providers: [AuthorsListCommonVM],
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class AuthorsListComponent {

  constructor(
    public cvm: AuthorsListCommonVM
  ) {
    this.cvm.loadAuthorList();
  }
}

In template of mobile platform, we can use this cvm member to access any cross-platform shared code for this authors-list component as shown below:

nativescript/app/modules/authors/authors-list.component.html

<ListView
    [items]="cvm.authors$ | async">
  <template let-item="item">
    ...
  </template>
</ListView>

Similarly, in web also we just need to inject the AuthorsListCommonVM viewModel and use inside component and template. You can refer the code of component and template for web platform.

This way of separating common view related code inside shared view-model also helps in testing by clearly separating test cases of DOM/View manipulation from the actual logic related code of the view. And in future if we want to create our application for any other platform, we just need to write template and DOM/View manipulation code for that platform and rest of the code will be ready to use with us.

Conclusion

So, now you have got an overview of how you can create cross-platform applications with single shared code base using angular and nativescript. You can quickly start creating such application by using nativescript-angular-web-starter. You can also refer to the Quotes Application for reference. Hope this guide will help you in creating your amazing web and native mobile applications with single codebase.

Quickly Use Material Font Icons In Nativescript Angular Apps


Nativescript natively supports font icons. That means you can use any font-icon library like fontawesome, material-design-icons, icomoon or your own custom font icon.

To use font icons in nativescript angular project, there is a great plugin nativescript-ng2-fonticon. This plugin requires a font file and a CSS file containing mapping of className and unicode of the icon.

Unfortunately material-design-icons does not provide ready to use CSS file which contains mapping of className and unicode of the icon. To solve this issue I have created a small plugin nativescript-material-icons, which will add MaterialIcons font and a ready to use CSS file to your project.

I have created a demo app as a companion to this tutorial.

With this much introduction, let me show step-by-step how to quickly use material-design-icons in your project.

Load Images With Different Height In List Using Nativescript Image-Cache For Android/iOS


In this blog post, I am going to show a quick tip on how to show images with different height, downloaded from network, in nativescript list-view. Nativescript provides Image-Cache, to cache the downloaded image and fetch it from cache whenever requested next time. This image-cache is very useful in list-view kind of controls, where the list-item is re-created whenever the list-item comes back into visible area.

If you don’t know, how to use image-cache with list-view, I recommend you to check a very detailed tutorial by Peter-Staev on Using NativeScript’s ImageCache to cache images and display them in list like controls.

If the images that you want to show in list-view have different heights, Android will automatically update the image height once the image is downloaded and will update list-item’s height accordingly. But iOS will not resize the image height till we scroll the list and come back to that list-item.

Angular Migration Step 0 – Creating Modular Angular 1 Application


This is a first part of series of posts for the topic of how to migrate Angular 1 application to Angular 2 by taking small steps at a time.

Throughout the series, I am going to use this angular-migration-tutorial-app as a base application.
You can see the final output of this step by checkout of code at step-0 tag.

git clone https://github.com/shripalsoni04/angular-migration-tutorial-app.git
git checkout step-0

If you are creating new Angular 1 application, you can skip this step and create application using hierarchical component structure introduced in Angular 1.5. Blog post of creating component style angular application, which makes migration to angular 2 somewhat easy, is coming shortly.

If you already have a considerable amount of code written in Angular 1 then continue reading.

Configure ESLint In Visual Studio Code


ESLint is the most flexible and configurable javascript linter among other javscript linters like JSHint, JSLint etc. It is good practice to have one javascript linter configured in our project, to keep the coding style consistant across the team and to detect some of the errors before runtime.

Here, I am going to show how to configure ESLint in Visual Studio Code editor.

Cross Platform Image Manipulation in Nativescript Using WebView Canvas


Do you want to implement image manipulation in your Android/iOS app using HTML5 Canvas Api/Library? If yes, then you will find this tutorial helpful.  

I have created a plugin nativescript-canvas-interface, which makes this very easy to implement. This plugin enables you to use any canvas library out there on the web (excluding animation libraries), to do image manipulation and render that image in native image element in your Android/iOS application. The image manipulation is done on canvas element in web-view, and the generated image is transfered from web-view to native application.

Bi-directional communication between WebView and Android/iOS in Nativescript


Nativescript provides a cross-platform web-view ui element. It serves the purpose of showing web-view content in our page.
But if you want to send some data to web-view or get some data from web-view, you need to write platform specific code for that. So rather than writing all such code in every application, it is better to have a plugin for that. Right?

I have created nativescript-webview-interface plugin to serve this purpose. You can clone the demo application to quickly get started with this plugin.

Menu