Angular + Cordova

An Unopinionated Implementation



Angular + Cordova




Lets port a new angular project to Cordova without using Ionic or NativeScript. Why? Because those frameworks can sometimes be too opinionated and constraining. By the end of this article, you'll have a functioning angular/cordova project which will be as loosely coupled with Cordova as possible. You'll be able to conform to the angular style conventions, and install whatever plugins, frameworks, or libraries you desire. (e.g. bootstrap, ng-bootstrap, pug, etc.)

First, I'd like to give credit to this post as it's what got me heading in the right direction however I found it to be a little incomplete.

You can find the demo source code here


Ok. Lets Get Started.


1. Setup A Vanilla Angular App

First we're going to create a vanilla angular 6 project called hello-cordova with the following command: ng new hello-cordova.

Some things you may want to do at this point would be to install and configure things like bootstrap & ng-bootstrap or any other angular library's you're interested in working with.

2. Basic Cordova Setup

Now that we have a basic angular app running with npm start we're going to create some basic scaffolding to setup our cordova build.

First let's make sure we have the cordova cli installed: npm install -g cordova.

Now let's create a basic cordova app. We're going to create the cordova app in a cordova subdirectory and give it an id of com.hellocordova.www and a name of Hello Cordova.

cordova create cordova com.hellocordova.www "Hello Cordova"

Now let's add some git tracking files to ignore some build files and keep directories:

touch cordova/.gitignore
touch cordova/www/.gitkeep

And add the following to cordova/.gitignore:

node_modules
www/*
!www/.gitkeep
platforms
plugins

Finally, let's add the iOS platform. To add platforms (and plugins) we'll want to make sure we're in the cordova directory and then run the following:

cordova platform add ios

3. Add Scripts

In the root directory of our project (not the one in the cordova folder), lets add some basic scripts to the package.json to automate a few things:

"clean": "rm -rf dist cordova/www/* && touch cordova/www/.gitkeep",
"build:cordova": "npm run clean && ng build --base-href=. --output-path=cordova/www",
"cordova:prepare": "cd cordova && rm -rf platforms plugins node_modules && npm ci && cordova prepare && cd ..",
"cordova:build:ios": "npm run build:cordova && cd cordova && cordova build ios && cd ..",
"cordova:emulate:ios": "npm run cordova:build:ios && cd cordova && cordova emulate ios && cd ..",
"open:xcode": "open $(find ./cordova/platforms/ios -name '*.xcworkspace')"

We're also going to modify the build script to be the following:

"build": "ng build --base-href=/"

This will give you the following utilities:

  • npm run cordova:prepare - Other devs should be able to run this script to initialize the Cordova app in their environment.
  • npm run open:xcode - Run this to easily open the project in Xcode
  • npm run cordova:build:ios - Run this to rebuild the iOS project
  • npm run cordova:emulate:ios - Run this to run an iOS emulator of the project

4. Performance Improvements (400ms delay)

Yes. Now let's remove the infamous 400ms delay that iOS defaults on webview clicks. We're going to add FastClick.js to our angular app.

$ npm i fastclick

Add the following to the top of your src/main.ts:

import * as FastClick from 'fastclick';

// attach FastClick.js
FastClick.attach(document.body);

Note: this should be added to main.ts as close to the top as possible so it executes immediately.

5. Initializing Plugins

Now if you want to install other plugins you'll need to configure them. This can be done in the ngOnInit() of your app.component.ts:

// src/app/app.component.ts
import { Component, OnInit } from '@angular/core';
import { CordovaService } from './service/cordova.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  title = 'app';

  constructor(protected _cordovaService: CordovaService) {}

  public ngOnInit(): void {
    this._cordovaService.initialize();
  }
}
// src/app/service/cordova.service.ts
import { Injectable } from '@angular/core';

@Injectable()
export class CordovaService {

  protected hasBegunInitialization: boolean = false;

  constructor() {}

  public initialize(): void {
    // listen for 'deviceready' event in case it hasn't
    // already been fired during application bootstrap
    document.addEventListener('deviceready', () => {
      window['isDeviceReady'] = true;
      this.init();
      console.log('deviceready fired after bootstrap finish');
    }, false);

    // check if 'deviceready' has already fired during
    // application bootstrap.
    if (window['isDeviceReady'] === true) {
      this.init();
      console.log('deviceready fired before bootstrap finish');
    }
  }

  protected init(): void {

    // lock initialization
    if (this.hasBegunInitialization) {
      return;
    }

    this.hasBegunInitialization = true;

    // do init stuff here
  }
}

Make sure you add a provider for the CordovaService class.

Troubleshooting

  • Error: Cannot read property 'replace' of undefined run cd platforms/ios/cordova && npm install ios-sim
  • Images/assets not showing up?
  • Whitescreen of death?

    • Cordova doesn't like <base href="/"></base>, instead use <base href="."></base> for your cordova distributions. This typically isn't what you want for your web distribution however as it can mess with your routing.
    • If you're having this issue please take a look at the Add Scripts section and take a look at the --base-href flags.