Using The Contentful CMS In Angular

November 28, 2018 by Victor Chtelmakh

What is a CMS and why use one, the pitfalls, and an introduction to using the Contentful Headless CMS with Angular.

A Brief Introduction to the JAMstack

Last month, some of the Callibrity team attended the JAMstack conferenceThe core of the JAMstack is in the name; it’s all about building websites with client-side Javascript, using APIs (often third party or “Serverless” code hosted on the cloud), and writing Markup delivered via a global CDN to quickly serve pages to users.

The root goal of the JAMstack is to enable front-end developers to quickly build websites without relying on back-end developers. It frees the front-end dev from re-creating commonly-used functionality, such as authentication, payments, forms, or content management. Instead this functionality is provided by (usually) paid, third party services and APIs. A lot of the heavy lifting is outsourced. For example, Netlify makes hosting static content and continuous deployment a breeze. Azure Function Apps and AWS Lambda Functions can serve as a secure mini-backend in the cloud, without ever needing to host your own backend code. All of this is certainly quick and convenient, but comes with certain limitations, vendor lock-in, and potentially prohibitive costs.

Despite understanding the theoretical pros and cons, it’s hard to get a feel for something without using it. One common pattern of the JAMstack is providing content via a headless CMS. To get a feel for just how quickly a website with editable content can be built with the JAMstack, I am going to walk you through building a site allowing a car dealership to list cars for sale using Contentful. If you are wondering what a CMS is, or why you need one at all, some background and history is coming right up. If you know why you need a CMS, and specifically a “Headless” one like Contentful, feel free to skip to the code sample.

Also, keep in mind that there is a lot more to the JAMstack than using a headless CMS API to get content, and that’s part of the appeal; you can pick and choose which aspects of the JAMstack you use in a particular project, allowing gradual migrations to the stack in places where it makes sense.

 

What's a CMS and Why Use One?

Let’s say you are building a typical website for a car dealership. The website needs to display the list of cars available for sale and show any announcements and promotions on the front page. This is a fairly straight forward process - you write some html, a backend, and a database schema, then add the car list to the database and voila!

Except now any time a dealership employee wants to update the inventory or announcements, they need to write some SQL queries, which is, at the very least, an inconvenience. As an even worse “solution”, you could hardcode the car list in the html and ask the dealer to change the HTML and re-deploy the site every time inventory changes.

Neither of these solutions are realistic. What the developer ends up doing is building a separate, easy-to-use UI which allows the employees to add new cars to the website, add announcements to the front page, apply discounts, etc. Often the dealer then asks the developer to limit access to some content only to his or her “trusted” employees. Then the developer has to build a set of roles and permissions for editing content.

The last paragraph describes a typical Content Management System (CMS) - essentially a set of tools which let non-technical users modify content based on their access level. Since the vast majority of business applications require a CMS, a number of companies offer prebuilt CMS. These prebuilt CMS can be plugged into most applications, generally avoiding a lot of redundant work re-writing something that already exists. There is a very large list of CMS to choose from, depending on your project’s needs.

 

The Pitfalls of Traditional CMS

Historically, a CMS was tightly-coupled with the front end of the application. The “content manager” would not only be able to enter data via a simple UI, but also modify how this data is presented. It’s important to note that traditional CMS are WYSIWYG (“what you see is what you get”), allowing users to preview exactly what the page would look like once deployed (as well as previewing the HTML which the WYSIWYG editor generates for the user). This is great for individuals who have no development experience, but can introduce a number of issues, specifically:

  1. No ability to take the data to a new platform. The content in a traditional CMS is not pure data - it’s combined with styles and markup. This makes it difficult to move the data to another system, such as moving from VanillaJS to React or Angular.
  2. No ability to use data across multiple platforms. Since data is mixed with markup, it cannot be shared across, say, a website, a mobile app, and an IoT device. It’s only designed to be used with a website.
  3. Potential for content managers to break the aesthetic of the site. The styles introduced by someone in the CMS might conflict with the overall design of the site.
  4. Security vulnerabilities - this is a broad topic, but since many traditional CMS are backed by a SQL db and allow users to modify HTML which gets rendered by the site, this introduces potential for XSS and SQL injection attacks, among others.

 

The Solution: A "Headless" CMS

Colloquically “Headless” CMS may be better thought of as “faceless”, since these CMS don’t allow content managers to edit the appearance, or the “face” of a website. A headless CMS only deals with storing data and serving it via an API. Because a headless CMS serves raw data (usually as JSON), the data can be sent to, and displayed by, multiple applications, written in any language, on any platform. A “headless” CMS is also generally simpler than a traditional CMS and allows far few openings for security exploits. Finally, because the content really is “just data” in a headless CMS, it can be organized and moved easily. In short, “Headless” CMS make life easier for developers, but may be disliked by content managers, because they lose the ability to directly modify the style of the pages they are responsible for.

 

Enough Theory - Let's Build

As mentioned earlier - one of the difficulties of building a website for an individual or a small business is handing over a product that the customer can manage on their own. Contentful helps us do that:

1. Create a Contentful account: Head over to https://www.contentful.com/, click “Try it for free”, and enter your new account information. When prompted with “How do you usually work with content?”, choose “I develop content-rich products”, and then “Skip” the tutorial.

2. Create a “space” for your content: Content in Contenful is organized in folders that hold content of a certain type or for a certain purpose called “spaces”. If you are interested in the details, read more about spaces here. To create a space, navigate to the top left of the screen, open the dropdown menu, and click “+ Add Space”. Choose the “Free” space and name it “Vehicles”. When prompted, choose to create an “Empty” space, we will build it together.

3. Add a “Content Model”: Just like in programming, a “model” describes the fields each piece of content will have, you can think of it as a class defintion. For example, we will create a “Car” content model which specifies that cars we enter into our website will have a year, make, model, description, and price. Navigate to “Content Model” in the top navigation bar and click “Add Content Type”. Use the UI to create a “Car” model and to add the following fields to it:

	 Year (Integer)
	 Make (Short Text)
	 Model (Short Text)
	 Description (Long Text)
	 Price (Integer)
	 Images (Images)

4. Add content: Now that we know what our car data should look like, let’s add some cars. Navigate over to “Content” in the top navbar and click “Add Car”. The point of using Contentful at all is that this step should be pretty self-explanatory to anyone. Add the needed data for any car and click “Publish”. Also, note that any images that you add are now available in the “Media” tab.

That’s it! These are the basics of adding data to Contentful. The best part is that if we want to take this data somewhere else in the future, we can just pull it out as JSON via the API and map it to a model in any language. Speaking of APIs, there’s one more step.

5. Get your API Keys: We will need a set of API keys to get Contentful data from our app. Go to the Settings dropdown in the top navbar and select API Keys. You will see an Example Key 1 key set. Keep this page open, we will need these keys soon.

 

Build the Angular App

Now let’s build the Angular app. If you have never built an Angular app on your system before, start with the pre-reqs here to get your system set up.

Open your favorite terminal, navigate to the directory where you want to create your app, and run:

ng new angular-contentful-demo

This will open several prompts:

? Would you like to add Angular routing? –> No

? Which stylesheet format would you like to use? –> CSS

After you make your choices, the Angular CLI will generate a basic Angular application for you. Navigate to your new app’s directory with cd angular-contentful-demo and run ng serve --open to see the application working in your default browser.

Run npm install contentful --save to add the Contentful npm package to your app. We will need to configure and use it to get data out of Contentful.

Now we will create a service to get data from Contentful for us, run the following command:

ng generate service contentful

Navigate to the newly created service file contentful.service.ts, and copy the following code:

import { Injectable } from '@angular/core';
import * as contentful from 'contentful';
import { Observable, from } from 'rxjs';
import { map } from 'rxjs/operators';

@Injectable({ providedIn: 'root' })
export class ContentfulService {

  private client = contentful.createClient({
    space: 'YOUR_SPACE_ID_HERE',
    accessToken: 'YOUR_ACCESS_TOKEN_HERE'
  });

  constructor() { }

  getContent(contentId): Observable<any> {
    const promise = this.client.getEntry(contentId);
    return from(promise)
      .pipe(
        map(entry => entry.fields)
      );
  }
}

In short, we import the Contentful library and add the dependencies we need to make asynchronous API calls. We then configure the Contentful library with our private API keys - replace the placeholders in the code above with the keys which you should have gotten in Step 5 of setting up Formio.

Now that we have the service we need to fetch data from Contentful, let’s get data for a car via the API and display it to the user. For the sake of simplicity, we will do this in app.component.ts. Copy the following code:

import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
import { ContentfulService } from './contentful.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})

export class AppComponent implements OnInit {
  car: Observable<any>;

  constructor(private contentful: ContentfulService) {}

  ngOnInit() {
    this.car = this.contentful.getContent('YOUR_CAR_ENTRY_ID_HERE');
  }
}

All we do here is import the contentful service we created earlier and use it to get a particular car from the API via its id. To get an id for any piece of content in Contentful, navigate to the Content tab. In our case we want an id for a car, so click on the car you created earlier, click “Info” on the top right, and copy the ENTRY ID over YOUR_CAR_ENTRY_ID_HERE in the code above.

Finally, we need to display the car information once it’s loaded. We will show the car information on the main page of our app, so copy the code below over the contents of app.component.html:

<div *ngIf="car | async as loadedCar">
  <img src="https://i.ytimg.com/vi/CxuOX80SxAE/hqdefault.jpg"
       class="img-rounded"
       width="300px">

  <div>
    <h1>
      { { loadedCar.year } }
      { { loadedCar.make } }
      { { loadedCar.model } }
    </h1>
    <h3>
      { { loadedCar.price | currency } }
    </h3>
    <p>
      { { loadedCar.description } }
    </p>
  </div>
</div>

If you stopped ng serve, run it again - you should now see the data about your car displayed on the page.

The purpose of this was to show you how easy it is to pull basic data from Contentful. In reality, you would likely be pulling all the cars from the API at the same time. If you are interested in more advanced applications, the Contentful team built an in-depth example app here.

Victor Chtelmakh
Victor Chtelmakh
Senior Software Developer
Victor graduated from the University of Cincinnati with a BBA in Finance and Accounting. He spent several years trying to automate his jobs and crypto-currency trading before moving into programming professionally. MongoDB, AngularJS, and Entity Framework are among his favorite technologies.