For this assignment, we will be working with a professionally designed Bootstrap 4 βBlogβ theme from https://bootstrapious.com/. (Important Note: we are using the βfreeβ version of this template, which means we must keep the footer link back to Bootstrapious, ie: βTemplate By Bootstrapiousβ intact in the footer)
The goal of this assignment is to use our knowledge of Angular to begin development and prototyping of a useable and scalable Blogging system User Interface (UI).Β We must use the provided HTML & CSS to help guide our design decisions and develop custom components to work with and display data.Β We will not have any βliveβ data for this blog, but we will have some test data that we can use to ensure that all of the components function properly.
Β
Specification:
Step 1: Creating an Angular App & Downloading the Files
To begin this project we must create a new Angular app using the @angular/cli command line tool: βngβ.Β If you do not have it installed yet, you can pull it from npm using the command:
Β
npm install -g @angular/cli
Β
Once the Angular CLI has been installed, you should be able to kickstart a new Angular application with the following command (Note: The options here enable routing, skip testing and skip the initialization of a βgitβ repository, respectfully)
Β
ng new web422-a4 βrouting -S -g
Β
With our app in place, we must now download the template files.Β You will not have to download anything directly from βbootstrapiousβ β instead, we have provided you with the main files that you will need here:
Β
https://ict.senecacollege.ca/~patrick.crawford/shared/winterβ2020/web422/A4/static.zip
Β
Step 2: Static Content & Main βViewβ Components
Before we start adding the HTML & CSS, we have to make a few updates to our index.html file, ie: Add the following HTML just before the closing <head> tag to the βindex.htmlβ file.Β The paths wonβt make any sense yet, but weβll fix that in the next step:
Β
<!β Bootstrap CSSβ>
<link rel=βstylesheetβ href=β/assets/vendor/bootstrap/css/bootstrap.min.cssβ>
<!β Font Awesome CSSβ>
<link rel=βstylesheetβ href=β/assets/vendor/font-awesome/css/font-awesome.min.cssβ> <!β Custom icon fontβ>
<link rel=βstylesheetβ href=β/assets/css/fontastic.cssβ>
<!β Google fonts β Open Sansβ>
<link rel=βstylesheetβ href=βhttps://fonts.googleapis.com/css?family=Open+Sans:300,400,700β³>
Β
<!β theme stylesheetβ>
<link rel=βstylesheetβ href=β/assets/css/style.default.cssβ id=βtheme-stylesheetβ>
<!β Custom stylesheet β for your changesβ>
<link rel=βstylesheetβ href=β/assets/css/custom.cssβ> <!β Faviconβ>
<link rel=βshortcut iconβ href=β/assets/img/favicon.icoβ>
<!β Tweaks for older IEsβ><!β[if lt IE 9]>
Β Β Β <script src=βhttps://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.jsβ></script>
Β Β Β <script src=βhttps://oss.maxcdn.com/respond/1.4.2/respond.min.jsβ></script><![endif]β>
Β
Next, we must copy the following folders (from our download in Step 1) into the βsrc/assetsβ folder within our newly created Angular application:
Β
- css
- vendor
- img
- fonts
Β
Finally, wipe out all of the content from app.component.html and replace it with <p>App Works!</p> (just to render something for the time being).
Β
Β
Creating new βviewβ Components
Β
Now that all of our styles are in place, we must create the primary components that will be rendered using client side routing.Β These can be thought of as the βviewβ components.Β Within most of these components, we will be creating smaller components.Β However, before we do that, letβs get the boilerplate code for each βviewβ added in its own component:
Β
For the next steps, use the Angular CLI (ng) to create the following components with boilerplate templates from the files provided (step 1)
Β
Β
HeaderComponent (header.component.ts)
Β
- Once you have created this component, copy the contents of the file βhtmlβ into the contents of βheader.component.htmlβ (replacing the automatically-generated paragraph element) and save the file.
- If you wish to test it at this time, you can add the <app-header></app-header> component to the component.html file
Β
FooterComponent (footer.component.ts)
Β
- Once you have created this component, copy the contents of the file βhtmlβ into the contents of βfooter.component.htmlβ (replacing the automatically-generated paragraph element) and save the file.
- If you wish to test it at this time, you can add the <app-footer></app-footer> component to the component.html file
Β
HomeComponent (home.component.ts)
Β
- Once you have created this component, copy the contents of the file βhtmlβ into the contents of βhome.component.htmlβ (replacing the automatically-generated paragraph element) and save the file.
- If you wish to test it at this time, you can add the <app-home></app-home> component to the component.html file (ideally between the header and footer)
Β
BlogComponent (blog.component.ts)
Β
- Once you have created this component, copy the contents of the file βhtmlβ into the contents of βblog.component.htmlβ (replacing the automatically-generated paragraph element) and save the file.
- If you wish to test it at this time, you can add the <app-blog></app-blog> component to the component.html file (ideally between the header and footer β you may want to comment out <apphome></app-home> if it was previously added)
Β
PostComponent (post.component.ts)
Β
- Once you have created this component, copy the contents of the file βhtmlβ into the contents of βpost.component.htmlβ (replacing the automatically-generated paragraph element) and save the file.
- If you wish to test it at this time, you can add the <app-post></app-post> component to the component.html file (ideally between the header and footerβ you may want to comment out <appblog></app-blog> if it was previously added)
Β
PageNotFoundComponent (page-not-found.component.ts)
Β
- You are free to put whatever you like in this template β itβs the β404β view for our application
Step 3: Main Route Configuration
Β
Now that our main components are in place, itβs a good idea to get βroutingβ up and running to make it easier to test and develop each individual view.Β To begin, configure the following routes in yourΒ app-routing.module.ts file by adding the configurations to the Routes array:
Β
- Path: βhomeβ β Shows the HomeComponent
- Path: βblogβ β Shows the BlogComponent
- Path: βpostβ β Shows the PostComponent
- Path: ββ β Redirects to the β/homeβ Route
- No Route Found β Shows the PageNotFoundComponent
Β
Once this is complete, place the βrouter outletβ component (<router-outlet></router-outlet>) between the <app-header></app-header> and <app-footer></app-footer> components in your app.component.html file.
Β
As a final task before we start configuring our individual βviewsβ, letβs make the βheaderβ work with our Angular Routing components:
Β
- Replace all 3 href properties with routerLink β this will ensure that whenever they are clicked, it will use our Angular routing instead of the default browser routing.
- Next, to ensure that our menu items are correctly highlighted, add the following βdirectiveβ to the same 3 anchor elements:
o routerLinkActive=βactiveβ
Β
Β
Step 4: Mock βBlogβ Data
Before we start dividing our views into reusable components (you probably have spotted all of the βwidgetsβ in the side bar β these are perfect candidates), letβs add a few fake blog posts.
Β
Since weβre using typescript, it makes sense for our data to be βtypedβ β ie: we must have a βclassβ to define the βshapeβ of each post.Β To achieve this, add the following 2 files / content
Β
Comment.ts
Β
export class Comment{
_id: stringΒ Β Β Β author: string;Β Β Β Β comment: string;
date: string;
}
Β
BlogPost.ts
Β
import { Comment } from β./Commentβ;
Β
export class BlogPost{
_id: string
title: string;Β Β Β Β postDate: string;Β Β Β Β featuredImage: string;
post: string;
postedBy: string;
comments: Array<Comment>;
category: string;Β Β Β Β tags: Array<string>;Β Β Β Β isPrivate: Boolean;
views: number;
}
Β
Next, we must copy the blogData.json file (from our files included with the download in step 1) and place it within the βappβ folder.Β This will serve as our sample data for the blog.
Β
Unfortunately, if we want to actually βimportβ this json file in any of our components, we must add a couple of
βcompiler optionsβ to our typescript configuration, ie: add the following two properties to the
βcompilerOptionsβ property in the βtsconfig.base.jsonβ file
Β
- βresolveJsonModuleβ: true
- βesModuleInteropβ: true
Β
Now, if we want to ever pull in the data, we can add the following βimportβ lines in our component files:
Β
import blogData from β../blogData.jsonβ; import { BlogPost } from β../BlogPostβ;
Β
In fact, since our βBlogComponentβ will need the blog data, letβs proceed to add the above two βimportβ lines to the top of the blog.component.ts file now.
Β
Inside the BlogComponent class, add the following property declaration:
Β
blogPosts: Array<BlogPost> = blogData;
Β
This will ensure that our data is loaded as βblogPostsβ before we start the next part of the assignment (in the future, this would come from a service which accesses a WEB API endpoint for data).
Β
Β
Step 5: βBlogβ view components
Β
If you open your app to the βblogβ route, you will notice that we have quite a few βcontainersβ in the UI that manage data.Β In the next part of this assignment, we will build out components for these containers PostCardComponent
Β
Β
Β
This component will be responsible for rendering the above chunk of UI (Hint: all the code inside <div class=βpost col-xl-6β³>β¦</div>, so use that as your template starting point).
Β
To get started, create the component using the ng g c (ng generate component) command, then proceed to implement the component according to the following specifications:
Β
- It must receive a single property post. The type of this property will be BlogPost (therefore, { BlogPost } must be imported from β../BlogPostβ and the @Input() decorator must also be used.Β Do not forget to import βInputβ from @angular/core
- It must be rendered using the class property: βclass=βpost col-xl-6β, ie: β<app-post-card class=βpost col-xl-6β³>β
- Its βpost-thumbnailβ element must show the postβs βfeaturedImageβ
- Its βdate meta-lastβ element must show the postβs βpostDateβ
- Its βcateogoryβ element must show the postβs βcategoryβ
- Its β<h3 class=βh4β> element must show the postβs βtitleβ
- Its β <p class=βtext-mutedβ> element must be changed to a <div> element instead (ie: β<div class=βtextmutedβ>β¦</div>β. Also its βinnerHtmlβ property must be set to the postβs βpostβ
- Its βavatarβ image must be changed to β/assets/img/user.svgβ
- Its βtitleβ element must show the postβs βpostedByβ
- Its βdateβ element (next to the clock) must show the postβs βpostDateβ
- Its βmeta-lastβ element must show the number of comments, ie comments.length
Β
Once this is done, use the new βapp-post-cardβ in place of the existing elements in the βblog.component.htmlβ template.Β You should be able to show one app-post-card per βpostβ in the βblogPostsβ array using *ngFor.
Β
Using our sample data, your βblogβ page should now look like the below screenshot:
Β
Β
Β
SearchWidgetComponent
Β
Β
Β
This component simply renders the βwidget-searchβ element, so use that as your template starting point.
Β
To get started, create the component using the ng g c (ng generate component) command.Β We will use this to render the template only β we not be implementing any logic at this time.
Β
Β
LatestPostsComponent
Β
Β
Β
This component will be responsible for rendering the above chunk of UI (Hint: this is the <div class=βwidget latest-postsβ> element, so use that as your template starting point).
Β
To get started, create the component using the ng g c (ng generate component) command, then proceed to implement the component according to the following specifications:
Β
- It must receive a single property posts. The type of this property will be Array<BlogPost> (therefore,
{ BlogPost } must be imported from β../BlogPostβ and the @Input() decorator must also be used.Β Do not forget to import βInputβ from @angular/core
- Inside the template, you will notice three βitemβ elements, each nested inside an β<a href=β#β>β element. The idea is that we must render one βitemβ link for every βpostβ in the βpostsβ array according to the following:
- Its βimageβ element must show the postβs βfeaturedImageβ
- Its βtitleβ element must show the postβs βtitleβ o Its βviewsβ element must show the postβs βviewsβ
- Its βcommentsβ element must show the number of comments, ie comments.length
Note: When you include the component, you can limit the number of βblogPostsβ that you provide to the component using the code:
Β
<app-latest-posts [posts]=βblogPosts.slice(0,3)β></app-latest-posts>
Β
When complete, your component should look like:
Β
Β
Β
Β
Β
Β
Β
Β
Β
Β
Β
Β
Β
Β
CategoriesComponent
Β
Β
Β
This component will be responsible for rendering the above chunk of UI (Hint: this is the <div class=βwidget categoriesβ> element, so use that as your template starting point).
Β
To get started, create the component using the ng g c (ng generate component) command, then proceed to implement the component according to the following specifications:
Β
- In the future, this component would have to worry about fetching the category data on its own. In the meantime, hardcode the following array as the componentβs single property:
Β
categories: Array<any> = [Β Β {cat: βCrimeβ, num: 2},
{cat: βComedyβ, num: 1},
{cat: βMusicalβ, num: 1},
{cat: βAdventureβ, num: 2},
{cat: βDramaβ, num: 2},
{cat: βActionβ, num: 2},
{cat: βDocumentaryβ, num: 1},
{cat: βThrillerβ, num: 1}
];
Β
- In the template, you will notice that <div class=βitem d-flex justify-content-betweenβ>β¦</div> is repeated once per category. We will use our βcategoriesβ array to accomplish the same output, ie: looping through the array and outputting one βitemβ per element.
Β
Β
Β
Β
Β
Β
Β
When complete, your component should look like:
Β
Β
Β
Β
TagsComponent
Β
Β
Β
The TagsComponent functions very much like the CategoriesComponent in that it doesnβt accept any props and (for now) we will hardcode the existing tags into a βtagsβ array, ie:
Β
tags: Array<string> =[
β#funnyβ,
β#dramaticβ,
β#rentalβ,
β#seeagainβ,
β#spookyβ,
β#worththecostβ,
β#lovedItβ,
β#scaryβ,
β#sillyβ,
β#good4kidzβ
];
In the template, you will notice that <li class=βlist-inline-itemβ>β¦</li> is repeated once per category.Β We will use our βtagβ array to accomplish the same output, ie: looping through the array and outputting one βlistinline-itemβ per element.
Β
When complete, your component should look like:
Β
Β
Β
Step 6: βPostβ view components
Now that we have completed all of the βBlogβ view components, we can move on to the βPostβ view components.Β The good news here is that that we can reuse most of the components from the βBlogβ view
Β
To get started, we will follow along with the same procedure as the βBlogβ component, ie adding in the βblogPostsβ array:
Β
First, add the following βimportβ lines in our post.component.ts file:
Β
import blogData from β../blogData.jsonβ; import { BlogPost } from β../BlogPostβ;
Β
Next, inside the PostComponent class, add the following property declaration:
Β
blogPosts: Array<BlogPost> = blogData;
Β
This will ensure that our data is loaded as βblogPostsβ (once again β in the future, this would come from a service which accesses a WEB API endpoint for data).
Β
Β
Reusing the <aside> from blog.component.html
Β
If you take a look at your completed blog.component.html, you should notice that your <aside> element is now much smaller than it used to be, ie:
Β
<aside class=βcol-lg-4β³>
<!β Widget [Search Bar Widget]β>
<app-search-widget></app-search-widget>
<!β Widget [Latest Posts Widget] β>
<app-latest-posts [posts]=βblogPosts.slice(0,3)β></app-latest-posts>
<!β Widget [Categories Widget]β>
<app-categories></app-categories>
<!β Widget [Tags Cloud Widget]β>
<app-tags></app-tags>
</aside>
Β
You will also notice that we have the exact same <aside> element in our post.component.html file (starting at line 100).Β For this next step, simply copy your full <aside> element from your blog.component.html and paste it in place of the existing <aside> within your post.component.html file!
Β
Β
PostDataComponent (The last one!)
Β
This Final Component is basically the entire βpost singleβ div from the sample html, ie: β<div class=βpostsingleβ>β¦</div>β (everything from line 6 to line 94 inclusive).Β So, to get started wiring up this component, simply cut all of that HTML including the <div class=βpost-singleβ>β¦</div> container element, and paste it into your post-data.component.html.Β Then, in its place add back:
Β
<app-post-data [post]=βblogPosts[0]β></app-post-data>
Β
Note: We will simply be hard-coding the first blog post for now.
Β
Next, once your html is in the right place (post-data.component.html) we can start working with the template according to the following specifications:
Β
- It must receive a single property post. The type of this property will be BlogPost (therefore, { BlogPost } must be imported from β../BlogPostβ and the @Input() decorator must also be used.Β Do not forget to import βInputβ from @angular/core
- The βpost-thumbnailβ must show the postβs βfeaturedImageβ
- The βcategoryβ must show the postβs βcategoryβ
- The <h1>β¦</h1> element must show the postβs βtitleβ
- The βavatarβ must be changed to show β/assets/img/user.svgβ
- The βtitleβ (directly beneath avatar) must show the postβs βpostedByβ
- The βdateβ element (next to the clock) must show the postβs βpostDateβ
- The βviewsβ element (next to the eye) must show the postβs βviewsβ
- The βcommentsβ element must show the number of comments, ie comments.length
- The βpost-bodyβ innerHTML property must be set to the postβs βpostβ
- The βpost-tagsβ must show one β<a href=β#β class=βtagβ>β¦</a> element for every tag in the postβs βtagsβ array such that the content of the <a> shows the value of a tag used in the post
- The βno-of-commentsβ must show the number of comments, ie comments.length
The last piece of this component implementation is the βcommentsβ list.Β To complete this, we must iterate over the βcommentsβ array and display a <div class=βcommentβ>β¦</div>β for every comment in the array.
This will involve updating the following βcommentβ related elements:
Β
- The βtitleβ should show the current commentβs βauthorβ
- The βdataβ should show the current commentβs βdateβ
- The βcomment bodyβ innerHTML property must be set to the current commentβs βcommentβ Step 7:Updating Links and Testing
Β
You will notice that there are a few things that are missing from our solution. Most notably the out of date, static blog posts on the Home page and in the footer.Β If you require additional practice with components please feel free to use the knowledge gained from updating the βBlogβ and βPostβ views to refactor these views as well.
Β
Also, you will notice that thereβs a lot of βhrefβ links still in the solution.Β Unless the app requires the user to navigate away (ie: heading to twitter, etc.) then βhrefβ should be replaced with βrouterLinkβ, ie:
Β
- href=β#β should be replaced with routerLink
- href=βpost.htmlβ should be replaced with routerLink=β/postβ
Β
Once this is complete, you should be ready to submit.Β However have a look at the full-page screenshots of the app and compare your results with what you see.Β Everyone is using the data, so it should look identical (unless you added some additional styling).
Β
Β





