Adding QuizEditComponent

Let's start with making our users aware that they can create their own quizzes.

Navigate to the /ClientApp/app/components/navmenu/ folder, open the navmenu.component.html file, and append the following <li> element to the end of the existing <ul> (new lines are highlighted):

[...]

<ul class='nav navbar-nav'>
<li [routerLinkActive]="['link-active']">
<a [routerLink]="['/home']">
<span class='glyphicon glyphicon-home'></span> Home
</a>
</li>
<li [routerLinkActive]="['link-active']">
<a [routerLink]="['/about']">
<span class='glyphicon glyphicon-info-sign'></span> About
</a>
</li>
<li [routerLinkActive]="['link-active']">
<a [routerLink]="['/login']">
<span class='glyphicon glyphicon-log-in'></span> Login
</a>
</li>
<li [routerLinkActive]="['link-active']">
<a [routerLink]="['/quiz/create']">
<span class='glyphicon glyphicon-log-in'></span> Create
Quiz

</a>
</li>
</ul>

[...]

Needless to say, we also need to update our Angular app's RouterModule to add the relevant route accordingly--along with the references to the component that will handle it--in the app.module.shared.ts file:

[...]

import { QuizListComponent } from './components/quiz/quiz-list.component';
import { QuizComponent } from './components/quiz/quiz.component';
import { QuizEditComponent } from './components/quiz/quiz-edit.component';
import { AboutComponent } from './components/about/about.component';

[...]

declarations: [
AppComponent,
NavMenuComponent,
HomeComponent,
QuizListComponent,
QuizComponent,
QuizEditComponent,
AboutComponent,
LoginComponent,
PageNotFoundComponent
],

[...]

RouterModule.forRoot([
{ path: '', redirectTo: 'home', pathMatch: 'full' },
{ path: 'home', component: HomeComponent },
{ path: 'quiz/create', component: QuizEditComponent },
{ path: 'quiz/:id', component: QuizComponent },
{ path: 'about', component: AboutComponent },
{ path: 'login', component: LoginComponent },
{ path: '**', component: PageNotFoundComponent }
])

[...]
Ensure that you add the new quiz/create rule above the quiz/:id one, or the request will be handled by the latter! That :id keyword we used there is a catch-all and hence it will accept numbers and strings.

Saving the file with these changes will immediately raise a TS to compile warning, as there isn't a QuizEditComponent class out there. Let's fix that by adding a quiz-edit.component.ts file within the /ClientApp/app/components/quiz/ folder with the following code:

import { Component, Inject, OnInit } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router";
import { HttpClient } from "@angular/common/http";

@Component({
selector: "quiz-edit",
templateUrl: './quiz-edit.component.html',
styleUrls: ['./quiz-edit.component.css']
})

export class QuizEditComponent {
title: string;
quiz: Quiz;

// this will be TRUE when editing an existing quiz,
// FALSE when creating a new one.
editMode: boolean;

constructor(private activatedRoute: ActivatedRoute,
private router: Router,
private http: HttpClient,
@Inject('BASE_URL') private baseUrl: string) {

// create an empty object from the Quiz interface
this.quiz = <Quiz>{};

var id = +this.activatedRoute.snapshot.params["id"];
if (id) {
this.editMode = true;

// fetch the quiz from the server
var url = this.baseUrl + "api/quiz/" + id;
this.http.get<Quiz>(url).subscribe(res => {
this.quiz = res;
this.title = "Edit - " + this.quiz.Title;
}, error => console.error(error));
}
else {
this.editMode = false;
this.title = "Create a new Quiz";
}
}

onSubmit(quiz: Quiz) {
var url = this.baseUrl + "api/quiz";

if (this.editMode) {
this.http
.post<Quiz>(url, quiz)
.subscribe(res => {
var v = res;
console.log("Quiz " + v.Id + " has been updated.");
this.router.navigate(["home"]);
}, error => console.log(error));
}
else {
this.http
.put<Quiz>(url, quiz)
.subscribe(res => {
var q = res;
console.log("Quiz " + q.Id + " has been created.");
this.router.navigate(["home"]);
}, error => console.log(error));
}
}

onBack() {
this.router.navigate(["home"]);
}
}

As always, we're also adding the quiz-edit.component.html template file:

<h2>{{title}}</h2>
<div class="quiz-edit">
<div>
<label for="title">Quiz title:</label>
<br />
<input type="text" id="title" [(ngModel)]="quiz.Title"
placeholder="choose a title..." />
</div>
<div>
<label for="description">Quiz description:</label>
<br />
<input type="text" id="description"
[(ngModel)]="quiz.Description" placeholder="enter a
description..." />
</div>
<div>
<label for="text">Quiz informative text:</label>
<br />
<textarea id="text" [(ngModel)]="quiz.Text" placeholder="enter
a text..."></textarea>
</div>
<div>
<input *ngIf="editMode" type="button" value="Apply Changes"
(click)="onSubmit(quiz)" />
<input *ngIf="!editMode" type="button" value="Create the Quiz!"
(click)="onSubmit(quiz)" />
<input type="button" value="Cancel" (click)="onBack()" />
</div>
</div>

Also, we're adding the quiz-edit.component.css stylesheet file:

.quiz-edit input[type="text"],
.quiz-edit textarea {
min-width: 500px;
}

The code is well documented as it has a lot of comments explaining what we're doing here and there. However, let's give it a quick overview:

  • The first thing to note is that we used the QuizEditComponent name, not QuizCreateController; the reason we did that is easily understandable by looking at the source code, we'll use the same component to handle either the Create a new Quiz feature--with a PUT request to our QuizController--or the Edit an existing Quiz feature--with a POST one. Doing that will save us a tremendous amount of development time, at the cost of some if-then-else conditional directives in the component class and template files; the whole purpose of the editMode internal property is to help us perform these switches wherever we need them.
  • We already know most of the Angular classes, services, and decorators that we used here--Inject, HttpClient, Router, ActivatedRoute, and so on; no need to repeat ourselves here, we already know why they're here.
  • We added three different UI buttons to our template file, but only two of them will be visible at the same time, depending on whether the component works in editMode or not. We did that trick using the *ngIf directive, which is another extremely useful tool in the Angular Template Syntax shed. As we can easily see, it can be used to conditionally add or remove any element from the DOM as long as we feed it with any property or expression returning a Boolean value.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.113.55