Autoplaying inline videos on iPhone iOS 10 using Angular

Starting with iOS 10, Safari on mobile (iPhone and iPad) started allowing developers to autoplay videos inline provided the video you are trying to play meets certain conditions. Before that, your video would not auto play inline, and when it would play, it would play full screen only. These changes were in place to prevent “websites that auto-play with sound”, if you’ve ever browsed to a site on your phone and an annoying video ad started playing, inline video autoplay is probably to blame for it but there are other legitimate uses for the inline video HTML attribute.

Video as Animated GIF

GIFs are cool we all agree, but behind the scenes they are HUGE objects and in terms of performance, GIF is definitely not the best format for the web.

It’s not uncommon for GIFs to tip the scales at several megabytes, depending on quality, frame rate, and length. If you’re trying to improve loading performance for your site and help users reduce data usage, animated GIF just isn’t compatible with that goal.

One way that you can remediate this issue is to convert these huges files to compressed lean videos which maintains a great resolution while saving huge amount of data for your users. 

Muted Autoplay

In short for a video to play inline on mobile browsers, it needs the following key attributes to be added to the video tag (depending on the mobile platform). 

  • autoplay
  • muted
  • playsinline

Only the first two are necessary for Chrome For Android but if you’re dealing with Safari, you’ll need to add the playsinline attribute which was introduced in iOS 10. A cross browser compatible code would look like:

<video src="/video.mp4" loop muted playsinline poster="/poster.png">

</video>

On most recent browsers and platforms, this would render your video inline autoplaying, with the loop attribute making your video essentially act as a GIF. 

It Doesn’t Work!

I highlighted most in the last sentence of the previous paragraph because you will most certain run into cases where it just doesn’t work. My specific case was iOS 10 and 11 iPhones running an app rendering an Angular app using a custom WebView. The video tag in that app would respect the autoplay attribute as soon as the user interacted with the page, as specified in the iOS technical guidelines for autoplay but would simply ignore the playsinline attribute and play the video full screen. It took quite a bit of Googling and trial and error to finally get it working but if you run into the same error, here are things to check for to get you up and running:

webView.configuration.allowsInlineMediaPlayback = true

if #available(iOS 10.0, *) {
webConfiguration.mediaTypesRequiringUserActionForPlayback = []
} else {
// Fallback on earlier versions
webConfiguration.mediaPlaybackRequiresUserAction = false
}

The muted attribute problem

With all these steps above enabled, I was still unable to play a video inline on an iPhone SE running iOS 10 no matter what I tried. This post brought to my attention the fact that as stated not only does the video NEEDS to be muted in order for the autoplay attribute to be taken in account, but the muted attribute itself might not work as advertised on all browsers and this unlocked the issue for me. Inspecting the video element showed me that even though the muted attribute was set in the HTML, the DOM element showed muted=false . I doubled checked with FFMpeg to make sure that indeed my mp4 file did not include an audio track and it did not! The only logical deduction was that something was broken at the browser level, prevent the muted attribute from being properly applied in the DOM. In the end a combination of using iphone-inline-video and dynamically triggering the play attribute gave me a cross-browser compatible way of playing videos inline. My final Angular code looked something like

# In my component.html

<video #gif loop muted playsinline [src]="gifUrl">
</video>



# in component.ts
import enableInlineVideo from 'iphone-inline-video';
@ViewChild('gif', { read: ElementRef }) gif: ElementRef;
ngAfterViewInit() {
const video: HTMLVideoElement = this.gif.nativeElement;
// Crucial to mute the video before for Safari autoplay to work
video.muted = true;
// Use iphone-inline-video library here after muting the video
enableInlineVideo(video);
video.oncanplay = () => {
video.muted = true;
};
const promise = video.play();
if (promise !== undefined) {
promise
.catch(err => {
// Auto-play was prevented
console.log('Autoplay was prevented');
})
.then(() => {
// Auto-play started
console.log('Autoplay started');
});
}
}

If you run into a

NotAllowedError (DOM Exception 35): The request is not allowed by the user agent or the platform in the current context, possibly because the user denied permission.

This is definitely an indication that autoplay was prevented by the browser, more than likely because of an issue with the muted attribute. Use the solution listed above and see if it solves your issue.

Integrating OpenCV.js with an Angular application

OpenCV (Open Source Computer Vision Library) is an open source computer vision and machine learning software library. It was originally written in C++ and has since been ported to most other major languages and platforms and most recently, with the boost provided by Web Assembly, it has finally crossed into the Javascript world. OpenCV.js is a “JavaScript binding for selected subset of OpenCV functions for the web platform”, and as you have noted, it exposes a subset of the OpenCV functionality to the web. I ran into this library trying to answer the question:

How do I detect a face in a picture using Javascript?

Initially, I went for Tracking.js a promising library that implemented a face detection algorithm that works pretty well, but the event based natured of the implementation, meaning that found faces are emitted as events, rather than the library being able to deterministically tell you whether it found a face or not made it challenging to work with in my particular use case where I wanted to know if a face was found as fast as possible. Furthermore, on top of face detection, I wanted to be able to detect an ID card (Driver License)in an image, which Tracking.js does not support. Googling around, OpenCV just kept coming back as the solution for similar use cases, so I finally decided to use it for all my image feature detection features. In these days there is an integration for all major libraries, or so I thought, imagine my surprise googling for an Angular OpenCV integration library and coming up with… fohi (nothing in Dyula)!!! The next paragraph will explain why.

Working with OpenCV.js in Angular

The tutorial explains that

OpenCV.js leverages Emscripten to compile OpenCV functions into asm.js or WebAssembly targets, and provides a JavaScript APIs for web application to access them

Existing library absent, I figured, let me just download the distribution, integrate it in my @angular/cli configuration and I am home free. WRONG! There is no official distribution or NPM package available for the Web, unless you’re working with NodeJS in the backend. The official usage guide and and al tutorials keeps referring to an opencv.js library file but in the example pages it is part of the build of the page. This meant I was going to have to build it from source as explained in the official documentation. Here are the steps to follow to building your own local version if you wanted to:

Install EmScripten

Emscripten is an LLVM-to-JavaScript compiler. In English, Emscripten basically allows you to compile C/C++ code into Javascript. This allows native code to be run on the web at near native performance, if you’ve ever heard of Web Assembly, this is what the hoopla is about. In our specific case, something as computationally intensive as computer vision can be brought to the Web using Javascript and Emscripten helps us get there. To install:

# Get the emsdk repo
git clone https://github.com/juj/emsdk.git

# Enter that directory
cd emsdk
# Download and install the latest SDK tools.
./emsdk install latest

# Make the "latest" SDK "active" for the current user. (writes ~/.emscripten file)
./emsdk activate latest

# Activate PATH and other environment variables in the current terminal
source ./emsdk_env.sh
On Windows, run emsdk instead of ./emsdk, and emsdk_env.bat instead of source ./emsdk_env.sh.

Build OpenCV

Next, we need to build OpenCV. You will need Python (≥ 2.7):

# Install from Github.
git clone https://github.com/opencv/opencv.git
# Go the openCV folder and run the build.
cd opencv
python ./platforms/js/build_js.py dist

The official guide says that:

The build script builds asm.js version by default. To build WebAssembly version, append --build_wasm switch.

I’ve found that to be incorrect. It always builds the WebAssembly version. I did not mind since the WASM version brings near native performance. Once the build is done, it takes a while, at the root of your folder you should have a dist/bin folder with 3 files

opencv_js.js
opencv_js.wasm
opencv.js

It wasn’t clear to me and I’ve researched it what the is for but the two that we’re interested with are opencv.js and opencv_js.wasm files. The files are created to be distributed so in the case of the opencv.js file, the file tripped my VSCode editor as some of the JS code was in a single line that exceeds the max line length, making Intellisense trip and crashing the plugin. Due to this line break issue, this will also break your @angular/cli build if you integrate it as is. I had to import the file in a Javascript editor to be able to format it correctly and be able to view it in VSCode.

Working with OpenCV.js

These initial issues outstanding, I ran into a bunch of issues trying to integrate into my Angular project. OpenCV.js as great as it is, is only documented through tutorials that assume that your initial configuration has already been done. It made it very difficult to figure out exactly what to do and it took me long tentative approach to finally get it working in a stable way.

Adding opencv.js to angular-cli.json

I added a new entry to the scripts:[] property in my angular-cli.json. On ng serve I ran into a

failed to load wasm binary file at opencv_js.wasm

error. I had to dig through the source code to be able to understand the source of the error, which of course was specifying the correct path to the opencv_js.wasm file, which for reference, I put in my assets folder. The library can’t function without that file being loaded. This brings me down to my next point.

The Module global variable

To make a long story short, as of version 3.4.3 (and v4 I am pretty sure):

The WASM OpenCV.js relies on a global Module variable, that has to be present and configured before the OpenCV.js library is loaded.

I’ve found that out doing a lot of view source on code examples. A minimal configuration should look like this:

var Module =  {
wasmBinaryFile: 'wasm/opencv_js.wasm',
usingWasm: true,
onRuntimeInitialized: () => { console.log('OpenCV is ready)}
}

As long as such an object is present on the page before the library is loaded, OpenCV.js will boot fine. It feels icky and very much in my opinion against the best practices for today’s libraries and I hope this is something that will be addressed in future releases of the library. Until then, pay attention to the wasmBinaryFile property in particular, it should point to the location in your project where the WASM file should be accessible. In my case it would have been assets/opencv/wasm/opencv_js.wasm . There are two ways you can go about including this in your project:

  • Adding a var Module in a <script> tag in your index.html with the right configuration.
  • Add an opencv_init.js file to your codebase, either in your src or assets folders, and add the correct path to your angular-cli.json scripts property. You have to make sure that file entry comes before the opencv.js file if you are loading the opencv.js library file through Webpack like:
"scripts": [
...
"../src/opencv/opencv_init.js",
"../src/opencv/opencv.js"
],

In any case, with this setup in place, you should be good to go and be able to use the library now.

Module Configuration

As explained before, there are two configuration properties in the opencv Module you will need to pay attention to:

  • wasmBinaryFile : This is the location of the WASM file associated with OpenCV. I am guessing if you are successful in building the ASM version you would not need to worry about that. The associated usingWasm property did not make a difference whether I set it to true or false. It would always try to load the WASM.
  • onRuntimeInitialized : This function is the callback triggered when the library is ready for use. If you’re integrating OpenCV through your own Angular service, you would need to wait until this callback is called to make the OpenCV based functionality available to the rest of your code. I have taken this into account by wrapping every of my OpenCV calls into a subscription to aisReady$ BehaviorSubject that will emit only when the library is ready.

Considerations for Face Detection

As stated in the beginning of this piece, my original goal for working with OpenCV was to make use of their face detection functionality. If you look at the example code here, and tried to copy and use it in your code, it would fail on this line:

faceClassifier = new cv.CascadeClassifier();
faceClassifier.load('haarcascade_frontalface_default.xml');

eyeClassifier = new cv.CascadeClassifier();
eyeClassifier.load('haarcascade_eye.xml');

Those files are part of the data folder that comes with the release and are the classification data needed by the ML algorithm uses to return result. Trying to run the code as is will get you an error like:

ERROR 6424592 - Exception catching is disabled, this exception cannot be caught. Compile with -s DISABLE_EXCEPTION_CATCHING=0 or DISABLE_EXCEPTION_CATCHING=2 to catch.

when using the detectMultiScale function. I’ll make a long story short here by saying that these files need to be created in memory and OpenCV.js has an non-advertised utility class called Utils that can be used for exactly that and other useful operations. I’ve used that class as the basis for my integration library, which saves you the whole pains I’ve just went through.

Announcing ng-open-cv

This is an open source integration of OpenCV.js and Angular 6 and it works currently as a service exposing various OpenCV functionality. It’s based on an “angularized” version of the Utils class provided by the framework and will allow you initially to:

  • Load OpenCV.js v3.4.3 asynchronously
  • Load classifier files

This is for the alpha release. Eventually, I am hoping to add functions to facilitate:

  • Face detection
  • Corner detection
  • Blur detection

And any other useful functions that the community will contribute to of course. You can grab the library here and I hope it will be useful to you as it was to me.

That was quite some reading, reward yourself with some Yang System!

A story of ngUpgrade: Bringing an AngularJS application from 1.6 to Angular 4

After reviewing an existing AngularJS application that was to be used as the basis for an enterprise dashboard application, i quickly determined that the best way forward would be to move the existing application to Angular before implementing the required new features in the more modern versions of the framework.

AngularJS in Typescript

Understanding ngUpgrade

Before embarking on this task, I first had to understand the nature of what I would be doing, I highly recommend the following material as preparatory reading:

These links should help you understand the nature of the upcoming tasks. Given my own circumstances I had to go about it in the following way:

  1. Upgrade the existing application’s AngularJS version to 1.6 from 1.3 and get it running in a Angular CLI environment.
  2. Set up the routing handling between Angular and AngularJS
  3. Componentize the 1.6 application using John Papa’s style guide
  4. Upgrade the componentized application to use Typescript
  5. Upgrade the Typescript components to Angular components

Angular CLI and the Upgrade process

I scaffolded a new project using the CLI, and installed the Angular UpgradeModule:

npm install @angular/upgrade --save

Next i manually imported all the of the existing AngularJS files and assets from their old location. It took some work to get the application up since we’d basically be giving up on the old build system and manually loading the AngularJS files. In my case give the relative small number of files it was worth dealing with the loss of minification until the upgrade process was completed.

Loading AngularJS *.js files in Angular-CLI

Due to my set up as previously mentioned where I had a relatively small number of files to load for the existing AngularJS application. I used regular script tags to load them in the index.html file of the cli application. My goal was to then used the scripts and styles entries in the .angular-cli.jsonto load them once every issue would have been resolved with bootstrapping the AngularJS application. For existing apps that use Gulp or Grunt as a build system, I think you would have to eject your webpack configuration to include the gulp build files in your existing cli configuration but don’t take my word for it. This was temporary in any case as these .js files would eventually be updated to Typescript .tsfiles during the upgrade process.

Setting up routing between AngularJS and Angular components

Next I needed to make sure that i could access the existing AngularJS application from my Angular setup. This took the most time as it meant familiarizing myself with the whole upgrade process and how it made both frameworks interact with each other. This implementation here was really useful in showing how to upgrade an existing application, in this case the vanilla Phonecat AngularJS implementation and its based on the official upgrade tutorial. Upgrading my app from v1.3 to 1.6 i ran into a routing error in AngularJS that took me a while to figure out and fix so if you ever run into weird url encoding issues while upgrading, definitely check out the related article here.

Upgrading from ES5 to Typescript

Once the application was working with the routing set up properly, the next step was to upgrade to Typescript. There is enough documentation on how to proceed if you know where to look for it for the different Angular elements (controllers, filters, directives, services) and how they can best be represented in Typescript and for filters and directives, there are a couple of ways you can go about upgrading them to Typescript.

Here are some additional tips form my experience:

  • Make sure to move all your library dependencies to NPM. This will lead to some errors, and in particular, loading AngularJS , something along the lines of :
AppComponent_Host.html:1 ERROR Error: AngularJS v1.x is not loaded!
    at Object.noNg (static.es5.js:15)
    at module$1 (static.es5.js:55)
    at UpgradeModule.webpackJsonp.../../../upgrade/@angular/upgrade/static.es5.js.UpgradeModule.bootstrap (static.es5.js:1249)
    at AppComponent.webpackJsonp.../../../../../src/app/app.component.ts.AppComponent.ngOnInit (app.component.ts:17)
    at checkAndUpdateDirectiveInline (core.es5.js:10848)
    at checkAndUpdateNodeInline (core.es5.js:12349)
    at checkAndUpdateNode (core.es5.js:12288)
    at debugCheckAndUpdateNode (core.es5.js:13149)
    at debugCheckDirectivesFn (core.es5.js:13090)
    at Object.eval [as updateDirectives] (AppComponent_Host.html:1)

you basically have to make sure that AngularJS is imported before loading your Angular app’s main.js.

  • This might be due to my ignorance of the angular-cli inner workings or the way Typescript module loading works but i ran into this issue where not all my Typescript files where not being compiled and included in the final bundle. I wanted to keep things modular by keeping each module defined first, and then import the module into for example a service file in which I would declare the service, then .service() attach it to the corresponding Angular module. This approach resulted in services, filters and other AngularJS components not being included in the final bundle file by Webpack. The correct approach is to import all the necessary components in the module definition file and then attach them to their particular component, a la:
import { CoreModule } from './../core/core.module';
import { ActionPopoverComponent } from './action-popover.component';
import { ActionPopoverService } from './action-popover.service';
import { ActionPopoverFilter } from './action-popover.filter';
import * as angular from 'angular';
import { NgModule } from '@angular/core';
export const CommonModule: angular.IModule = angular.module(
'actionApp.common',
[CoreModule.name]
);
CommonModule.component('actionPopover', ActionPopoverComponent);
CommonModule.service('actionPopoverService', ActionPopoverService);
CommonModule.service('actionPopoverFilter', ActionPopoverFilter);
@NgModule({})
export class CommonNgModule {}

There are also some gotchas with for example finding the best way to represent a ES5 Angular as either a Typescript function or a Typescript class, obviously representing as a class makes it easier to then transform into an Angular pipe but it introduces complexities when your filter relies on some injected services.

Upgrade the Typescript components to Angular components

Last but not least once your app is once again working all in Typescript, you can take the approach of now upgrading all AngularJS Typescript components to their Angular versions. This is not an exact science and will undoubtedly involves rewrites and refactors, especially if you were using 3rd party libraries that you will also need to upgrade to their Angular equivalent if available or alternatives if not. This is also a good opportunity to review your code and maybe improve upon it time permitting. There are better guides on this specific part out there on the internet but it was one the most fun part as it really involves bettering your knowledge of Angular and AngularJS. There are some

Summary

The process of upgrading an application from AngularJS can be daunting because it involves having a good deal of comfort with 3 domain knowledges: AngularJS, Angular and Typescript. For an individual or team coming in with good AngularJS knowledge, I would suggest getting comfortable with Angular and Typescript before attempting the movement, maybe by implementing a brand new feature in Angular to get a feel for how everything fits before trying to translate AngularJS paradigms into this new stack. The Angular team did a good job though to provide tools to make this process less daunting then it is.

Reverse Proxying an angular-cli SPA with Apache and Tomcat

It’s always beneficial to have your development environment closely mirror your production one, and thanks to tools like Docker it’s quite easy to set it up nowadays. In this post we will learn how to set up a dev environment for an Angular 4 application, backed by an api served with Tomcat fronted by Apache. If my production Angular app was served available at http://www.production.com , and the backing api at http://www.production.com/api, my goal would be to replicate the same set up for my local dev environment, so have my front-end being served at http://www.production.dev/app and apiat http://www.production.dev/api. I would want to I will be making the following assumptions:

  • an Apache-2.4 web server running on port 80, configured for the domain http://production.dev/, with the modules mod_proxy and mod_proxy_http enabled
  • a Tomcat-8.5.27 api server running on port 8080 that is accessible on http://production.dev:8080/api.
  • An angular-cli (v1.3.2) Angular 4 application running in dev with ng serve that is normally available by default at http://localhost:4200.

As a developer I wanted both my Angular and API application to be able to be accessible from a production.dev domain when developing locally. There are three separate areas to configure to make it happen.

Custom Domain

In order to be able to access the production.dev address, I set up a custom domain in my Mac’s etc/hosts file. This maps my default localhost address to this custom domain:

127.0.0.1    production.dev

Apache

The first thing you will need is to configure Apache to reverse proxy both the Angular (app)and the Tomcat (api)applications. A representative httpd.conf would look like:

<IfModule mod_proxy.c>
    ProxyRequests Off
    ProxyVia Off
    ProxyPreserveHost On
    ProxyStatus On
    ProxyPass           /api    http://localhost:8090/api
    ProxyPassReverse    /api    http://localhost:8090/api
    ProxyPassReverseCookiePath  /api  /
    ProxyPass           /app    http://localhost:4200/app/
    ProxyPassReverse    /app    http://localhost:4200/app/
</IfModule>

This sets up Apache to forward requests from /api to our Tomcat api app instance. You can also set it up in the exact same way if you are using Virutal Hosts. You’ll notice that the Tomcat port is 8090 which is not the default Tomcat port, which leads me to the next configuration.

Tomcat

In my case I wanted my app to have the context path of api since it would be served from the same domain as my front end app. So my server.xml configuration was updated to contain a Context entry. I will not add it here since it was done through configuring my IntelliJ configuration but my app was available after a local deploy at http://production.dev/api. You’ll need to create a new connector for your app for the proxying to work. A sample configuration would be :

<Connector port="8090" 
connectionTimeout="20000" 
redirectPort="8443"
maxThreads="48" 
minSpareThreads="10"           
enableLookups="false"
acceptCount="10" 
debug="0" 
URIEncoding="UTF-8"
protocol="org.apache.coyote.http11.Http11NioProtocol"
proxyName="production.dev"
proxyPort="80" 
scheme="http"/>

proxyPort is the important configuration here, it allows you application to reflect the port being proxied (80) rather than the port it is running on (8090).

Angular

In order for this setup to work locally, I had to set up local proxying made enabled by the proxy feature of the webpack-dev-server that powers the angular-cli. I was getting 404s when using the http://production.dev/app url to access my Angular app when it was served through ng serve — host production.dev. In order to resolve this I had to add to add a proxy configuration for my app like this in a newproxy.conf.json in my app’s root directory :

{
"/app/**": {
"target": "http://production.dev:4200/",
"secure":false,
"logLevel":"debug",
"pathRewrite": {
        "^/app": ""
    }
}
}

With that configuration in place, i ran my ng serve as

ng serve --host production.dev --proxy-config proxy.conf.json --base-href /app/

The base-href option is important in this case it allows us to specify the base url of the application which is app in this case.

This completes the local setup of a development environment to try and mirror production domain wise using Angular, Apache and Tomcat. In the case of Angular you might need the deploy-url option to be specified with the ng serve to get this to work.

Going Mobile: Wrapping an existing web application in Cordova/Phonegap

Going Mobile: Wrapping an existing web application in Cordova/Phonegap

There are many choices to build mobile applications, from native to the growing popularity of hybrid development, with everything in between. In all of these cases, as a developer you would be writing a brand new mobile application from scratch with the ultimate goal of deploying to one the app stores. Let’s say that a company has an existing web application and for reasons we will not debate here, management has decided that this web application is to be packaged and delivered as a mobile app available from the main app stores. How would you go about it from a development perspective and what would be some of the pitfalls and difficulties to take into account when engaged in this endeavor? This article will attempt to cover these and offer some solutions based on actual experience.

Design

The first and most crucial problem that you will have to resolve before doing anything is ensuring that:

  • Your web app is responsive
  • Your web app uses mobile friendly form controls

The second point is actually quite important if you’re using custom controls that will not be rendered natively by the mobile browser (I am looking at you Bootstrap dropdowns). You will have to scour your application on a mobile screen to ensure that the experience is consistent and frustration free for your target users. I emphasized target here because if your goal is to keep your application available through the web but still be available as a mobile application, I can almost guarantee that you will run into design issues where one platform will have to be favored over the other for optimal design so be prepared to make these choices when they come up.

Assuming that your design is now mobile ready. How do you go about wrapping into something that will feel native to your users?

Phonegap/Cordova and the InAppBrowser

Adobe PhoneGap is a distribution of Apache Cordova and Cordova is an open-source mobile development framework that allows you to use standard web technologies — HTML5, CSS3, and JavaScript for cross-platform mobile development. In simple words use standard web development tools to create and deploy mobile applications from a single codebase across different platforms. Phonegap offers some additional enterprise services for the development and deployment of your Cordova application but in essence you will always be working with Cordova under the hood.

What’s interesting about Cordova is that it can be extended using plugins which allow your app to use native device capabilities beyond what is available to pure web apps. That is how your standard HTML, CSS and Javascript codebase can hook into the phone’s camera or microphone to perform native functions, reinforcing the illusion of native to your app’s user. One of these plugins that can be made use of to wrap an existing application into a native app is the InAppBrowser who started as an independently developed plugin before being incorporated as a core Cordova plugin. It’s basically a child web browser that allows you to access third party web content. Wrapping an existing web application as a native application comes down at this point to loading said web application in an InAppBrowser window and managing the whole mobile app experience.

In practice, the loading is the easy part, with the managing of the mobile experience come the hard parts.

Cross Window Communication

Outside of just authentication, there will be many instances where you will find yourself wanting to communicate values between the wrapper app and the wrapped app in the inAppBrowser window. This great article by TJ VanToll lays the foundation for how to to just that even though it dates from 2013. Basically by making use of the inAppBrowser executeScript function, and using localStorage, you can exchange values between both windows. It’s not a standard way of operating but while waiting for Cordova to implement the window.postMessage specification in the InAppBrowser, this remains the most effective way of going about cross window communication.

Authentication and Session Management

If your application only consist off content that does not require authentication, you’re home free. Skip the rest of the article and enjoy yourself a cold drink. For the majority of cases the existing application is without a doubt functional only after authentication. With authentication also comes session management not only in the context of your application but also in the context of your user being on a mobile phone with the expected corresponding behavior. The following questions need to be answered

  • How long does the session last once the user is authenticated? In web applications, users are used to re-authenticating every time their session expire but on mobile, the expectation is to authenticate once and forget it, or use other less intrusive mechanisms like fingerprint reading or pin entering when security must be enforced. The takeaway is that you should not make the user have to enter a username and password every time they open the app if they really don’t have to.
  • Is your Forgot Password application flow mobile friendly? Does it require users to perform steps (Captcha) that are easy to do on a computer but would be more challenging on a mobile device? If not, you will have to adjust your flow to use more mobile friendly techniques (verification codes sent by SMS or Email).
  • What happens to your application when the user navigates away from it? Do not assume that your application will always be front and center, what happens when a call comes in and last longer than your session duration? What happens if the user sends the application to the background and then comes back to it a couple of days later? How would you know the state of the application at that time?

These questions show that even though the goal is to simply wrap an application in an hybrid app, to ensure an optimal mobile experience you will sometimes have to write additional code in the hybrid app to handle these different situations. For authentication and session management, the general idea is for also the hybrid wrapper to be aware of the authenticated and session state of your application.

Cookie Authentication

If your application uses cookies for authentication and session management and you don’t need to add an extra layer of security or convenience like fingerprint authentication, you will need to make sure that when opening the InAppBrowser window, you set the proper option for the window to remember cookies, otherwise if you kill the app and re-open it, your session cookies will be gone and the user will need to re-authenticate:

var ref = (url, target, options);

From the documentation:

options: Options for the InAppBrowser. Optional, defaulting to: location=yes. (String)

The options string must not contain any blank space, and each feature’s name/value pairs must be separated by a comma. Feature names are case insensitive. All platforms support the value below:

location: Set to yes or no to turn the InAppBrowser‘s location bar on or off.

clearcache: set to yes to have the browser’s cookie cache cleared before the new window is opened

clearsessioncache: set to yes to have the session cookie cache cleared before the new window is opened

The clearcache and clearsessioncache options are available on both Android and iOS and for clearsessioncache it should be set to no to ensure that your session cookies are remembered.

Token Authentication

If you’re using a token based authentication strategy and want to add fingerprint or pin-based authentication, you will need add extra code to the hybrid app to check for the state of the user when the app opens and present them with either a login screen or a fingerprint/pin reader. Refer to the Cross Window Communication section on how to communicate this state between the wrapper and wrapped apps.

External Links

A (nasty) little gotcha from using the inAppBrowser is that clicking on a link to external content, while you are in a inAppBrowser window will load that content in the inAppBrowser window you are using which leads to the following scenario:

  • User is enjoying your app thinking it’s native as your design job is excellent.
  • User sees a link to let’s say a Twitter page and “clicks” on it
  • The Twitter page loads in the InAppBrowser window, replacing your app with the Twitter page where your app screen used to be.
  • Extreme confusion ensues as the task manager shows that the user is indeed using your app, but they have no way to get back to your app content on iOS, or have to use the Back button on Android, and at this point the magic of native feel is broken.

The expected behavior would have been for the link click to open in a new OS Browser window, keeping the application intact and allowing the user to get back to it once they are done.

The solution in general is to track every external link with a script and open them using the window.open(url, '_system') call as illustrated here. Remember that this script will have to run in your wrapped app to work otherwise you will need to communicate the link url back to the wrapper app and issue the window.open call.

Android Only: Back Button

On Android you will have to be careful and plan out what happens when the user clicks on the Back button whether it’s a hardware or software button. If your wrapped app is a SPA, this can leads to some weird situations with your app state.

First thing to know is that there is a configuration for it in the InAppBrowser launch options:

hardwareback: set to yes to use the hardware back button to navigate backwards through the InAppBrowser‘s history. If there is no previous page, the InAppBrowser will close. The default value is yes, so you must set it to no if you want the back button to simply close the InAppBrowser.

If it is set to yesand your SPA handles back history fine, if the user gets back to the first page in your history and touches the Back button again, they will be taken back to the entry page of your wrapper app! Again not optimal and it breaks the experience of native and leaves the user confused again. One way to handle it would be to listen to the exit event on the InAppBrowser window so that when it is closed, the wrapper itself also is closed. Check this StackOverflow issue for alternatives.

Angular Only: ngZone for event handlers

After working with Cordova/Phonegap for a while and installing plugins, you’ll become familiar with how Cordova exposes functionality through events and event callbacks or promises. One gotcha if you’re working with Angular (>v4) is that these are events that are happening outside of Angular and if you remember even from AngularJS, any event happening outside of AngularJS needs to be synced with the AngularJS digest cycle using the $scope.apply() function. It’s no different in Angular expect now that Angular run in zones and uses the ngZone.run() function to re-enter the Angular zone. If you have Cordova plugin events firing and you are not able to tap into them in Angular, make sure to capture the event within a run function call as illustrated here in this example service for the Back button event being fired.

import { Injectable, NgZone } from '@angular/core';
@Injectable()
export class BackButtonService {
constructor(private logger: DebugLogService,
private ngZone: NgZone) {
document.addEventListener('backbutton', () => {
this.ngZone.run(() => {
console.log('Back button has been pressed')
});
}, false);
}
}

Summary

Although the initial goal of wrapping an existing web app into a native mobile app might look simple enough with Cordova/Phonegap, in practice there a quite a few pitfalls in making sure that the experience is optimal for users and fulfills their expectations for what a native app experience is. Behind the scene there will be quite a few orchestrations to put in place to maintain the illusion of native. I would then only recommend this approach as a stop gap measure while either a more appropriate native or hybrid app is being developed. Even they don’t work on Safari yet thus making them a more limited choice at this point, Progressive Web Applications are also a very viable option when targeting mobile applications.

Dragons when upgrading from Angular 2.1.x to 2.4.x

The reason why I decided to upgrade to 2.4.x was simple enough. On trying to redirect to a new URL I was getting a rather obscure error message, and no amount of URL fiddling worked to get a redirect to work or get me a sensical error message:

error_handler.js:47 EXCEPTION: Uncaught (in promise): TypeError: Cannot read property '_outlets' of undefined
TypeError: Cannot read property '_outlets' of undefined
at http://localhost:4200/main.bundle.js:14278:56
at forEach (http://localhost:4200/main.bundle.js:5901:13)
at PreActivation.deactiveRouteAndItsChildren (http://localhost:4200/main.bundle.js:14277:99)
at http://localhost:4200/main.bundle.js:14279:19
at forEach (http://localhost:4200/main.bundle.js:5901:13)
at PreActivation.deactiveRouteAndItsChildren (http://localhost:4200/main.bundle.js:14277:99)
at PreActivation.traverseRoutes (http://localhost:4200/main.bundle.js:14262:22)
at http://localhost:4200/main.bundle.js:14233:19
at Array.forEach (native)
at PreActivation.traverseChildRoutes (http://localhost:4200/main.bundle.js:14232:29) .

From the Github issue thread, the easiest fix was to upgrade to a later version of Angular2, which I tried and little did I know it opened up a brand new can of worms. Here is a summary of dragons that may lie in your path to an upgrade.

Lazy Loaded Module Paths

The most obvious errors you will get if you are using lazy loading of your modules using the CLI in 2.1.x and you upgrade to 2.4.x will be path resolutions errors. All lazy loaded modules in 2.1.x were loaded with a path relative to the src directory. In 2.4.x you will have to upgrade these paths to be relative to the file that is configuring the lazy loading.

Dependency Injection, Singletons and Lazy Loading

The most insidious “error” I ran into resulted actually from an enforcement of Angular2 DI rules. To describe the issue, trying to navigate to one of the routes of the application relied on the lazy loading of a module but I consistently got a blank page. Debugging the issue further, it turned out that the authentication guard that was placed on that route was failing, preventing navigation to the route. The user was definitely authenticated so this did not make sense. I dug further and it turned out that the Auth service which initially was using the rightfully authenticated logged value, was using a different value when used by the guard service. There were in effect 2 instances of what I assumed was a singleton in 2.1.x. You can see a similar description of the issue here.

You can read about Angular2’s hierarchical DI model but in the specific case of lazy loaded modules, you have to understand that they create their own branch on the Dependency Injection (DI) tree. The reason why is that as such,

it’s possible to have services that belong to a lazy loaded module, that are not accessible by the root module or any other eagerly loaded module of our application.

Why this was not enforced in 2.1.x, I don’t know but starting with 2.2.x the enforcement is definitely present. In order to create true singletons, you have to use Angular2′ hierarchical DI structure and have your lazy loaded module use the same dependency injection tree as the rest of the application. This tutorial covers the details in an excellent way and I highly recommend it.

Error: *Module is not an NgModule

Last but not least in the list of upgrade errors I ran into was this cryptic:

Error: *Module is not an NgModule

I put * because it happened for a couple of different modules for me and it’s listed as an issue on the Angular repo. This seems to be related between incompatibilities in third party libraries using versions of Angular < 2.2.x in a > 2.3.x environment. This happens when you list the module in question as part of the imports in a module. The suggested workaround that I used for now was to still import the module but to manually list the import components/services in the imports/exports section of the defining module until a fix is found.

Resolving these issues brought my app back to a seemingly working state, I might run into more issues and if I do and resolve them, I will make sure to share. If you have insights, explanations or fixes on any of the issues I listed here, please make sure to share in the comments.

Angular2: A couple of months in

Like any other Angular 1.x developer, I have followed the evolution of the next version of the platform from its announcement through its eventual release. I was not incensed by the lack of (complete )backwards compatibility, as some seemed to be on the initial announcement; the way I saw it, it was indeed a complete rewrite intended on fixing what was missed in the 1.x version using more modern concepts, which I am all for. The 1.x project I was on was mission critical so there was no way of attempting to rewrite it in 2.0 but I prepared by adopting John Papa’s excellent 1.x style guide recommendations, which if you are not, you should (Todd Motto’s is another excellent alternative). I followed the news on each of the RC releases, practiced with one of the initial SystemJS versions of the Tour of Heroes tutorial on the official site to get a feel for the platform and Typescript until I was finally able to work on a full fledged Angular2 project. This is a summary of my experience learning and using the platform two months in.

Learning Angular2

I started from the source, so after initially doing the SystemJS Tour of Heroes, I naturally went back to do it again this time using the CLI build version, having forgotten most of what I had learned initially. The second run through was informative, helping break in some of the new concepts by contrasting them at times with the way Angular 1.x did things. Overall I found the tutorial a good way to break into the framework, although some of the concepts can take a minute to sink in.

An additional hurdle for me was also making the transition to Typescript, which outside of typing can also be compiled to include most of the ES6 features. This made the learning curve a bit more steep but as with everything I have learned in programming so far, nothing builds confidence like practice. The addition of types makes code in my opinion clearer in defining contracts when creating APIs. It looks daunting from the outside but once you get of hang of it, it becomes second nature and your brain gets better at understanding and processing the code when scanning it. Now to get to the meat of the matter, what does it feel like to work with Angular2?

Angular2 is a good/great framework

There is one reason why Angular has spread in the enterprise world the way that it has and contrary to what you might have heard with all the new frameworks popping up, it’s not going anywhere anytime soon. To build large scale enterprise application within the context of a team where knowledge has to be shared, it’s in my opinion one, if not the best frameworks out there. The addition of Typescript, like it or not, has in a way reduced the barrier of entry for those who were considered before pure backend developers. Anecdotally, friends of mine in the .Net world have found the transition to buildings UIs a lot less complicated when using Angular2. Something about types and static language developers…

To get back to what makes Angular2 a good/great (depending mainly on the type of problem you are trying to solve) framework, here is a list based on my experience:

  • Angular2 is well designed, you can tell by comparison with Angular 1.x, which was built out of the concept of providing easy two-way data binding, that lessons were learned and applied .
  • Documentation is good: with the new site, the accent is really put on getting you going as fast as possible but with access to more advanced concepts not too far away. It can be confusing and overwhelming at times, but I found it so far to be useful.
  • Component based approach: That’s the way of the future, and it has been made trivial in Angular2. It’s an easy and natural way to represent and application. The only hard part, which is not unique to Angular2 is to decide how atomic to get in the creation of components.
  • Routing: The one weak point of vanilla Angular 1.x which was easily remediated with the excellent UI Router. Now it’s a module on itself with all kinds of functionality, everything you need to handle your routing needs.
  • LifeCycle hooks: Finally Angular officialised a way to hook into initialization and finalization in a way that did not feel hackish when creating views/components (Yes I know, using .component in 1.x offers the same functionality).
  • Quirky function and component names: You loved them in 1.x, they’re back in 2. forRoot anyone?

But seriously I felt Angular2 gave me all the tools needed to be able to develop my application and even more should I want to dig deeper. There were dragons though.

Angular2 is difficult to grasp (initially)

I know you all remember the famous graphic listing the Angular 1.x learning curve, i feel like that initial curve is even steeper. As i said earlier, the introduction of Typescript makes it even more intimidating. I am a seasoned veteran, with years of JS and Angular development behind me but it took me a bit of time to get comfortable with the framework, especially in trying to use best practices when designing and implementing features. It’s nothing for the most part that going back to the documentation often and Google Fu won’t fix, but it’s not always obvious. To summarize my feeling about it, as I was struggling through the initial phases of learning, I thought about all the new developers coming to the Web Development world through self learning and all these bootcamp type of schools and wondered how the learning curve would be for them. Maybe it is a case of old dog learning new tricks but with the emergence of Typescript, ES6, RxJS and functional programming, I feel like the barrier of entry into Front End Development has been kicked up quite a notch.

Angular 1.x was famously known for it’s steep learning curve but for a complete newbie, a good preparation in Javascript would be good enough to eventually understand it and become proficient in it, but now there are new concepts (mainly related to RxJS) that have been made part of the core framework which significantly kick up the complexity level of the framework. With the case of promises and observable for example, the intent for the makers of the framework is clearly to move into the Observable direction, but for the purpose of backwards compatibility, support for Promises is still around and it is also the way to handle HTTP calls used in the basic tutorial until you go deeper. It’s a good thing and a bad thing at the same time for me because as I learned Angular2, I wanted to learn the best practices at the same time, so even though the example with the Promises made sense, from everything I had read before I knew that it was not the “best” way of going about interacting with the API but I guess it helped easing me into the framework by proposing something familiar but in hindsight I would have liked a more tear the BandAid approach to getting up to speed with Angular2.

Angular2 annoyances and gotchas

  • How do I add custom tasks to the CLI?
  • Watch out for Shadow DOM style scoping: ( :host \deep\ shall be your friend). i.e if you’re losing your mind trying to override global styles in a component, read the documentation.
  • An HTTP call returns an Observable, but the HTTP call itself won’t fire if you don’t subscribe to the returned observable. It seems obvious now but in practice, not so much.

In summary, after working with it for 2 months now, I am still a believer in the Angular way. I think the team solidly positioned Angular2 to move forward with the best that is being done in Front End development moving forward and to do so, moving away from 1.x was necessary. So far I have seen good support for the 1.x version of the framework, and applications written with it at this point are “outdated” only in your mind. Angular2 is a different approach and you should definitely consider it for your next project but be prepared for a steeper learning curve.