Angular App With Contentful CMS
Tags: cms, angular, aws, frontend
Published: 2019-10-28 20:17:11

Have you ever wondered how to create an app in Angular using a headless CMS product? After doing a lot of research on multiple CMS products like:

-Contentful
-CloudCMS
-ButterCMS
-MURA CMS

I decided to go with Contentful.

Contentful is content infrastructure. Its platform lets you create, manage, and distribute content to any platform. Unlike a CMS, it gives you total freedom to create your own content model so you can decide what content you want to manage. Contentful provides you RESTful APIs so you can deliver your content across multiple channels such as websites, mobile apps (iOS, Android, and Windows Phone) or any other platform you can imagine (from Google Glass to infinity). With an uncluttered user interface, Contentful is an efficient tool for creating and managing your content online, either alone or in a team. You can assign custom roles and permissions to team members; add validations depending on the kind of content you have to insert; and add media such as images, documents, sounds, or video.

I decided to use Contentful for an Enterprise project for one of the biggest companies in the USA.

A few things I like about Contentful:

-It allows multiple users
-It's easy for the non-technical minded
-It streamlines scheduling
-It improves site maintenance
-Design changes are simple
-It helps you manage content
-You're in control
-Try for free
-Works with multiple frontend technologies like Angular, React, Vue, etc
-They have a slack channel
-Good community support
-GraphQL friendly

Did I mention Contentful is built on top of AWS? :)

**This tutorial requires some level of knowledge with Angular.**

<h1>Working with Contentful</h1>
1) Create an account with <a href="https://www.contentful.com">Contentful</a>.

2) Create a new free space.
<img src="https://thepracticaldev.s3.amazonaws.com/i/i5cdfs35h633atndxqsk.png"/>

3) Enter the new space name E.g <i>angular-cms</i>.
4) Make sure the option <strong>Create an empty space</strong> is selected.
5) Click on <strong>PROCEED TO CONFIRMATION</strong>.
6) Click on <strong>CONFIRM AND CREATE SPACE</strong>.
7) After your space has been created, at the very top click on <strong> CONTENT MODEL</strong>.
8) Then click on the blue button <strong>Add Content Type</strong>.
9) A modal will appear. Enter your content type name as Angular Post. Write down the content type ID (should be angularPost).
10) Click <strong>CREATE</strong>.
<img src="https://thepracticaldev.s3.amazonaws.com/i/00cpcww77d66t4bhn5dh.png" width="500px"/>
11)Time to add some fields. On your right side, click on the blue button <strong>Add Field</strong>.
12) Create a new field called <strong>title</strong> of type TEXT.
13) Create a new field called <strong>body</strong> of type TEXT.

<i>Note: Right below where you enter the name of the field, make sure you select <strong>Long text, full-text search</strong></i>.

14) Create a new field called <strong>image</strong> of type MEDIA.
<i>Note: Below where you enter the name of the field, make sure you select <strong>One file.</strong></i>
15) On the top right corner, click the green button <strong>SAVE</strong>.
16) In the top navigation bar, click on <strong>CONTENT</strong>.
17) Click the "Add Angular Post".
18) Enter the Title and Body for your new post. Create at least 2 posts.
19) Now click on <strong>Create a new asset and link</strong>.
20) A modal will open; enter the name of your asset, description (optional), and select your image file.
21) On the right sidebar, click on <strong>PUBLISH</strong>.
22) Click the "<" arrow at the top left of the modal to go back to the creation of your post.
23) On the right sidebar, click on <strong>PUBLISH</strong>.
24) In the top navigation bar, click on <strong>SETTINGS/API Keys</strong>.
25) On the right sidebar, click on <strong>Add API Key</strong>.
26) Copy your Space ID and your Content Delivery API Access Token, save them somewhere.
27) On the top right corner, click </strong>SAVE</strong>.


<h1>Connecting Angular with Contentful</h1>

<i>Note: I'm not going to get into best practices with angular, I'm only showing you how to use Contentful with Angular.</i>


1) Create a new Angular app. Select SASS as your CSS compiler, and the option to create the routes file.

   ```javascript 
   ng new angular-cms
   ```
2) Create a new service.
  
   ```javascript 
   ng g s contentful
   ```
   
3) Install the Contentful NPM package.

   ```javascript 
   npm i --save contentful
   ```
4) At the top of you contentful.service.ts file, add the following line:
   ```javascript 
   import { createClient } from 'contentful';
   ```
5) Time to use your Space Id and accessToken.
   ```javascript 
    private CONFIG = {
     space: 'ENTER YOUR SPACE ID HERE',
     accessToken: 'ENTER YOUR ACCESS TOKEN HERE',
     contentTypeIds: {
      //The property name angularPost can be whatever
      //what matters in the value which is the content type Id.
      angularPost: 'angularPost'
     }
    };
   ```

  <i>Note If you didn't save your content type ID somewhere, you can get it as followed: contentful -> content model -> click your content model name -> right sidebar look for your content type ID.</i>

6) Create a new private variable.
   ```javascript
     private cdaClient = createClient({
       space: this.CONFIG.space,
       accessToken: this.CONFIG.accessToken
     });
   ```
7) At the top of your file, import <strong>from</strong> from RxJs.
   ```javascript
   import { from } from 'rxjs';
   ```

8) Now let's create a function to retrieve all of our posts.
   ```javascript
   getPosts(query?: object): any {
    return from(
      this.cdaClient.getEntries({
        ...Object,
        content_type: this.CONFIG.contentTypeIds.angularPost,
        query
      })
    ).pipe(map(posts => posts.items));
   }
   ```

9) Time to create a function to retrieve a single post.
   ```javascript
   getPost(id: string): any {
    return from(this.cdaClient.getEntry(id));
   }
   ```
10) Inside of your constructor, let's initialize the our getPosts() method.
   ```javascript
   constructor() {
    this.getPosts();
   }
   ```
11) The contenful.service.ts file should look like this:
https://github.com/devpato/angular-contentful-cms/blob/master/src/app/contentful.service.ts


12) Let's create to new 2 components.

   ```javascript 
   ng g c posts-page
   ```
   ```javascript 
   ng g c post-details
   ```

13) Now go to your posts-page component, and enter the following lines of code:
   ```javascript
   import { ContentfulService } from '../contentful.service';
   ...
   posts$ = this.contentfulService.getPosts();
   constructor(private contentfulService: ContentfulService) {}
   ```
14) Your code should look like this:
https://github.com/devpato/angular-contentful-cms/blob/master/src/app/posts-page/posts-page.component.ts

15) Clear your post-page.component.html and paste:

   ```html
   <div class="content">
    <div class="posts">
     <ng-container *ngIf="posts$ | async as posts">
      <div *ngFor="let p of posts" class="post">
        <div class="post-image">
          <img src="{{ p.fields.image.fields.file.url }}" />
        </div>
        <a [routerLink]="['/posts/', p.sys.id]">
          <div class="post-title">
            {{ p.fields.title }}
          </div>
        </a>
      </div>
     </ng-container>
    </div>
   </div>
   ```

16) Copy the code from the link listed below, and paste it to your posts-page.components.scss

https://github.com/devpato/angular-contentful-cms/blob/master/src/app/posts-page/posts-page.component.scss

17) Time to go to our post-details.component.ts and add the following:
   ```javascript
   import { ContentfulService } from '../contentful.service';
   import { ActivatedRoute } from '@angular/router';
   ...
   id = this.route.snapshot.paramMap.get('id');
   post$ = this.contentfulService.getPost(this.id);

   constructor(private contentfulService: ContentfulService, private 
   route: ActivatedRoute) {}
   ```
18) Your code should look like this:
https://github.com/devpato/angular-contentful-cms/blob/master/src/app/post-details/post-details.component.ts

19) Clear your post-details.component.html and paste:
   ```html
   <div class="post-details" *ngIf="post$ | async as post">
    <div class="post">
     <div class="post-header">
      <div class="header--image">
        <img src="  {{ post.fields.image.fields.file.url }}" />
      </div>
      <div class="header--title">
        {{ post.fields.title }}
      </div>
     </div>
     <div class="post-body">
      {{ post.fields.body }}
     </div>
    </div>
   </div>
   ```

20) Copy the code from the link listed below, and paste it to your post-details.components.scss
https://github.com/devpato/angular-contentful-cms/blob/master/src/app/post-details/post-details.component.scss

21) Now let's work with the routes. Add the following routes to your app-routing.module.ts

   ```javascript
   const routes: Routes = [
    {
     path: '',
     component: PostsPageComponent
    },
    {
     path: 'posts/:id',
     component: PostsDetailsComponent
    }
   ];
   ```
22) Now run your server. You should see something like this:

<i> Note: the information on the posts may be different. It depends on whatever you entered when creating a new Angular post. </i>

<img src="https://thepracticaldev.s3.amazonaws.com/i/ggs6fxcbufcq4i9qcb4j.png" />

23) Click on one of the items, it should take you to its details page.
<img src="https://thepracticaldev.s3.amazonaws.com/i/m6s1oqn1r2jf7njn8bpm.png" />