AngularJS migration — Leveraging Progressive Web Apps to target multiple platforms with a near same codebase.

Progressively enhance AngularJS to adapt to the device, browser capabilities, and network availability

Pascal Maniraho
Simple
10 min readSep 6, 2017

--

faster — Photo by Maico Amorim on Unsplash

Intro

JavaScript is evolving from “learn once, write anywhere to “write once, run anywhere type of language, or “universal” if you wish. Technologies to reuse most of your code in a browser, server, desktop, and mobile platforms mature every single day.

We’ve got you covered If you are interested with Using PWAs with React instead.

On another hand, Angular is morphing into a platform. Latest versions(2, 4.x, 5.x) are sharpening super powers to run well on multiple platforms (server: server-side rendering, mobile, web, desktop: electron and IoT) with a near same code base. React is a whole new story.

Desambiguation: the “universal” mentioned above is not this universal javascript a.k.a “isomporhic”

Nota: this is the 3rd in a series of blog posts about soft code migration, as I move Hoo.gya platform that helps you to rent various stuff from your friends, neighbours and coworkersfrom Angular 1.x to Angular 4.x. Your recommendation (👏 👏 ) is my motivation to followup with a new article. I will be glad to help you if you have any question(leave questions in comments below)! I hope this post helps you! Here is the First article and second article, you are currently reading the third.

Native

Native code is always “close to metal”. That comes with privileges to access to low level resources. With power, comes responsibilities(or sacrifices). It is hard to believe, but in native apps world, Continuous Delivery cycle is calculated in days, not hours. It takes time to approve published apps on various App Stores. This makes releasing multiple times a day feasible only on your web stack. Keep in mind that there is no universal native mobile apps programming language. Every platform has its own programming language(Android: Java/Kotlin, iOS: ObjectiveC/Swift). This fact induces complexity to quality of service(user experience). Each platform tends to have its kind of bugs, multiple developers+testers, not to mention investments in terms of time and money. Elliot published thoughts on why native mobile are “probably” doomed: Native Apps are Doomed, and a followup: Why Native Apps Really are Doomed: Native Apps are Doomed pt 2, and I personally can’t agree more. The way to unify apps development today starts with Progressive Web Apps(PWA).

Options

You have multiple options to target multiple platforms with near same codebase, but PWA makes sense. If you use React in one of your projects instead of Angular, you are covered. In fact, it is possible to write near same code for iOS/Android with React Native than it is in Angular. No offence! With a few hacks, near same codebase Angular for both iOS/Android is achievable by learning NativeScript. If your code is highly composeable, which it is, most of codebase can be re-used on desktop(with electron and pals), even server side(nodejs). If you already used libraries like underscore(or lodash), you get the picture!

Progressive Web App

PWA combines various frontend development paradigms into coherent-verifiable steps to enhance user experience based on device and browser capabilities. Your Continuous Delivery cycle is now calculated in hours, not days. In addition to traditional Progressive Enhancement and Responsive Design, PWA enforces secure communication over SSL, fast data transfer over HTTP2, Offline support via Service Worker (Cache API with a possible fallback “hack-ish” to HTML5 AppCache), background tasks for server synchronization via a WebWorker-like API. Service Worker specification allows PWA to behave more like native apps with possibility to receive Notification Push (not available yet on Safari iOS), Geolocation API(Geofencing). Once installed, PWA becomes discoverable and linkable like any app(on Android, Apple is catching up on this).

CSS Async

Lazy Loading CSS without using JavaScript. Conditional stylesheets help to isolate browser specific initialization. Lazy loading on another hand helps first paint without the browser waits to download all CSS.

lazy load combining with conditional stylesheet — source and carbon

You may find following links useful to audit, refactor your CSS for responsiveness and performance(lazy load or inlining). Refactoring CSS for progressive enhancement can be achieved by 1) taking existing CSS code is considered to be for smallest device supported 2) Add media query from medium to largest device supported by your code 3) Adopting modular design (BEM, SMACKS or OCSS).Bootstrap ease transition with by applying meta-css-classes to existing CSS classes. To move fast, since your existing code base was designed for desktop(larger medium), it makes sense to delete(or hide) components that are not required for mobile. Destruction happens faster than creation. Programming new stuff takes more time than deleting un-wanted stuff. As a quick example, CSS bootstrap provides .hidden-[xs|ms|md|lg] and .visible-[xs|ms|md|lg]-[block|inline|inline-block] utility classes that you can leverage to add functionalities depending on screen size. That saves time.

Bootstrap ease transition with by applying meta-css-classes to existing CSS classes. To move fast, since your existing code base was designed for desktop(larger medium), it makes sense to delete(or hide) components that are not required for mobile. Destruction happens faster than creation. Programming new stuff takes more time than deleting un-wanted stuff. As a quick example, CSS bootstrap provides .hidden-[xs|ms|md|lg] and .visible-[xs|ms|md|lg]-[block|inline|inline-block] responsive utility classes that you can leverage to add functionalities depending on screen size. That saves time.

Install-able PWA

No mobile strategy? No problem. Applying principles of PWA to legacy web apps payoff in long run. When cache is leveraged, the web app loads faster since most static resources are persisted on client. In addition, you save on bandwidth. When push notifications are leveraged, user re-engagement becomes as easy as on native apps(PS: Apple doesn’t support push notification on Safari Mobile). With background tasks that sync data with server whenever network is re-established, PWA guarantees BASE(basically available, soft state, eventually consistent: similar to ACID but in distributed systems), which is quite hard to come by in classic web apps scenario.

Manifest

The manifest.json makes your app installable on Android. The manifest also tells Safari where to find vital resources for augmented home screen experience. To install PWA, one manifest and an HTML tag are required. Apple devices allow you to add to home screen instead. Which is not enough, but not bad. Installed PWA will not enable features close to the metal, such as the Contact API. That can be remedied by a deep integration with third party services such as Google/iCloud/etc, to be able to access to users’ Contact List, etc.

# Add this line 
# <link rel="manifest" href="/manifest.json"/>

To generate icons and pre-validate your manifest, you may need this app. Example of a manifest looks as following:

An Example of Manifest File — carbon
Testing via Chrome DevTool will give you something similar(this is my case). Icons generated from link

If You are using Angular 2 already, some inspirations can be found on this blog post: “progressive web apps with angular 2+” by Houssein Djirdeh.

Full screen mode

With a couple lines of HTML code in header, iOS Safari provides a way to leverage full screen mode even to non installable web apps. Full screen mode on iPhone/iPad(Safari), “minimal-ui” prevents extra header and footer, whereas “-scale” meta data prevents zooming(line#1). Line#2 changes status bar appearance. Line#3 turns the title in black-translucent. Title used on home-screen is added in line#4, use line#5 to add custom icon. Line#6 specifies launch screen image. If you already have an on AppStore, you can add line #7 to advertise via a smart app banner. More explanations can be found on Apple website.

Full Screen mode with Apple device support — source

Chrome’s Web Fundamentals has couple more examples you can dig into for better understanding. Stripped down yet more explanatory example, can be found here on medium: “Service Workers replacing AppCache: a sledgehammer to crack a nut”. Adding App install banners for Chrome, more on installable web apps specifications or Application shell source: Smashing Magazine and Firefox pinned Apps. Registering worker with Firefox use:

HTML5 Camera and Camcorder

It is quite rare to find apps that don’t capture(upload) pictures. HTML <input type=file/> plays a big role in this regards. However, your app will not have access to filesystem. The workaround is HTML5 Camera and Camcorder specification. It will help your code to snap photos while on mobile browsers, and fallback to input or drag-and-drop on regular browsers. Using existing code, you can enhance Camera or Video Recording thanks to HTML5 properties.

<input type="file" accept="image/*"> #taking picture
<input type="file" accept="video/*;capture=camcorder"/> #video rec.

Service Workers

Service workers are one of the building blocks of PWAs. The service worker relies on Cache API to cache static resources, and serve them whenever the application fetches resources from a remote server. Since not all browsers implements Cache API, Service Worker fallbacks to App Cache API(HTML5 AppCache). For that to work, AppCache manifest has to be added the old way. Note that this approach is not qualified by Lighthouse(one-sided decision). If you are on hunt of AppCache support, Jake Archibald wrote a nice article back in 2012 that can get you up and running: Application Cache is a Douchebag. To initialize your Service Worker, you will need cache version and static files(for app shell).

# service worker to load app shell
var version = 'version:or:app-name';
#files to cache
var files = ['/index.html',
'/style.css',
'/script.js',
'/favicon.ico'];

Service Worker vs Web Worker vs Web Socket

It is important to mention that Service Workers are distinct, independent and therefore different from Web Workers. It is also important to mention that both Service Workers and Web Workers are distinct, and therefore different, from WebSockets. If you want to skip and get a “relatively” quick deep understanding of those 3 technologies, head to “The difference between Service Workers, Web Workers and WebSockets” by Aaron T. Grogg.

  • jQuery of Service Workers. To get started, you can go Vanilla, or use some libraries. There are two that I find quite useful: UpUp and Workbox(by Google). PS: There is no such a thing as “is jQuery but for Web Workers ;-)
  • UpUp. The UpUp library helps to get started with Service Workers really quick. The library is far from being the jQuery of Service Workers, but really makes things easier. Guys at OAuth made a quick intro that leverages UpUp speedy setup: “Creating Offline-First Web Apps with Service Workers”. To dig deeper, the UpUp tutorial from the developer is: “Getting Started with Offline First using UpUp”.
  • Workbox. I have no idea how hard, or simple this library is, but first impression is a Swiss knife, whereas a kitchen knife can do the job. Gives access to offline support(obviously), offline analytics and background sync.
  • Modernizr. This library helps to detect if a browser supports certain parts of a specification. Since Safari doesn’t support Service Worker yet, this will eventually to detect if your browser can use your library or not.
  • Service Worker Events. To dive into it, Mozilla drafted a couple of documentations such as Service Worker API and Using Service Workers. In addition to Worker Registration phase, you will have to provide handling of install, fetch and activate events.

Service Worker Registration

To enable Service Worker, a code similar to the following is all you need. Latest version of Modernizr, can beautify “if” condition as “if(Modernizr.serviceworker)”.

Registering a service worker — source

Service Worker Installation

The install event initializes your cache. This is the place you add your static resources.

Service Worker Installation code — source

Service Worker Cache

The fetch event requires a strategy. Wide range of choices are available. Important ones being “cache with fallback to network” or “network with fallback to cache”. At end of the day, you will make a decision based on your use case. Jake Archibald has a cookbook on offline that can provide deep insight. The offline cookbook.

Source

Service Worker Clean Cache

The activate event holds a place to clean your cache. The alternative to reset your cache(especially in production), is to upgrade the version(or add hash to your cache name). If you are hungry for more, Pascal Precht ʕ•̫͡•ʔ had a talk about these events. Slides can be found here: Angular Service Workers.

Cleaning the cache — source

Outro

Gosh, You rock If you read all above goodness! Thank You! Like last time, YOUR recommendation(👏 👏 👏) motivates me to follow up this post with techniques I use while upgrading Hoo.gy — a platform that makes it possible for you to rent stuff from your friends and neighbours. It is green sharing economy, to curb consumerism and save you thousands in credit card debts, and our planet;-).

--

--

Pascal Maniraho
Simple
Editor for

Web lover, code crafter, beer drinker, created http://hoo.gy, Montrealer, and training to run a half-marathon :-)