Displaying blog posts on the home page
We would like our users to see the list of available blog posts as soon as they land on our blog website. According to the default route path that we have defined, ArticlesComponent
is the landing page of our blog. Scully provides ScullyRoutesService
, an Angular service that we can use in our components to get information about the routes that it will create according to the blog posts. Let's put this service in action on our landing page:
- Navigate to the
articles.component.ts
TypeScript class file. - Import
ScullyRoute
andScullyRoutesService
from the@scullyio/ng-lib
package:import { ScullyRoute, ScullyRoutesService } from '@scullyio/ng-lib';
- Inject
ScullyRoutesService
in the constructor of theArticlesComponent
class:constructor(private scullyService: ScullyRoutesService) { }
- Create a component property of
ScullyRoute
array type:posts: ScullyRoute[] = [];
- Edit the
ngOnInit
method of the component and add the following code:ngOnInit(): void { this.scullyService.available$.subscribe(posts => { this.posts = posts.filter(post => post.title); }); }
- Open the
articles.component.html
file and add the following HTML code:<div class="list-group mt-3"> <a *ngFor="let post of posts" [routerLink]="post.route" class="list-group-item list-group-item-action"> <div class="d-flex w-100 justify-content-between"> <h5 class="mb-1">{{post.title}}</h5> </div> <p class="mb-1">{{post.description}}</p> </a> </div>
There are many Angular techniques involved in the previous steps, so let's break them down piece by piece.
When we want to use an Angular service in a component, we just need to ask for it from the Angular framework. How? By adding it as a property in the constructor of the component. The component does not need to know anything about how the service is implemented.
The ngOnInit
method is part of the OnInit
interface, which is implemented by our component. It is called by the Angular framework when a component is initialized and provides us with a hook to add custom logic to be executed.
Tip
Angular services that provide initialization logic to a component should be called inside the ngOnInit
method and not in the constructor because it is easier to provide mocks about those services when unit testing the component.
The available$
property of ScullyRoutesService
is called an observable. To retrieve its value, we need to subscribe to it. The returned posts
variable will contain all the available routes that were generated from Scully. Scully is run against all routes of our Angular application. To avoid displaying routes other than those related to blog posts, such as the contact route, we filter out the results from the available$
property.
When we subscribe to an observable, we need to unsubscribe from it when our component no longer exists. Otherwise, we may experience memory leaks in our Angular application. Let's see how we can accomplish this task using another life cycle hook of the component called ngOnDestroy
:
- Declare a private
routeSub
property of theSubscription
type in theArticlesComponent
class.Subscription
can be imported from therxjs
npm package. - Set the returned value of the
available$
observable to therouteSub
property. - Add the
OnDestroy
interface to the list of implemented interfaces of the component.OnDestroy
can be imported from the@angular/core
npm package. It is executed when the component is destroyed, and it is not rendered on the screen anymore. - Implement the
ngOnDestroy
method and call theunsubscribe
method of therouteSub
property in the body of the method.
The resulting TypeScript file of the component should look like the following:
articles.component.ts
import { Component, OnInit, OnDestroy } from '@angular/core'; import { ScullyRoute, ScullyRoutesService } from '@scullyio/ng-lib'; import { Subscription } from 'rxjs'; @Component({ selector: 'app-articles', templateUrl: './articles.component.html', styleUrls: ['./articles.component.scss'] }) export class ArticlesComponent implements OnInit, OnDestroy { posts: ScullyRoute[] = []; private routeSub: Subscription | undefined; constructor(private scullyService: ScullyRoutesService) { } ngOnInit(): void { this.routeSub = this.scullyService.available$.subscribe(posts => { this.posts = posts.filter(post => post.title); }); } ngOnDestroy(): void { this.routeSub?.unsubscribe(); } }
In the template of our component, we use the *ngFor
Angular built-in directive to iterate over the posts
array inside HTML. We can then access each item of the array using the post
template reference variable and use interpolation to display title
and description
.
Finally, we add a routerLink
directive to each anchor element to navigate to the respective blog post when clicked. Notice that routerLink
is surrounded by []
. The []
syntax is called property binding, and we use it when we want to bind the property of an HTML element to a variable. In our case, we bind the routerLink
directive to the route
property of the post
template reference variable.
Now that we have finally completed all the pieces of the puzzle, we can see our blog website in action:
- Run the
build
command of the Angular CLI to build our Angular application:ng build
- Execute the following npm command to build Scully and generate our blog routes:
npm run scully
The preceding command will create a
scully-routes.json
file inside thesrc\assets
folder. It contains the routes of our Angular application and is needed from the Scully runtime.Tip
Running the Scully executable for the first time will prompt you to collect anonymous errors to improve its services.
- Run the following npm command to serve our blog:
npm run scully:serve
The preceding command will start two web servers: one that contains the static prerendered version of our website built using Scully and another that is the Angular live version of our application:
If we open our browser and navigate to http://localhost:1668, we will not see any blog posts. Why is that?
A blog post created with Scully is not returned in the available$
property of ScullyRoutesService
unless we publish it. To publish a blog post, we do the following:
- Navigate to the
mdfiles
folder that Scully created and open the only.md
file that you will find. The name and contents may vary from your file because it is based on the creation date from Scully:--- title: 2020-11-15-posts description: 'blog description' published: false slugs: - ___UNPUBLISHED___khm71wkh_hIzSmrBDHceuWrDrqqTnY8qCwvurkxdT --- # 2020-11-15-posts
Scully has defined a set of properties between the closing and ending
---
lines at the top of the file representing metadata about the blog post. You can also add your own as key-value pairs. - Delete the
slugs
property and set thepublished
property totrue
:--- title: 2020-11-15-posts description: 'blog description' published: true --- # 2020-11-15-posts
Tip
If you do not want to publish a post manually, Scully supports the automatic publishing of blog posts. You can use the reserved
publishDate
property in the metadata to define the date you want to publish the blog post, and Scully will do the rest. - Run the following command to force Scully to regenerate the routes of our application:
npm run scully
We need to execute the previous command every time we make a change in our blog-related files.
- Execute the
npm run scully:serve
command and navigate to preview the generated website.
We can now see one blog post, the default one that was created when we installed Scully. Let's create another one:
- Run the following
generate
command of the Angular CLI:ng generate @scullyio/init:post --name="Angular and Scully"
In the preceding command, we use the
@scullyio/init:post
schematic, passing the name of the post that we want to create as an option. - Set the target folder for the new blog post to
mdfiles
: - Scully will create a Markdown file named
angular-and-scully.md
inside the specified folder. Open that file and update its content to be the same as the following:--- title: 'Angular and Scully' description: 'How to build a blog with Angular and Scully' published: true --- # Angular and Scully Angular is a robust JavaScript framework that we can use to build excellent and performant web applications. Scully is a popular static website generator that empowers the Angular framework with Jamstack characteristics. You can find more about them in the following links: - https://angular.io - https://scully.io - https://www.jamstack.org
- Run
npm run scully
to create a route for the newly created blog post. Scully will also update thescully-routes.json
file with the new route.
If we preview our application now, it should look like the following:
If we click on one of the blog items, we will navigate to the selected blog post. The content that is currently shown on the screen is a prerendered version of the blog post route:
To verify that, navigate to the dist
folder of your Angular project, where you will find two folders:
my-blog
: This contains the Angular live version of our application. When we execute theng build
Angular CLI command, it builds our application and outputs bundle files in this folder.static
: This contains a prerendered version of our Angular application generated from Scully when we run thenpm run scully
command.
If we navigate to the static
folder, we will see that Scully has created one folder for each route of our Angular application. Each folder contains an index.html
file, which represents the component that is activated from that route. The contents of the index.html
file are auto-generated from Scully, and it is the result as if we run our application live and navigate to that component.
Now you can take your Angular application, upload it to the CDN or web server of your choice, and you will have your blog ready in no time! All you will have to do then will be to exercise your writing skills to create excellent blog content.