In this tutorial, we will learn how to build a quiz app using Angular and Firebase. The app will allow users to take quizzes, view their scores, and create and manage quizzes as well. We will be using Angular for the front-end and Firebase as our backend, which will handle user authentication and data storage.
Prerequisites
To follow along with this tutorial, you will need to have the following installed on your machine:
- Node.js and npm (Node Package Manager)
- Angular CLI (Command Line Interface)
- Firebase account
Step 1: Set Up Angular Project
Let’s start by setting up our Angular project. Open your terminal and run the following command to install Angular CLI globally:
npm install -g @angular/cli
Next, create a new Angular project by running the following command:
ng new quiz-app
Navigate to the project directory:
cd quiz-app
Step 2: Set Up Firebase Project
Now, let’s set up our Firebase project. Go to the Firebase console and create a new project. Give it a name and select your preferred region.
Once your project is created, click on the “Authentication” tab in the sidebar, and enable the “Email/Password” sign-in method.
Next, click on the “Database” tab in the sidebar, and create a new Cloud Firestore database. Choose the “Start in test mode” option for now.
After creating the database, click on the “Rules” tab and replace the existing rules with the following:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if request.auth != null;
}
}
}
Step 3: Create the Quiz and Authentication Services
Let’s create two services: quiz.service.ts
for managing quizzes and auth.service.ts
for authentication.
Create a new directory called services
under the src/app
directory, and inside it, create quiz.service.ts
and auth.service.ts
files.
In quiz.service.ts
, add the following code:
import { Injectable } from '@angular/core';
import { AngularFirestore, AngularFirestoreCollection } from '@angular/fire/firestore';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class QuizService {
private quizCollection: AngularFirestoreCollection<any>;
constructor(private firestore: AngularFirestore) {
this.quizCollection = this.firestore.collection<any>('quizzes');
}
createQuiz(quiz: any): Promise<any> {
return this.quizCollection.add(quiz);
}
getQuizzes(): Observable<any[]> {
return this.quizCollection.valueChanges({ idField: 'id' });
}
}
In auth.service.ts
, add the following code:
import { Injectable } from '@angular/core';
import { AngularFireAuth } from '@angular/fire/auth';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class AuthService {
user$: Observable<firebase.User | null>;
constructor(public auth: AngularFireAuth) {
this.user$ = this.auth.authState;
}
signUp(email: string, password: string): Promise<any> {
return this.auth.createUserWithEmailAndPassword(email, password);
}
signIn(email: string, password: string): Promise<any> {
return this.auth.signInWithEmailAndPassword(email, password);
}
signOut(): Promise<void> {
return this.auth.signOut();
}
}
Ensure that you have installed the angular/fire
package by running the following command:
ng add @angular/fire
Step 4: Create the Quiz List Component
Now, let’s create the quiz list component which will display a list of available quizzes.
Generate a new component called quiz-list
using the Angular CLI:
ng generate component quiz-list
In the quiz-list.component.ts
file, add the following code:
import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
import { QuizService } from '../services/quiz.service';
@Component({
selector: 'app-quiz-list',
templateUrl: './quiz-list.component.html',
styleUrls: ['./quiz-list.component.css']
})
export class QuizListComponent implements OnInit {
quizzes$: Observable<any[]>;
constructor(private quizService: QuizService) { }
ngOnInit() {
this.quizzes$ = this.quizService.getQuizzes();
}
}
In the quiz-list.component.html
file, add the following code:
<div *ngFor="let quiz of quizzes$ | async">
<h2>{{ quiz.name }}</h2>
<p>{{ quiz.description }}</p>
</div>
Step 5: Create the Quiz Detail Component
Let’s create the quiz detail component which will display the details of a specific quiz.
Generate a new component called quiz-detail
using the Angular CLI:
ng generate component quiz-detail
In the quiz-detail.component.ts
file, add the following code:
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-quiz-detail',
templateUrl: './quiz-detail.component.html',
styleUrls: ['./quiz-detail.component.css']
})
export class QuizDetailComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}
In the quiz-detail.component.html
file, add the following code:
<h2>Quiz Detail</h2>
Step 6: Set Up Routing
Now, let’s set up the routing for our app. Open the app-routing.module.ts
file and replace the existing code with the following:
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { QuizListComponent } from './quiz-list/quiz-list.component';
import { QuizDetailComponent } from './quiz-detail/quiz-detail.component';
const routes: Routes = [
{ path: '', redirectTo: '/quizzes', pathMatch: 'full' },
{ path: 'quizzes', component: QuizListComponent },
{ path: 'quiz/:id', component: QuizDetailComponent }
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
Step 7: Update App Component and HTML
Open the app.component.ts
file and replace the existing code with the following:
import { Component } from '@angular/core';
import { AuthService } from './services/auth.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
constructor(public authService: AuthService) { }
}
Open the app.component.html
file and replace the existing code with the following:
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<a class="navbar-brand" href="/">Quiz App</a>
<ul class="navbar-nav ml-auto">
<li class="nav-item" *ngIf="!authService.user$ | async">
<a class="nav-link" routerLink="/signin">Sign in</a>
</li>
<li class="nav-item" *ngIf="!authService.user$ | async">
<a class="nav-link" routerLink="/signup">Sign up</a>
</li>
<li class="nav-item" *ngIf="authService.user$ | async">
<a class="nav-link" (click)="authService.signOut()">Sign out</a>
</li>
</ul>
</nav>
<div class="container mt-4">
<router-outlet></router-outlet>
</div>
Step 8: Create the Signin and Signup Components
Let’s create the signin and signup components which will handle user authentication.
Generate a new component called signin
using the Angular CLI:
ng generate component signin
Generate a new component called signup
using the Angular CLI:
ng generate component signup
Add the routing for the signin
and signup
components in the app-routing.module.ts
file:
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { QuizListComponent } from './quiz-list/quiz-list.component';
import { QuizDetailComponent } from './quiz-detail/quiz-detail.component';
import { SigninComponent } from './signin/signin.component';
import { SignupComponent } from './signup/signup.component';
const routes: Routes = [
{ path: '', redirectTo: '/quizzes', pathMatch: 'full' },
{ path: 'quizzes', component: QuizListComponent },
{ path: 'quiz/:id', component: QuizDetailComponent },
{ path: 'signin', component: SigninComponent },
{ path: 'signup', component: SignupComponent }
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
In the signin.component.ts
file, add the following code:
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { AuthService } from '../services/auth.service';
@Component({
selector: 'app-signin',
templateUrl: './signin.component.html',
styleUrls: ['./signin.component.css']
})
export class SigninComponent implements OnInit {
email: string;
password: string;
error: string;
constructor(private authService: AuthService, private router: Router) { }
ngOnInit() {
}
signIn() {
this.authService.signIn(this.email, this.password)
.then(() => this.router.navigate(['/']))
.catch(error => this.error = error.message);
}
}
In the signin.component.html
file, add the following code:
<h2>Sign In</h2>
<div class="form-group">
<label for="email">Email</label>
<input type="email" class="form-control" id="email" [(ngModel)]="email">
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" class="form-control" id="password" [(ngModel)]="password">
</div>
<div class="form-group">
<button class="btn btn-primary" (click)="signIn()">Sign In</button>
</div>
<p *ngIf="error" class="text-danger">{{ error }}</p>
In the signup.component.ts
file, add the following code:
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { AuthService } from '../services/auth.service';
@Component({
selector: 'app-signup',
templateUrl: './signup.component.html',
styleUrls: ['./signup.component.css']
})
export class SignupComponent implements OnInit {
email: string;
password: string;
error: string;
constructor(private authService: AuthService, private router: Router) { }
ngOnInit() {
}
signUp() {
this.authService.signUp(this.email, this.password)
.then(() => this.router.navigate(['/']))
.catch(error => this.error = error.message);
}
}
In the signup.component.html
file, add the following code:
<h2>Sign Up</h2>
<div class="form-group">
<label for="email">Email</label>
<input type="email" class="form-control" id="email" [(ngModel)]="email">
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" class="form-control" id="password" [(ngModel)]="password">
</div>
<div class="form-group">
<button class="btn btn-primary" (click)="signUp()">Sign Up</button>
</div>
<p *ngIf="error" class="text-danger">{{ error }}</p>
Step 9: Deploy Firebase Hosting
Now that we have completed the app, let’s deploy it using Firebase Hosting.
First, install the Firebase CLI by running the following command:
npm install -g firebase-tools
Then, log in to Firebase by running the following command and following the authentication prompts:
firebase login
Next, initialize Firebase in your project directory by running the following command:
firebase init
Select “Hosting” as the Firebase CLI feature, and choose the Firebase project you created earlier. For the public directory, enter “dist/quiz-app” (or the directory where the build artifacts are located).
After the initialization is complete, build the Angular app by running the following command:
ng build --prod
Finally, deploy the app to Firebase Hosting by running the following command:
firebase deploy
Congratulations! Your quiz app is now deployed and accessible using the provided Firebase Hosting URL.
Conclusion
In this tutorial, we learned how to build a quiz app using Angular and Firebase. We set up the Angular project, created services for managing quizzes and authentication, and built components for the quiz list, quiz detail, signin, and signup functionality. We also deployed our app using Firebase Hosting, making it available to users on the web.
Feel free to enhance the app by adding more features, such as the ability for users to answer quizzes and view their scores. You can also explore other Angular and Firebase features, such as Firestore transactions for handling quiz submissions and Cloud Functions for server-side logic. Happy coding!