Chapter 3: Beginning Frontend Development with Angular CLI
Activity 6: Designing the Frontend and Components for the Blogging Application
Create a new Angular project named blog by pointing your CLI to the Blogging Application directory, and create an Angular CLI application with the name blog using the following code:
ng new blog
Import the bootstrap theme and its resources from http://bit.ly/2DI7UsR by opening the link, and download the bootstrap theme and resources into the blog/src/assets folder.
Update index.html with the necessary resource links using the following code:
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>Blog</title> <base href="/"> <meta name="viewport" content="width=device-width, initial-scale=1"> //[…] <script type="text/javascript" src="assets/js/main.js"></script> </body> </html>
File name: index.html
Live link: http://bit.ly/2HDFQuT
File name: header.component.html
Live link: http://bit.ly/2Rt5h1Q
File name: blog-home.component.html
Live link: http://bit.ly/2FXWZh9
File name: footer.component.html
Live link: http://bit.ly/2UnBZ6s
File Name: view-post.component.html
Live link: http://bit.ly/2FWIJoP
Create the header components and update the HTML template with the theme's header using the following code:
ng generate component header <nav class="navbar navbar-expand-lg navbar-dark absolute_header"> <div class="container"> <a class="navbar-brand" href="#"> <img class="etcodes-normal-logo" src="assets/images/logo-light.png" width="84" height="22" alt="Logo"> <img class="etcodes-mobile-logo" src="assets/images/logo.png" width="84" height="22" alt="Logo"> </a> <button class="navbar-toggler hamburger-menu-btn" type="button" data-toggle="collapse" data-target="#navbarNavDropdown" aria-controls="navbarNavDropdown" //[…] </ul> </li> </ul> </li> </ul> </div> </div> </nav>
Create the title-header component using the following code:
<div class="bg-img-71 py-100px py-md-200px mb-80px"> <div class="container"> <div class="row"> <div class="col-lg-6 offset-lg-3 text-center all-text-content-white"> <h1>MEAN Stack Courseware Blog Application Project</h1> <p>Aenean lacinia bibendum sed consectetur. Etiam porta sem malesuada magna mollis euismod. Fusce dapibus pertinax vix, varius cursus turpis dign issim id aliquam habemus tractatos.</p> </div> </div> </div> </div>
Create the blog-home components and update the HTML template with the theme's content:
ng generate component blog-home <!-- Blogs --> <title-header></title-header> <div class="container"> <div class="row"> <div class="col-lg-12"> <div class="row blog_posts stander_blog"> <div class="col-md-6 col-lg-4"> <article> <div class="post_img card-blog-img"> <img src="assets/images/home-27/2.jpg" alt="Card image cap"> <a href="blog-standard-two-col-right-sidebar.html"> <span class="card-blog-meta"> //[…] </div> </div> </div> </div>
Create the footer components and update the HTML template with the theme's footer:
ng generate component footer <footer class="web-footer footer bg-color-blackflame all-text-content-white"> <div class="footer-widgets pt-85px pb-55px"> <div class="container"> <div class="row large-gutters"> <div class="col-lg-5 mb-30px"> <div class="footer-widget"> <h3>Paulappz</h3> <p class="pt-20px pt-lg-155px">© 2018 Paulappz Themes</p> //[…] </div> </div> </div> </div> </footer>
Create the view-post components and update the HTML template:
ng generate component view-post <div class="page-container scene-main scene-main--fade_In"> <!-- view post --> <div class="container"> <div class="row"> <div class="col-md-12"> <div class="post_img"> <img src="assets/images/b3.jpg" alt="Card image cap"> //[…] </div> </div> </div> </div> </div>
Update the root component template with the following code:
<div id="main-content" class="bg-color-gray"> <app-header></app-header> <div class="page-container scene-main scene-main--fade_In"> <app-blog-home></app-blog-home> <app-footer></app-footer> </div> </div>
Run ng serve -o to start the application. You will obtain the following output in the browser:
Update the root component template with the following code:
<div id="main-content" class="bg-color-gray"> <app-header></app-header> <div class="page-container scene-main scene-main--fade_In"> <view-post></view-post> <app-footer></app-footer> </div> </div>
You will obtain the following output in the browser:
Activity 7: Writing Services and Making HTTP Request Calls to an API
File name: blog-home.component.html
Live link: http://bit.ly/2TjT1FQ
File name: view-post.component.ts
Live link: http://bit.ly/2VqoaoB
Create a service file for blog post articles by running the following command:
ng generate service service/article
Update app.module.ts file by importing the provider for ArticleService using the following code:
import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { AppComponent } from './app.component'; import { BlogHomeComponent } from './blog-home/blog-home.component'; import { HeaderComponent } from './header/header.component'; import { FooterComponent } from './footer/footer.component'; import { ArticleService } from './service/article.service'; import { HttpClientModule } from '@angular/common/http'; import { ViewPostComponent } from './view-post/view-post.component'; import { TitleHeaderComponent } from './title-header/title-header.component'; @NgModule({ declarations: [ AppComponent, BlogHomeComponent, HeaderComponent, FooterComponent, ViewPostComponent, TitleHeaderComponent ], imports: [ BrowserModule, HttpClientModule ], providers: [ArticleService], bootstrap: [AppComponent] }) export class AppModule { }
Write the interface class in a newly created posts.ts file using the following code:
export interface Post { photo: string; title: string; body: string; tag: string; }
Import the Injectable, HttpClient, Post, and Observable modules using the following code:
import { Injectable } from '@angular/core'; import { HttpClient, HttpHeaders } from '@angular/common/http'; import { Post } from '../posts' import { Observable } from 'rxjs';
Declare Url variables and assign string values in the ArticleService class as shown:
articlesUrl = 'http://localhost:3000/articles'; articleUrl = 'http://localhost:3000/article/'; article: any; httpOptions:any;
Inject HttpClient and set HttpHeaders using the following code:
constructor(private http: HttpClient) { this.httpOptions = new HttpHeaders({ 'Access-Control-Allow-Origin':'*', 'Access-Control-Allow-Methods':'PUT, POST, GET, DELETE, OPTIONS', }); }
Write the CRUD service functions:
getArticles(): Observable<Post> { this.article = this.http.get<Post>(this.articlesUrl); return this.article; } getArticle(id: number) { return this.http.get(this.articleUrl + id); } /** POST: add a new article to the database */ PostArticle(article: Post): Observable<Post> { return this.http.post<Post>(this.articlesUrl, { 'title': article.title, 'body': article.body, 'tag': article.tag, 'photo': article.photo }, { headers: this.httpOptions }) } deleteArticle(id: number): Observable<{}> { return this.http.delete(this.articleUrl + id, { headers: this.httpOptions }) } updateArticle(id: number, article: Post): Observable<Post> { return this.http.put<Post>(this.articleUrl + id, { 'title': article.title, 'body': article.body, 'tag': article.tag, 'photo': article.photo },{ headers: this.httpOptions }) } }
Update the blog-home components class file with the following code:
import { Component, OnInit } from '@angular/core'; // Import service import { ArticleService } from '../service/article.service'; @Component({ selector: 'app-blog-home', templateUrl: './blog-home.component.html', styleUrls: ['./blog-home.component.css'] }) export class BlogHomeComponent implements OnInit { articles:any=[]; //Inject service constructor(private articleService:ArticleService) { } ngOnInit() { this.articleService.getArticles() .subscribe( res => { for(let key in res){ this.articles.push(res[key]); } }, err => { console.log("Error occured"); } ); } }
Update the blog-home components template file with the following code:
<app-title-header></app-title-header> <div class="container"> <div class="row"> <div class="col-lg-12"> <div class="row blog_posts stander_blog"> <div class="col-md-6 col-lg-4" *ngFor="let article of //[…] </div> </div> </div> </div>
Update the root component template with the following code:
<div id="main-content" class="bg-color-gray"> <app-header></app-header> <div class="page-container scene-main scene-main--fade_In"> <app-blog-home></app-blog-home> <app-footer></app-footer> </div> </div>
Run ng serve -o on the command line to view the following output:
Update the view-post components class file with the following code:
import { Component, OnInit } from '@angular/core'; import { ArticleService } from '../service/article.service'; @Component({ selector: 'view-post', templateUrl: './view-post.component.html', styleUrls: ['./view-post.component.css'] }) export class ViewPostComponent implements OnInit { id: any; article: any; constructor( private articleService: ArticleService) { } ngOnInit() { this.id = '5b9426b70e473447f400eeb9' // id of first post from API this.articleService.getArticle(this.id) .subscribe( res => { console.log(res) this.article = res; }, err => { console.log("Error occured"); } ); } }
Update the view-post components template file with the following code:
<div class="page-container scene-main scene-main--fade_In"> <!-- Blog post --> <div class="container"> <div class="row"> <div class="col-md-12"> <div class="post_img"> <img src="https://{{article.photo}}" alt="Card image cap"> //[…] </div> </div> </div> </div> </div>
Update the root component template with the following code:
<div id="main-content" class="bg-color-gray"> <app-header></app-header> <div class="page-container scene-main scene-main--fade_In"> <view-post></view-post> <app-footer></app-footer> </div> </div>
Run ng serve -o to start the application. You should obtain the following output:
Activity 8: Creating a Form Application Using the Reactive/Model-Driven Method
File name: packt-student-reactive-form.component.html
Live link: http://bit.ly/2WutecX
Create an Angular CLI application named Reactive form:
ng new reactive-form
Create a component and name it PacktStudentReactiveForm:
ng generate component reactive-form/ PacktStudentReactiveForm
Register the reactive forms module in app.module.ts and add it to import properties to activate the reactive form using the following code:
import { ReactiveFormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms'; imports: [ BrowserModule, FormsModule, ReactiveFormsModule ],
Import the FormControl and FormGroup modules into the reactive form component (packt-student-reactive-form.component.ts):
import { FormGroup, FormControl, Validators, FormBuilder } from '@angular/forms'; import { Component, OnInit } from '@angular/core';
Declare and initialize the array in the reactive components class, as follows:
courseTitles = ['MEAN Stack', 'MEVN Stack', 'MERN Stack'];
Create FormGroup and FormControl variable instances:
myform : FormGroup; name : FormControl; courseTitle : FormControl; duration : FormControl; constructor() { }
Create functions to initialize and validate using the following code:
ngOnInit() { this.createFormControls(); this.createForm(); } createFormControls() { this.name = new FormControl('Paul Adams', Validators.required), this.courseTitle = new FormControl(this.courseTitles[0], Validators.required), this.duration = new FormControl('6 days') } createForm() { this.myform = new FormGroup({ name: this.name, courseTitle : this.courseTitle, duration: this.duration }); } submitted = false; onSubmit() { this.submitted = true; console.log(this.name) } newStudent() { this.myform.reset(); }
Register the control and form group in the html form template of the reactive component (packt-student-reactive-form.component.html) using the following code:
<div class="container"> <div *ngIf="!submitted"> <h1>Packt Course Form</h1> //[…] </select> <div *ngIf="courseTitle.invalid" class="alert alert-danger"> courseTitle is required </div> </div> <div class="form-group"> <label for="duration">Course Duration</label> <input type="text" class="form-control" id="duration" formControlName="duration"> </div> <button type="submit" class="btn btn-success" [disabled]="">Submit</button> <button type="button" class="btn btn-default" (click)="newStudent();">New Student</button> </form> </div>
Add a submitted view to the template:
<div *ngIf="submitted"> <h2>You submitted the following:</h2> <div class="row"> <div class="col-xs-3">Name</div> <div class="col-xs-9 pull-left">{{name.value}}</div> </div> <div class="row"> <div class="col-xs-3">Course Duration</div> <div class="col-xs-9 pull-left">{{duration.value}}</div> </div> <div class="row"> <div class="col-xs-3">Packt Course Title</div> <div class="col-xs-9 pull-left">{{courseTitle.value}}</div> </div> <br> <button class="btn btn-primary" (click)="submitted=false">Edit</button> </div> </div>
Update the components style sheet (packt-student-reactive-form.component.css) and the main application style sheet (style.css) using the following code:
// packt-student-reactive-form.component.css .ng-valid[required], .ng-valid.required, .has-success { /* border-left: 5px solid #42A948; */ /* green */ } .ng-invalid:not(form) { /* border: 1px solid #a94442; */ /* red */ } // style.css /* You can add global styles to this file, and also import other style files */ @import url('https://unpkg.com/[email protected]/dist/css/bootstrap.min.css');
Update the root template file (app.componets.html file) by hosting the form component src/app/app.component.html using the following code:
src/app/app.component.html < app-packt-student-reactive-form ></ app-packt-student-reactive-form >
Launch the application by running the following snippet in the command-line terminal and go to the address localhost:4200:
ng serve --open
You will obtain the following output:
Activity 9: Creating and Validating Different Forms Using the Template and Reactive-Driven Method
File name: login.component.html
Live link: http://bit.ly/2B8XFfK
Update the app component template with the following snippet and run ng serve to view the User Login form output:
File name: register.component.html
Live link: http://bit.ly/2Wul1p2
File name: create.component.html
Live link: http://bit.ly/2HGg3SD
Create the User Login form component using the following code:
ng generate component login
Update the User Login form component class and update the HTML template to be reactive/model-driven using the following code:
import { Component, OnInit } from '@angular/core'; import { FormGroup, FormControl, Validators, FormBuilder } from '@angular/forms'; @Component({ selector: 'login', templateUrl: './login.component.html', styleUrls: ['./login.component.css'] }) export class LoginComponent { loginForm: FormGroup; displayMessage: string; submitted = false; constructor(private formBuilder: FormBuilder) { /* Declare Reactive Form Group here */ this.loginForm = this.formBuilder.group({ email: ['', [ Validators.required, Validators.pattern("[^ @]*@[^ @]*") ]], password: ['', [ Validators.minLength(8), Validators.required ]], }); } submitForm() { this.submitted = true; /* Change the display message on button click / submit form */ if (this.loginForm.valid) { this.loginUser(); } } loginUser() { console.log('Logged In sucessfully') } }
The template is updated with the following:
<title-header></title-header> <div class="" style="padding-bottom: 3rem!important;"> <div class="row"> <div class="col-md-6 mx-auto"> <!-- form card login --> <div class="card rounded-0"> //[…] <!-- /form card login --> </div> </div> </div>
<div id="main-content" class="bg-color-gray"> <app-header></app-header> <div class="page-container scene-main scene-main--fade_In"> <app-login></app-login> <app-footer></app-footer> </div> </div>
You will obtain the following output once you type http://localhost:4200/login in the browser:
Create the User Registration form component:
ng generate component register
Create an exportable user class file in the app directory using the following code:
export class Users { constructor( public fullName: string, public email: string, public password: string ) { } }
Update the User Registration form component class and update the HTML template to be template-driven.
The class is updated as follows:
import { Component, OnInit } from '@angular/core'; import { Users } from '../users'; @Component({ selector: 'app-register', templateUrl: './register.component.html', styleUrls: ['./register.component.css'] }) export class RegisterComponent implements OnInit { model = new Users('', '', ''); constructor(/*private auth: AuthService, private router: Router*/) { } ngOnInit() { } onSubmit() { this.registerUser(); } registerUser() { console.log('Registration Successful') }
The template is updated as follows:
<title-header></title-header> <div> <div class="row"> <div class="col-md-6 mx-auto"> <!-- form card login --> <div class="card rounded-0"> <h3 class="mb-0" style="text-align:center">Register Admin User</h3> <div class="card-header"> </div> <div class="card-body"> <form (ngSubmit)="userForm.form.valid && onSubmit()" //[…] <!-- /form card login --> </div> </div> </div>
Update the app component template with the following snippet and run ng serve to view the User Registration form output:
<div id="main-content" class="bg-color-gray"> <app-header></app-header> <div class="page-container scene-main scene-main--fade_In"> <app-register></app-register> <app-footer></app-footer> </div> </div>
You will obtain the following output once you run http://localhost:4200/register in the browser:
Create the Post Create/Edit form component and an exportable Posts class:
ng generate component create export class Posts { constructor( public title: string, public body: string, public tag: string, public photo: string, ) { } }
Update the Post Create/Edit form component class and update the HTML template to be template-driven.
The class is updated as follows:
import { Component, OnInit } from '@angular/core'; import { Posts } from '../post'; @Component({ selector: 'app-create', templateUrl: './create.component.html', styleUrls: ['./create.component.css'] }) export class CreateComponent implements OnInit { tags = ['POLITICS', 'ECONOMY', 'EDUCATION','STORY','TECH']; model = new Posts('','','',''); submitted = false; constructor() { } onSubmit() { this.submitted = true; console.log(this.model) } ngOnInit() { } }
The template is updated as follows:
<title-header></title-header> <div class="py-5"> <div class="row"> <div class="col-md-6 mx-auto"> <!-- form card login --> <div class="card rounded-0"> <h3 style="text-align:center" class="mb-0">Create Post</h3> <div class="card-header"> </div> <div class="card-body"> <form (ngSubmit)="postForm.form.valid && onSubmit()" //[…] <button type="submit" class="btn btn-success">Submit</button> </form> </div> <!--/card-block--> </div> <!-- /form card login --> </div> </div> </div>
Update the app component template with the following snippet and run ng serve to view the Post Create/Edit form output:
<div id="main-content" class="bg-color-gray"> <app-header></app-header> <div class="page-container scene-main scene-main--fade_In"> <app-create></app-create> <app-footer></app-footer> </div> </div>
You will obtain the following output once you type http://localhost:4200/create in the browser window:
Activity 10: Implementing a Router for the Blogging Application
File name: blog-home.component.html
Live link: http://bit.ly/2CSjEaQ
Import RouterModule into the app module file (app.module.ts) and update the import property:
import { RouterModule, Routes } from '@angular/router'; imports: [ BrowserModule, HttpClientModule, RouterModule.forRoot(appRoutes), FormsModule, ReactiveFormsModule, BrowserAnimationsModule ],
Configure the route in app.module.ts as follows:
const appRoutes: Routes = [ { path: 'blog', component: BlogHomeComponent, data: { title: 'Article List' }}, { path: 'blog/post/:id', component: ViewPostComponent }, { path: 'login', component: LoginComponent}, { path: 'register', component: RegisterComponent}, { path: 'create', component: CreateComponent}, { path: '', redirectTo: '/blog', pathMatch: 'full' }, // { path: '**', component: PageNotFoundComponent } ];
Add router-outlet to the app.component.html file:
<div id="main-content" class="bg-color-gray"> <app-header></app-header> <div class="page-container scene-main scene-main--fade_In"> <router-outlet></router-outlet> <app-footer></app-footer> </div> </div>
Add the router link to the blog-home component:
<title-header></title-header> <div class="container"> <div class="row"> <div class="col-lg-12"> <div class="row blog_posts stander_blog"> <div class="col-md-6 col-lg-4" *ngFor="let article of articles"> <article> <a routerLink="post/{{article._id}}"> <div class="post_img card-blog-img"> <img src="https://{{article.photo}}" //[…] </div> </div> </div> </div>
Start the server and serve the Angular application using the CLI with the following commands:
> node server.js > ng serve -o
Open the browser and input http://localhost:4200/blogin in the URL address bar to view the router in action.
You will obtain the following output:
If you click on one of the posts, it will navigate to the view-post page, as shown: