GSD

How to Add a Blog to Your Angular App with ButterCMS

Posted by Taminoturoko Briggs on October 11, 2023

After a website has been planned and designed, all that’s left is to develop and implement it. This is something we will want to do as quickly as possible using reliable technologies so that we can move on to other important things.

Over the years, many great tools and platforms have been introduced to aid in the development process. Among these is ButterCMS, a headless content management system (CMS) that abstracts all the lengthy server-side code that would take days to complete. It does this by providing an easy-to-navigate dashboard where we can create and manage our content and a content delivery API to access that content using our preferred client framework and tools.

In this tutorial, we will learn about ButterCMS and how to use it with Angular to build a blog.

Why use Angular?

Angular is a mature front-end framework developed and maintained by Google that is used for building single-page applications (SPAs). It is known for having a great ecosystem, detailed documentation that clearly explains its concepts and practices, and support for building not only web apps but also progressive web apps (PWAs) and mobile apps. 

Since Angular is built on TypeScript, which is a strict syntactical superset of JavaScript, applications built with it are more secure, more error-free, and easier to debug. Angular uses a component-based approach that allows the creation of individual self-contained pieces of code which can be reused throughout our app—speeding up the development process and creating code that is easier to maintain.

As mentioned earlier, Angular is mature, so while using it we can rest assured that all the tools and resources needed for creating our desired front-end experience are readily available.

Angular CMS Banner CTA

Why use ButterCMS?

ButterCMS, being a headless CMS, acts as a repository for storing content which can then be accessed via its content API using native tools. Unlike a traditional monolithic CMS whose structure can limit your user experience, a headless CMS like ButterCMS gives us full control over the frontend by acting as your backend content repository, allowing us to create custom experiences for users. On top of that, ButterCMS is cloud-based and serverless, which means we don’t have to worry about scaling, security, or maintenance—saving us time to focus on more important problems.

Another great feature ButterCMS provides that we will be utilizing in this tutorial is its built-in blog engine. It includes a content structure for creating SEO-friendly blog posts and an interface for managing created posts.

Keep in mind that while we will be using the ButterCMS blog engine for this tutorial, you can also quickly create a custom blog page using ButterCMS page types.

Tutorial prerequisites

To follow along with this tutorial, you need the following:

  • Knowledge of Angular
  • Angular CLI installed
  • Node.js v16 or greater installed on your system
  • An active ButterCMS account (New accounts have a free trial period of 30 days.)

The code for this tutorial can be found in this GitHub repo.

Building a blog with Angular and ButterCMS

We will start by building the UI for our blog app. After that, we will set up ButterCMS, which will hold our blog content that will then be fetched and displayed in our app. Here is what we will be building in this tutorial:

Finished blog homepage

Setting up Angular

To generate a new Angular app, enter the following commands in the terminal:

ng new angular-blog

When prompted, pick CSS for styling and add router to the app. The above will create a starter angular app called angular-blog.

Let’s clean up the app a bit, removing code that is not needed. Open the created app in a code editor and first modify src/app/app.component.html to the following:

<div></div>

Next, add the following styling to src/styles.css:

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

.wrapper {
  width: 95%;
  margin: 0 auto;
  max-width: 1100px
}

body {
  min-height: 100vh;
  background-color: #f6fbff;
}

Now, let’s start the development server with the ng serve command.

Creating the blog’s UI

The blog will have a home page where all blog posts will be displayed and a details page for viewing individual posts. Let’s create the pages and the components to be used in them.

Open the terminal and make sure to enter the following commands in the root directory of the app. For pages:

for page in home detail; do ng g c --skip-tests=true "pages/${page}"; done

For a component:

ng g c --skip-tests=true "components/card"

After entering the above commands, here is what the folder structure of the app folder will look like:

app
 ┣ components
 ┃ ┗ card
 ┣ pages
 ┃ ┣ detail
 ┃ ┗ home

Now, let’s create the routes for our pages. Head over to app/app-routing.module.ts and add the following imports and modify the routes variable to the following:

import { DetailComponent } from './pages/detail/detail.component';
import { HomeComponent } from './pages/home/home.component';

const routes: Routes = [
  {path: '', component: HomeComponent},
  {path: 'detail/:slug', component: DetailComponent}
];

Next, head over to app/app.component.html and modify it to the following:

<div>
  <router-outlet></router-outlet>
</div>

Now, let’s create the pages, starting with the home page which will contain blog cards which display brief information about each blog post. Let’s first create the blog card component.

Head over to app/components/card/card.component.ts and add the following import:

import { Input } from '@angular/core';

Next, add the following properties in the component before the constructor:

@Input() slug!: string;
@Input() image!: string;
@Input() date!: string;
@Input() title!: string;
@Input() summary!: string;
@Input() authorImage!: string;
@Input() authorName!: string;

Above, we have declared the properties which will be passed down from the parent.

Next, modify the HTML file card.component.html to the following:

<div class="card" [routerLink]="['detail', slug]">
  <img [src]="image" />
  <div class="card__details">
    <div>
      <span class="mgn">{{ date }}</span>
      <h3>{{ title }}</h3>
      <p class="mgn">{{ summary }}</p>
    </div>
    <div class="card__author">
      <img [src]="authorImage"/>
      <span>{{ authorName }}</span>
    </div>
  </div>
</div>

Above, using the routerLink directive, we have made the container div to navigate to the details page when clicked with the slug attached to the URL. We are attaching the slug because it will be used later to fetch and display the corresponding blog post on the details page.

Next, in the card.component.css file, add the following styles:

.mgn {
  margin-bottom: 15px;
  display: block;
}
.card {
  max-width: 350px;
  cursor: pointer;
  box-shadow: 0 2px 6px gainsboro;
  border-radius: 5px;
  height: 100%;
  display: flex;
  flex-direction: column;
}
.card:hover {
  transform: scale(1.01);
}
.card > img {
  object-fit: cover;
  width: 100%;
  height: 200px;
}
.card__details {
  padding: 10px;
  display: flex;
  flex-direction: column;
  flex: 1;
  justify-content: space-between;
}
.card__details span:first-child {
  color: gray;
}

.card__details p {
  font-size: 16px;
  color: rgb(63 63 66);
}
.card__author img {
  object-fit: cover;
  width: 50px;
  height: 50px;
  border-radius: 100%;
  vertical-align: middle;
}
.card__author span {
  margin-left: 10px;
}

Now, let’s render the created component and design the home page. In the home page, we will be using the ngModel directive for two-way binding for the search bar, so we will need to add the forms module to our app. To do this, head over to src/app/app.module.ts and add the following imports:

import { FormsModule } from '@angular/forms';

Then, modify imports to look like the following:

imports: [
    // ...
    FormsModule,
],

Next, head over to the app/pages/home/home.component.ts file and define the following property which will hold the value entered in the search bar:

searchValue = ''

Next, let’s create the search bar and render the created card component by modifying the HTML file home.component.html to the following:

<div>
  <div class="hero">
    <div class="wrapper">
      <header>
        <h2>Angular Blog</h2>
        <form>
          <input 
            placeholder="Search blog" 
            [(ngModel)]="searchValue"
            name="search-value"
            />
          <button>Search</button>
        </form>
      </header>
      <h2>Explore Angular based contents</h2>
      <p>
        Lorem ipsum dolor sit amet, consectetur adipiscing elit. 
        Mauris et porta erat, in sodales augue.
      </p>
    </div>
  </div>
  <div class="wrapper cardList">
    <app-card
      slug="slug of the post" 
      summary="Lorem ipsum dolor sit amet, consectetur adipiscing elit"
      title="Example blog post"
      image="https://images.unsplash.com/photo-1667586091163-e759ac830a1b?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1414&q=80"
      authorImage="https://images.unsplash.com/photo-1650196186567-b42a5de15fb5?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1972&q=80"
      authorName="Ray"
      >
    </app-card>
  </div>
</div>

Right now we are passing dummy data to the card component just so we can see what we are building. Later on, we will replace the dummy data with data from ButterCMS.

Next, let’s add some styling. Add the following styles to home.component.css:

header {
  display: flex;
  align-items: center;
  justify-content: space-between;
}
header h2 {
  text-align: center;
  margin: 20px 0;
}
form input, button {
  height: 30px;
  padding: 0 5px;
  border: none;
}
form button:hover {
  background-color: gainsboro;
}
.hero {
  background-color: rgb(215,59,95);
  height: 300px;  
  text-align: center;
  color: white;
}
.hero .wrapper > h2 {
  margin-top: 70px;
  font-size: 28px;
}
.hero .wrapper p {
  font-size: 18px;
}
.cardList {
  display: grid;
  margin-top: 20px;
  place-content: center;
  grid-template-columns: repeat(auto-fill, 350px);
  gap: 15px;
}

Now, if we open our app in the browser, we will see the following:

Rendered blog UI

Now, let’s work on the details page. For now, we will just add its styling. We will do the rest after we have fetched our data from ButterCMS. So, head over to app/page/detail/detail.component.css and add the following styling:

.blogDetail {
  width: 95%;
  max-width: 900px;
  margin: 50px auto;
}
.blogDetail__head span:nth-child(2):after {
  content: '.';
  margin-left: 5px;
  vertical-align: top;
}
.blogDetail__head span {
  margin-left: 5px;
  color: rgb(128, 128, 128);
}
.blogDetail > h1 {
  margin-bottom: 15px;
}
.blogDetail > img {
  margin: 15px 0;
}
.blogDetail img{
  width: 100%;
}
.blogDetail .blogDetail__profileImg {
  width: 30px;
  height: 30px;
  border-radius: 100%;
  vertical-align: middle;
}
.blogDetail__body p {
  color: rgb(62, 60, 60);
  font-size: 18px;
}
.blogDetail__body h2, h3{
  margin: 20px 0 5px;
}
.blogDetail__body img, video {
  margin: 5px 0;
}

Setting up ButterCMS

Let’s use the blog engine to create a blog post to be displayed on the front-end and get the Read API Token which will enable us to work with ButterCMS.

Head over to the ButterCMS website and set up a user account if you do not have one already. After logging in, we will be directed to our dashboard where we can navigate to the blog engine by clicking on the Blog Posts icon in the sidebar.

Blog Posts tab in ButterCMS

We will see the following page:

Blog posts page in the ButterCMS

Here, an example post has already been created for us which we can work with, but let’s create another to get familiar with how it’s done.

Click on the New Post button at the top-right of the page. On the next page, write your blog content with the WYSIWYG editor then fill out the rest of the inputs and click on the Publish button at the top-right of the page. For this tutorial, we can just use dummy data to fill out the fields.

Below is a GIF that demonstrates how to add a new blog post:

Adding content to blog post

Now, to get our read API token, hover over your image at the top on the sidebar and click on Settings in the dropdown that appears and we will be taken to the following page where we will see the token:

Read API token

Copy the token and store it in the src/environments/environment.ts file in our app, which should look like this:

export const environment = {
  production: false,
  readApiToken: "<your-read-api-token-here>"
};

Angular CMS Banner CTA

Working with ButterCMS in our application 

Now that everything has been set up, we are down to consuming our content created with the ButterCMS blog engine. For this, we can either use the content API or ButterCMS SDK, both of which function as stated in the API reference. For this tutorial, we will be using the content API.

We will start by displaying all our blog posts on the home page. Since we will be using HttpClient for sending requests, let’s add its module to our app. Head over to app/app.module.ts and add the following import:

import { HttpClientModule } from '@angular/common/http';

Next, modify the imports property by adding HttpClientModule to it:

imports: [
  //..
  HttpClientModule
]

Next, head over to app/pages/home/home.component.ts and add the following import:

import { HttpClient } from '@angular/common/http';
import {environment} from '../../../environments/environment'

Next, modify HomeComponent to the following:

export class HomeComponent implements OnInit {

  blogPosts:any[] = []
  searchValue = ''

  constructor(private http: HttpClient) { }

  ngOnInit(): void {  this.http.get(`https://api.buttercms.com/v2/posts/?exclude_body=true&auth_token=${environment.readApiToken}`).subscribe((res: any) => {
      this.blogPosts = res.data
    })
  }
}

Above, in the ngOnInit method using HttpClient, we fetch all blog posts from ButterCMS and store them in the blogPosts property. 

To display the blog posts, modify the home.component.html file to the following:

<div>
  <h2>Angular Blog</h2>
  <div class="wrapper cardList">
    <app-card
      *ngFor="let blogPost of blogPosts"
      [slug]="blogPost.slug" 
      [date]="blogPost.created"
      [summary]="blogPost.summary"
      [title]="blogPost.title"
      [image]="blogPost.featured_image"
      [authorImage]="blogPost.author.profile_image"
      [authorName]="blogPost.author.first_name + blogPost.author.last_name"
      >
    </app-card>
  </div>
</div>

Now, the fetched blog post will be displayed, but we will notice that the displayed dates aren’t in the right format. To fix this, head over to app/components/card/card.component.ts and modify the ngOnInit method to the following:

ngOnInit(): void {
  this.date = new Date(this.date).toISOString().split('T')[0]
}

With this, when we open our app in the browser, we will see the following:

Rendered blog with new content from ButterCMS

Now, let’s make the search bar work where we will use the text entered in the search bar to filter posts by their title and display the results. For this, we will need to create a property that will hold the filtered post and be used to display the UI rather than directly filtering the fetched posts in the blogPosts property so that no post will be lost even after searching.

First, add the following property in HomeComponent:

filteredBlogs: any[] = []

Since the filteredBlogs property will be used to display the UI, it needs to contain the fetched blog post when the app loads. To do this, modify the ngOnInit method to the following:

ngOnInit(): void {
this.http.get(`https://api.buttercms.com/v2/posts/?exclude_body=true&auth_token=${environment.readApiToken}`).subscribe((res: any) => {
    this.blogPosts = res.data
    this.filteredBlogs = this.blogPosts
  })
}

Next, let’s create the search method by adding the following lines of code after the constructor:

handleSearch() {
  this.filteredBlogs = this.blogPosts.filter((blog) => (
    blog.title.trim().toLocaleLowerCase()
    .includes(this.searchValue.trim().toLocaleLowerCase())
  ))
}

Now let’s call the above method when the search form is submitted and display the UI with the filteredBlogs property.

Head over to the HTML file home.component.html and modify the opening form tag to the following:

<form (submit)="handleSearch()">

Next, modify the rendered app-card component to look like the following:

<app-card
  *ngFor="let blogPost of filteredBlogs"
  // ...
 >
 </app-card>

With this, the search bar should now be working.

Angular blog search feature

Now, let’s work on displaying an individual blog post on the details page. Head over to app/pages/detail/detail.component.ts and add the following imports:

import { ActivatedRoute } from '@angular/router';
import { HttpClient } from '@angular/common/http';
import {environment} from '../../../environments/environment'

Next, modify DetailComponent to the following:

export class DetailComponent implements OnInit {

  blogPost:any
  date!: string

  constructor(
    private route: ActivatedRoute,
    private http: HttpClient
  ) { }

  ngOnInit(): void {
    const slug = this.route.snapshot.paramMap.get('slug')  this.http.get(`https://api.buttercms.com/v2/posts/${slug}/?auth_token=${environment.readApiToken}`)
    .subscribe((res: any) => {
      this.blogPost = res.data
      this.date = new Date(this.blogPost.created).toISOString().split('T')[0]
    })
  }
}

Above, after injecting the required services by modifying the constructor in the ngOnInit() method, we get the slug which is attached to the details page URL and use it to fetch the corresponding blog post from ButterCMS. Then, we store the result in the blogPost property.

Now let’s display the blog post on the page. Modify the detail.component.html file to the following:

<div class="blogDetail">
  <h1>{{blogPost?.title}}</h1>
  <div class="blogDetail__head">
    <img [src]="blogPost?.author.profile_image" class="blogDetail__profileImg" />
    <span>{{blogPost?.author.first_name + ' ' + blogPost?.author.last_name}}</span>
    <span>{{date}}</span>
  </div>
  <img [src]="blogPost?.featured_image"/>
  <div class="blogDetail__body" [innerHTML]="blogPost?.body"></div>
</div>

With this, when we navigate to the details page by clicking on a blog card, we will see the content of that blog post. But there are a few things that still need fixing, like the styling not being applied even though we added it earlier while building the UI and the video under the Blog Engine Demo heading not displaying. This is a result of Angular’s view encapsulation and HTML sanitization being carried out on HTML inserted with the innerHTML property, as was done in the above code.

To fix this, there are a few tweaks we need to make. In the detail.component.ts file, add the following imports:

import {ViewEncapsulation} from '@angular/core'
import {DomSanitizer, SafeHtml} from '@angular/platform-browser';

Next, to make the styling work, add the following property to the @Component decorator:

@Component({
  // ..
  encapsulation: ViewEncapsulation.None,
})

Next, to display the video under the Blog Engine Demo heading, we will need to bypass Angular’s sanitization, which deems the iframe that inserts the video as unsafe. To do this, first add the following property to the component and modify the constructor to inject DomSanitizer:

html!: SafeHtml

constructor(
 //…
 private sanitizer: DomSanitizer
) { }

Next, modify the subscribe method to the following:

subscribe((res: any) => {
  this.blogPost = res.data
  this.date = new Date(this.blogPost.created).toISOString().split('T')[0]
  this.html = this.sanitizer.bypassSecurityTrustHtml(this.blogPost.body);
})

Above, we bypass the sanitization by supplying the body of the blog content to the bypassSecurityTrustHtml method, then store the result in the html property. 

Now we need to display the value of the html property rather than directly displaying the body of the blog’s content. In the detail.component.html file, modify the div with the innerHTML property to the following:

<div class="blogDetail__body" [innerHTML]="html"></div>

With this, the example post on the details page should now look fine:

Individual blog posts page

Final thoughts

With a platform like ButterCMS, we can get our blog up and running in a few hours using its blog engine rather than taking several days writing lengthy server-side code. In this tutorial, we have learned about ButterCMS and how to use it to build a blog in Angular. I hope you enjoyed this tutorial, and be sure to leave a comment if you have any questions.

Make sure you receive the freshest Angular tutorials and Butter product updates.
    
Taminoturoko Briggs

Taminoturoko Briggs is a software developer and technical writer with sound knowledge of different web technologies. His core languages include JavaScript and Python.

ButterCMS is the #1 rated Headless CMS

G2 crowd review award G2 crowd review award G2 crowd review award G2 crowd review award G2 crowd review award G2 crowd review award G2 crowd review award G2 crowd review award G2 crowd review award G2 crowd review award G2 crowd review award G2 crowd review award

Don’t miss a single post

Get our latest articles, stay updated!