Create Blogging Site With Angular And Scully

In the previous article: Getting Started With Scully - Angular Static Site Generator, We have seen what is Scully, How it works & How to use it to prerender routes.

 
In this article, we will see How Scully can help us to create a blogging site in angular?  

Create Blogging Site With Angular And Scully

Scully is the best option to create a blogging site in Angular, because
  • It provides very good SEO support. 
  • Superfast performance as pages are prerendered
  • Markdown support - Scully uses markdown files for blog content
  • Various plugins are available for syntax highlighting, google analytics integration, and many more.
  • Easy to create a new blog post : Once you have created the blog dashboard and blog page design setup in an angular application, you just need to add markdown files in your blog folder to create a new blog post
So let's get started 
  
We will continue working on previous article example. If you want to start along then download/clone this GitHub repository.
  • We will add the blog support using Scully.
  • Create a Blog dashboard where it will show all the blogs.

Add Blog Support  

 
Scully provides a schematic to add blog support in your existing angular application.  execute the following command on command prompt or terminal. 

(If you have not done Scully setup in your angular app, refer Getting Started With Scully.)
  1. ng generate @scullyio/init:blog  
 This will,
  • Update scully.[projectName].config.ts file
    1. // scully.portfolio.config.ts  
    2.   
    3. import { ScullyConfig } from '@scullyio/scully';  
    4. export const config: ScullyConfig = {  
    5.   projectRoot: "./src",  
    6.   projectName: "portfolio",  
    7.   outDir: './dist/static',  
    8.   routes: {  
    9.     '/blog/:slug': {  
    10.       type: 'contentFolder',  
    11.       slug: {  
    12.         folder: "./blog"  
    13.       }  
    14.     },  
    15.     // Other Parameterized Route Config   
    16.     }  
    17.   }  
    18. };  
  • Create a blog component where markdown content will be rendered.
    1. //blog.component.ts  
    2.   
    3. import {Component, OnInit, ViewEncapsulation} from '@angular/core';  
    4. import {ActivatedRoute, Router, ROUTES} from '@angular/router';  
    5.   
    6. declare var ng: any;  
    7.   
    8. @Component({  
    9.   selector: 'app-blog',  
    10.   templateUrl: './blog.component.html',  
    11.   styleUrls: ['./blog.component.scss'],  
    12.   preserveWhitespaces: true,  
    13.   encapsulation: ViewEncapsulation.Emulated  
    14.   
    15. })  
    16. export class BlogComponent implements OnInit {  
    17.   ngOnInit() {}  
    18.   
    19.   constructor(private router: Router, private route: ActivatedRoute) {  
    20.   }  
    21. }  
    1. <!-- blog.component.html -->  
    2.   
    3. <h3>ScullyIo content</h3>  
    4. <hr>  
    5.   
    6. <!-- This is where Scully will inject the static HTML -->  
    7. <scully-content></scully-content>  
    8. <hr>  
    9. <h4>End of content</h4>  
    <scully-content> is a predefined component. Our blog content will be projected here. We can update the header and footer part as per our requirements.

  • Create blog.module.ts and blog-routing.module.ts for blog component route
    1. // blog-routing.module.ts  
    2.   
    3. import {NgModule} from '@angular/core';  
    4. import {Routes, RouterModule} from '@angular/router';  
    5.   
    6. import {BlogComponent} from './blog.component';  
    7.   
    8. const routes: Routes = [  
    9.   {  
    10.     path: ':slug',  
    11.     component: BlogComponent,  
    12.   },  
    13.   {  
    14.     path: '**',  
    15.     component: BlogComponent,  
    16.   }  
    17. ];  
    18.   
    19. @NgModule({  
    20.   imports: [RouterModule.forChild(routes)],  
    21.   exports: [RouterModule],  
    22. })  
    23. export class BlogRoutingModule {}  
    1. // blog.module.ts  
    2.   
    3. import {CommonModule} from '@angular/common';  
    4. import {NgModule} from '@angular/core';  
    5. import {ScullyLibModule} from '@scullyio/ng-lib';  
    6. import {BlogRoutingModule} from './blog-routing.module';  
    7. import {BlogComponent} from './blog.component';  
    8.   
    9. @NgModule({  
    10.   declarations: [BlogComponent],  
    11.   imports: [CommonModule, BlogRoutingModule, ScullyLibModule],  
    12. })  
    13. export class BlogModule {}  
  • Update the app-routing.module.ts to add the blog module route.
  • Add one sample blog markdown file with [current date] blog.md name in /blog folder
    1. ---  
    2. title: 2021-04-03-blog  
    3. description: blog description  
    4. published: false  
    5. ---  
    6.   
    7. # 2021-04-03-blog  
    title, description, and published are meta details for this blog. These details can be used while we show the blog details on the blog dashboard. This can be accessible with the ScullyRoutesService. We can also add other meta properties as per our requirement, for example, authorName, authorTwitterId etc. 

Test The Default Blog


We have added new components and modules. So we have to take a new angular build
  1. ng build --prod  
Once it is done take Scully build 
  1. npm run scully -- --scanRoutes 
Scully maintains the cache for routes, if you want to discard the cache and recalculate the route, execute the command with --scanRoutes .

When published flag is false in a markdown file, and you take a Scully build it will add slugs property with ___UNPUBLISHED___[random-string]in markdown file and generate a blog HTML page in dist/static/blog folder with ___UNPUBLISHED___[random-string].
 
Unpublished blog path

When you want to publish this blog, set the published flag to true. and take a Scully build again with npm run scully. 
 
Now serve the prerendered pages with npm run scully:serve and open the blog path in the browser.
 
Unpublished blog 
 

Change The Blog Layout


We can customize the blog layout by doing related changes in BlogComponent. I have removed the default header and footer provided by Scully.  I am using Bootstrap to apply some styling. You can do more customization as per your requirement. 
  1. // blog.component.html    
  2.     
  3. <div class="row">    
  4.     <div class="col-md-12">    
  5.         <div class="card shadow-sm">    
  6.             <div class="card-body">    
  7.                 <!-- This is where Scully will inject the static HTML -->    
  8.                 <scully-content></scully-content>    
  9.             </div>    
  10.         </div>    
  11.     </div>    
  12. </div>      

Create Blog Dashboard

 
In BlogModule we will create BlogsComponent, In this component, we will show the list of blogs. We will update the route in blog-routing.module.ts to open BlogsComponent when the user open the /blog/ route.
  1. ng generate component blog/blogs  
  1. //blogs.component.ts    
  2.     
  3. import { Component, OnInit } from '@angular/core';    
  4. import { ScullyRoute, ScullyRoutesService } from '@scullyio/ng-lib';    
  5. import { map } from 'rxjs/operators';    
  6.     
  7. @Component({    
  8.   selector: 'app-blogs',    
  9.   templateUrl: './blogs.component.html',    
  10.   styleUrls: ['./blogs.component.scss']    
  11. })    
  12. export class BlogsComponent implements OnInit {    
  13.     
  14.   blogs$ = this.scullyService.allRoutes$.pipe(    
  15.               map(routes => routes.filter(r=> r.route.startsWith('/blog/')))    
  16.            );    
  17.     
  18.   constructor(private scullyService: ScullyRoutesService) {    
  19.     
  20.   }    
  21.   ngOnInit(): void {    
  22.   }    
  23.     
  24. }     
We can access all routes details with ScullyRoutesService. We will filter the blog routes and render them on template.
  1. <!-- blogs.component.html -->    
  2.     
  3. <h1 class="text-center">Blogs</h1>    
  4. <hr>    
  5. <div class="row">    
  6.     <div class="col-md-12 p-2" *ngFor="let blog of blogs$|async">    
  7.         <div class="card shadow-sm">    
  8.           <div class="card-body">    
  9.             <h4 class="card-title">{{blog.title}}</h4>    
  10.             <p class="card-title">{{blog.description}}</p>    
  11.             <div>    
  12.                 <span class="badge" [ngClass]="{'badge-info': blog.published, 'badge-dark': !blog.published}" >{{blog.published ? 'PUBLISHED' : 'DRAFT'}}</span>    
  13.             </div>    
  14.             <a class="btn btn-primary" [routerLink]="[blog.route]" >Read More</a>    
  15.         </div>    
  16.         </div>    
  17.     </div>    
  18. </div>     
We are done with our blog dashboard implementation. Now let's create few sample blogs and Test our blogging site.
 

Create A New Blog Post


We can create a new blog by creating a new markdown file in /blog folder. In this markdown file, we also have to mention the meta properties which is specified in about sample. We can do this manually, but instead of this Scully provides a schematic to create a new post, which will generate the file with meta properties.
  1. ng generate @scullyio/init:post  
Create a new blog post
 
It will generate the markdown file with the following content,
  1. ---  
  2. title: Getting Started with Scully  
  3. description: blog description  
  4. published: false  
  5. ---  
  6.   
  7. # Getting Started with Scully  
 Here we will add the blog content and to publish this blog we will change published flag to true.
 

Final Output


I have added a few other blogs and changed all blog post published flags to true. Remove unpublished slug from markdown.

Take a new angular build and Scully build and start the scully static server.
  1. ng build --prod  
  2. npm run scully -- --scanRoutes 
  3. npm run scully:serve  
Blogging Site With Scully and Angular
 
Great !!! We have successfully created a blogging site with Angular and Scully.
 
Get the Source Code at this GitHub Repository
 

Summary 


In this article, We have seen how to create a blogging site with angular and Scully. 
 
I hope you like this article, please provide your valuable feedback and suggestions🙂.