How to create a progressive web app featuring Angular and headless CMS

Ondrej Chrastina
May 17, 2018 · 6 min read
Image for post
Image for post

Have you ever wondered how a headless Content Management System fits in with Progressive Web Apps?

I recently read my colleague Bryan’s story about Progressive Web Apps. The article talks about the implementation of a Progressive Web App (PWA) that lists interesting places stored in the headless CMS.

You could install this app on your device. It uses a service worker to cache the application and data about the points of interest. The application was written in plain JavaScript.

Having written a good share of JavaScript code, I wanted to expand on the concept using more complex frameworks.

I narrowed my choices down to three big players — React, Vue, and Angular. I chose to use Angular, because it has already support for service workers, and I wanted to use TypeScript.

Each step of this tutorial will be accompanied by a link to a GitHub commit. This way, you’ll always be able to see what the code looks like.

To run the app, just download or clone the commit and run npm install and ng serve -o. The whole code is stored in one of the branches.

Let’s get to it!

Prerequisites

  • Angular CLI v.1.7.4 installed as a global dependency via the npm package manager: npm install -g @angular/cli

Getting started

ng new cloud-sample-angular-pwa-aps

Boilerplate configuration

Image for post
Image for post
Configured boilerplate commit

There are a few steps to configure the boilerplate.

The generated code uses plain CSS by default. But, you might want to make your life easier with SCSS. To achieve this, perform these steps:

  1. Set defaults.styleExt value from cssto scssin the/.angular-cli.jsonconfiguration file
  2. Rename styles.css to styles.scss
  3. Rename /src/app.component.css to /src/app.component.scssand reflect this renaming in app.component.ts in the component declaration atribute’s styleUrls property value.

Create some initial content for the app

Lets have a look!

Image for post
Image for post
Voila, first run of the app.

Just run this command:

ng serve -o

Load the data

Image for post
Image for post
Data loading commit.

Let’s finally use the power of Angular. In this section, we will define an injectable client that allows the app to get Kentico Cloud data. I will use the same data source as Bryan used in his article.

First of all, install Kentico Cloud Delivery SDK via the following command:

npm install -P kentico-cloud-delivery-typescript-sdk

Then, create a client provider that will be used in dependency injection.

Create a new file in the /src/app folder and name it delivery-client.provider.ts. This provider module needs to export an object defining the factory used to create our client. In the code below, you can see the ID of the project in Kentico Cloud where the data is stored.

import { DeliveryClient, DeliveryClientConfig } from 'kentico-cloud-delivery-typescript-sdk';export const DeliveryClientFactory = (): DeliveryClient => {
const projectId = '975bf280-fd91-488c-994c-2f04416e5ee3';
return new DeliveryClient(
new DeliveryClientConfig(projectId, [])
);
};
export const DeliveryClientProvider = {
provide: DeliveryClient,
useFactory: DeliveryClientFactory,
deps: []
};

Next, edit app.module.ts. This is the place where you state which modules are loaded.

... 
import { DeliveryClientProvider } from './delivery-client.provider';
...
@NgModule({
...
providers: [DeliveryClientProvider]
...
})

Now we are ready to use the client in the app component.

We will set up the app.component.ts to use the DeliverClient that is auto-magically injected as a parameter to the constructor. We’ll also subscribe the component to the client’s observable and we’ll define a corresponding observer action.

import { Component, OnInit, OnDestroy } from '@angular/core';
import { DeliveryClient, ContentItem } from 'kentico-cloud-delivery-typescript-sdk';
import { Subscription } from 'rxjs/Subscription';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit, OnDestroy {
dataSubscription: Subscription;
pointsOfInterest: ContentItem[];
constructor(private deliveryClient: DeliveryClient) { }ngOnInit() {
this.dataSubscription = this.deliveryClient
.items<ContentItem>()
.type('point_of_interest')
.get()
.subscribe(response => {
this.pointsOfInterest = response.items;
});
}
ngOnDestroy(): void {
this.dataSubscription.unsubscribe();
}
}

The last step is to display the data from CMS using the Angular ngFor directive to iterate through the items and render them.

<header>
<h2>Pack and Go</h2>
</header>
<main class="main">
<div class="card" *ngFor="let poi of pointsOfInterest">
<h2 class="title">{{poi.title.value}}</h2>
<div class="content" innerHTML="{{poi.description.value}}"></div>
<a class="map-link" target="_blank" href="http://maps.google.com/?ie=UTF8&amp;hq=&amp;ll={{poi.latitude__decimal_degrees_.value}},{{poi.longitude__decimal_degrees_.value}}&amp;z=16">
Open the map
</a>
</div>
</main>

Allow adding a shortcut icon

Image for post
Image for post

This step is quite easy. It requires us to create a JSON file containing metadata about the app and link it from the head tag. The manifest file should point to multiple URLs of icons in various sizes.

We should also list the manifest.json file in an assets declaration in the .angular-cli,json configuration file.

{
...
apps: {
assets : [
...,
"manifest.json"
],
...
},
...
}

But, more importantly, link to the manifest.json file from index.html.

<link rel="manifest" href="manifest.json" />

Finally, we’ll create the manifest itself, together with all the icon renditions. Take a look at the link below to see the result.

Image for post
Image for post
Commit link with the data.

Set up the service worker

Service workers work as a proxy between the client and the internet. Depending on the actual configuration, the service worker can pre-cache the app skeleton (called the ‘app shell’) during the first load. This means that subsequent requests are blazing-fast. The service worker can also silently cache all other application data.

First of all, it is required to install the service worker module to the application.

npm install -P @angular/service-worker

Now enable the service worker in Angular in the .angular-cli.json configuration file.

{
...
apps: {
"serviceWorker": true,
...
},
...
}

Now, let’s import the service worker module to our app using the app.module.ts file.

...
import { ServiceWorkerModule } from '@angular/service-worker';
...
@NgModule({
...
imports: [
...
ServiceWorkerModule.register('/ngsw-worker.js', { enabled: environment.production })
],
...
})
˛...

The last thing is to configure the caching strategies for the app shell and the data. First we need to create ngsw-config.json configuration file under the /src folder.

For the app shell, we’ll use the default set up described in the documentation. This configuration will pre-fetch index.html , the favicon.ico , and the app shell, including the linked CSS and JavaScript bundles. Files in /assets folder are lazy-loaded.

Requests for the data from Kentico Cloud will use another caching strategy. We will define an API endpoint as a new data group and set the caching to use the freshness strategy. In the commit link bellow, you can see the whole content of the configuration file.

Image for post
Image for post
Commit link

Now we are ready to install the app on the device. For instance, in Chrome in Android, you can do so by tapping the ellipsis glyph and choosing “Add to Home screen”.

Image for post
Image for post

All right, we’re done. Despite a quick and simple implementation, the app is quite powerful and fast. And we’re free to extend it in various ways, like importing the material design or font icons.

The PWA APIs also allow us to use cool native features such as:

  • read device’s sensors
  • display push notifications
  • and use the device’s cameras.

Our app could also sense when the device transitions from online to offline, and vice versa. We could also use the automatically generated, strongly-typed models of content items from the CMS.

As you can see, creating a PWA in Angular is easy, yet allows us to extend the app much further.

freeCodeCamp.org

This is no longer updated.

Thanks to Jan Lenoch

Ondrej Chrastina

Written by

Traveler, tech enthusiast, and Developer Advocate at Kentico (Kentico EMS and Kentico Kontent).

freeCodeCamp.org

This is no longer updated. Go to https://freecodecamp.org/news instead

Ondrej Chrastina

Written by

Traveler, tech enthusiast, and Developer Advocate at Kentico (Kentico EMS and Kentico Kontent).

freeCodeCamp.org

This is no longer updated. Go to https://freecodecamp.org/news instead

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store