Activity 1: Setting up the Environment
In this activity, you will set up your development environment for full-stack web development with MongoDB, Node.js, and Angular.
Create a new Angular project using the CLI. Create a new Node.js project using npm init.
Activity 2: Building a Simple RESTful API
In this activity, you will build a simple RESTful API using Node.js and Express.js.
Representational State Transfer is a way of doing Web Services.
A RESTful API is of running a method on class on a remote server and getting back the result. RESTful API are a standard to deliver web services via the Service Oriented Architecture way of doing things.
My Video on Service Oriented Architectures: Install Express.js using npm install express. Create a new Express.js app. Define a GET endpoint to retrieve all blog posts. Define a POST endpoint to create a new blog post. Test the API using Postman.
Here is an example of what the server.js file might look like:
const express = require('express');
const mongoose = require('mongoose');
const app = express();
mongoose.connect('mongodb://localhost/blog', { useNewUrlParser: true, useUnifiedTopology: true });
const postSchema = new mongoose.Schema({
title: String,
body: String,
date: { type: Date, default: Date.now }
});
const Post = mongoose.model('Post', postSchema);
app.use(express.json());
app.get('/posts', async (req, res) => {
const posts = await Post.find().exec();
res.send(posts);
});
app.post('/posts', async (req, res) => {
const post = new Post(req.body);
await post.save();
res.send(post);
});
app.listen(3000, () => {
console.log('Server started on port 3000');
});
Activity 3: Building the Front-End
In this activity, you will build the front-end of the application using Angular.
Use the Angular CLI to generate a new component for displaying blog posts. Use the Angular HTTP client to retrieve blog posts from the API. Display the blog posts in the component.
Here is an example of what the post-list.component.ts file might look like:
import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Component({
selector: 'app-post-list',
templateUrl: './post-list.component.html',
styleUrls: ['./post-list.component.css']
})
export class PostListComponent implements OnInit {
posts: any[] = [];
constructor(private http: HttpClient) { }
ngOnInit(): void {
this.loadPosts();
}
loadPosts() {
this.http
.get(`http://localhost:3000/posts`)
.subscribe((posts: any[]) => {
this.posts = posts;
});
}
}
And here is an example of what the post-list.component.html file might look like:
<h2>Blog Posts</h2>
<ul>
<li *ngFor="let post of posts">
<h3>{{ post.title }}</h3>
<p>{{ post.body }}</p>
<small>{{ post.date | date }}</small>
</li>
</ul>
Activity 4: Adding Pagination to the Front-End
In this activity, you will add pagination to the front-end of the application using Angular.
Add a page property to the PostListComponent class. Add a perPage property to the PostListComponent class. Modify the loadPosts method to accept page and perPage parameters. Modify the loadPosts method to use the HttpClient params option to send page and perPage as query parameters. Modify the post-list.component.html file to display pagination controls.
Here is an example of what the post-list.component.ts file might look like:
import { Component, OnInit } from '@angular/core';
import { HttpClient, HttpParams } from '@angular/common/http';
@Component({
selector: 'app-post-list',
templateUrl: './post-list.component.html',
styleUrls: ['./post-list.component.css']
})
export class PostListComponent implements OnInit {
posts: any[] = [];
page = 1;
perPage = 10;
constructor(private http: HttpClient) { }
ngOnInit(): void {
this.loadPosts();
}
loadPosts() {
const params = new HttpParams()
.set('page', this.page.toString())
.set('perPage', this.perPage.toString());
this.http
.get(`http://localhost:3000/posts`, { params })
.subscribe((posts: any[]) => {
this.posts = posts;
});
}
nextPage() {
this.page++;
this.loadPosts();
}
prevPage() {
this.page--;
this.loadPosts();
}
}
And here is an example of what the post-list.component.html file might look like:
<h2>Blog Posts</h2>
<ul>
<li *ngFor="let post of posts">
<h3>{{ post.title }}</h3>
<p>{{ post.body }}</p>
<small>{{ post.date | date }}</small>
</li>
</ul>
<button (click)="prevPage()" [disabled]="page === 1">Previous page</button>
<button (click)="nextPage()">Next page</button>
Activity 5: Adding Validation to the Front-End
In this activity, you will add validation to the front-end of the application using Angular.
Use Angular's built-in validation directives to add client-side validation to the NewPostComponent. Disable the form submission button if the form is invalid. Modify the PostListComponent to display error messages if the API returns a validation error.
Here is an example of what the new-post.component.html file might look like:
<h2>New Post</h2>
<form (submit)="createPost()" #postForm="ngForm">
<div>
<label for="title">Title:</label>
<input id="title" name="title" [(ngModel)]="post.title" required>
<div *ngIf="postForm.controls.title.invalid && (postForm.controls.title.dirty || postForm.controls.title.touched)">
<div *ngIf="postForm.controls.title.errors.required">Title is required</div>
</div>
</div>
<div>
<label for="body">Body:</label>
<textarea id="body" name="body" [(ngModel)]="post.body" required></textarea>
<div *ngIf="postForm.controls.body.invalid && (postForm.controls.body.dirty || postForm.controls.body.touched)">
<div *ngIf="postForm.controls.body.errors.required">Body is required</div>
</div>
</div>
<button type="submit" [disabled]="postForm.invalid">Create post</button>
</form>
<div *ngIf="errorMessage">
{{ errorMessage }}
</div>
And here is an example of what the new-post.component.ts file might look like:
import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Router } from '@angular/router';
@Component({
selector: 'app-new-post',
templateUrl: './new-post.component.html',
styleUrls: ['./new-post.component.css']
})
export class NewPostComponent {
post = {
title: '',
body: ''
};
errorMessage = '';
constructor(private http: HttpClient, private router: Router) { }
createPost() {
this.http
.post(`http://localhost:3000/posts`, this.post)
.subscribe(
() => {
this.router.navigate(['/posts']);
},
(error) => {
if (error.status === 422) {
this.errorMessage = error.error.message;
} else {
this.errorMessage = 'An error occurred while creating the post.';
}
}
);
}
}
Activity 6: Adding Authentication to the API
In this activity, we will add authentication to our API using JSON Web Tokens (JWT).