You are on page 1of 30

IMPORTS

HttpClient is Angular's module to perform HTTP requests.


HttpErrorResponse is used for handling HTTP error responses.
IContact likely represents the structure of a contact object.
Observable, catchError, and throwError are part of the RxJS library used for handling
asynchronous operations.
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { IContact } from '../models/IContact';
import { Observable, catchError, throwError } from 'rxjs';

@Injectable({
providedIn: 'root',
})

Injectable Service: @Injectable is a decorator used to mark a class as available to an injector for
instantiation. Here, the ContactService is marked as injectable at the root level (providedIn:
'root'), which means Angular can provide this service

export class ContactService {


private serverUrl: string = 'http://localhost:9000';
constructor(private httpClient: HttpClient) {}

Private Variables: private serverUrl: string = 'http://localhost:9000'; initializes a private


variable serverUrl that holds the base URL for the API.

constructor(private httpClient: HttpClient) {}

Constructor: Angular injects HttpClient into the service through its constructor. This allows the
service to use HttpClient to perform HTTP requests.

1. getAllContacts(): Observable<IContact[]> {
2. let dataUrl: string = `${this.serverUrl}/contacts`;
3. return this.httpClient
4. .get<IContact[]>(dataUrl)
5. .pipe(catchError(this.handleError));
6. }
7. getAllContacts: This method sends an HTTP GET request to
the server to retrieve all contacts. It returns an
Observable of type IContact[] that emits the response data.
getContact(contactId: string): Observable<IContact> {
if (!contactId) {
return throwError('Contact ID is missing or invalid');
}
const dataUrl: string = `${this.serverUrl}/contacts/${contactId}`;
return this.httpClient
.get<IContact>(dataUrl)
.pipe(catchError(this.handleError));
}

2. getContact: This method sends an HTTP GET request to the


server to retrieve a specific contact by its ID. It takes
the contact ID as a parameter and returns an Observable of
type IContact that emits the response data. If the contact ID
is missing or invalid, it throws an error using
the throwError function.

createContact(contact: IContact): Observable<IContact> {


let dataUrl: string = `${this.serverUrl}/contacts`;
return this.httpClient
.post<IContact>(dataUrl, contact)
.pipe(catchError(this.handleError));
}

3. createContact: This method sends an HTTP POST request to


the server to create a new contact. It takes the contact
object as a parameter and returns an Observable of
type IContact that emits the response data.

updateContact(contact: IContact, contactId: string): Observable<IContact> {

let dataUrl: string = `${this.serverUrl}/contacts/${contactId}`;


return this.httpClient
.put<IContact>(dataUrl, contact)
.pipe(catchError(this.handleError));
}
updateContact: This method sends an HTTP PUT request to the
server to update an existing contact. It takes the contact
object and the contact ID as parameters and returns an
Observable of type IContact that emits the response data.

deleteContact(contactId: string): Observable<{}> {


let dataUrl: string = `${this.serverUrl}/contacts/${contactId}`;
return this.httpClient
.delete<{}>(dataUrl)
.pipe(catchError(this.handleError));
}

5. deleteContact: This method sends an HTTP DELETE request to


the server to delete a contact. It takes the contact ID as
a parameter and returns an Observable of type {} (empty
object) that emits the response data.

handleError(error: HttpErrorResponse) {
let errorMessage: string = '';
if (error.error instanceof ErrorEvent) {
errorMessage = `Error: ${error.error.message}`;
} else {
errorMessage = `Status: ${error.status} \n Message: ${error.message}`;
}
return throwError(errorMessage);
}

6. handleError: This method is a centralized error handler for


HTTP requests. It takes an HttpErrorResponse object as a
parameter and returns an error message. If the error is an
instance of ErrorEvent, it extracts the error message from it.
Otherwise, it formats the error message using the status
and message properties of the HttpErrorResponse object.

Observable:

An Observable is a powerful data stream that represents a sequence of values or events over time. It is
part of the Reactive Extensions for JavaScript (RxJS) library often used within Angular for handling
asynchronous operations such as HTTP requests, event handling, and more.
Observables can emit multiple values asynchronously and are used to handle data streams or sequences
over time. They allow you to work with asynchronous data and perform operations like mapping,
filtering, combining, and subscribing to handle the emitted values.

.pipe():

The .pipe() method in Angular is used to combine multiple operators sequentially to transform the
emitted values of an Observable.
Operators like map(), filter(), catchError(), tap(), etc., are part of RxJS and can be chained using .pipe() to
perform various operations on the emitted data from Observables.
This method enables you to create a chain of operations that are applied to the data stream emitted by
the Observable.
Constructor:

In Angular, the constructor is a TypeScript feature used in classes to initialize class properties and
perform necessary setup operations when an instance of the class is created.
In components, services, or other Angular constructs, the constructor is used to inject dependencies by
specifying them as parameters. Angular's dependency injection system uses constructors to provide
instances of services or other dependencies to the class.

IContact.ts – MODEL
export class IContact {
id? : string;
name: string;
email: string;
mobile: string;
}

• id? : string;: This property is optional (? denotes optional) and


represents the unique identifier of a contact. It is of
type string.
• name: string;: This property represents the name of a contact.
It is of type string.
• email: string;: This property represents the email address of a
contact. It is of type string.
• mobile: string;: This property represents the mobile number of a
contact. It is of type string.

1. Models: In Angular, models are used to define the structure


and properties of data objects. They provide a way to
organize and manage data within an application.
2. Interfaces: The I in IContact stands for interface. In
TypeScript, interfaces are used to define the shape of an
object. They specify the properties and their types that an
object must have.

APP-ROUTING.MODULE.TS

1. Routes: Routes define the mapping between a URL path and a


specific component. Each route consists of a path and a
component that will be rendered when the corresponding URL
is accessed.
2. Router: The Angular Router module is responsible for managing
the application's navigation. It matches the current URL to
a route and loads the associated component.
3. Route Parameters: Route parameters allow us to pass dynamic
values in the URL. These values can be extracted and used
within the component.

import { NgModule } from '@angular/core';

import { RouterModule, Routes } from '@angular/router';


import { ContactManagerComponent } from './components/contact-manager/contact-
manager.component';
import { AddContactComponent } from './components/add-contact/add-
contact.component';
import { ViewContactComponent } from './components/view-contact/view-
contact.component';
import { PageNotFoundComponent } from './components/page-not-found/page-not-
found.component';

const routes: Routes = [


{ path: '', redirectTo: 'home', pathMatch: 'full' },
{ path: 'home', component: ContactManagerComponent },
{ path: 'edit/:contactId', component: ContactManagerComponent },
{ path: 'view/:contactId', component: ViewContactComponent },
{ path: '**', component: PageNotFoundComponent },
];

@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
})
export class AppRoutingModule {}

1. The Routes array defines the mapping between URL paths and
components. In this example, we have the following routes:
o An empty path ('') redirects to the 'home' path.
o The 'home' path maps to the ContactManagerComponent.
o The 'edit/:contactId' path maps to the ContactManagerComponent and
allows passing a dynamic contactId parameter.
o The 'view/:contactId' path maps to the ViewContactComponent and
allows passing a dynamic contactId parameter.
o The ** path is a wildcard route that matches any URL
that doesn't match the defined routes. It maps to
the PageNotFoundComponent.
2. The AppRoutingModule class is decorated with @NgModule. Inside
the decorator, we import the RouterModule and call
the forRoot() method, passing in the routes array. This method
configures the router with the provided routes. Finally, we
export the RouterModule to make it available for other modules
to import.

APP.COMPONENT.HTML
<router-outlet></router-outlet>

<router-outlet> is a directive that plays a crucial role in handling


routing and displaying different components based on the current
route. It acts as a placeholder where the content of the active route
is rendered.

In this example, the <router-outlet> directive is used to define the


location where the content of the active route will be rendered. When
the application is loaded, the router will determine the initial route
based on the URL and render the associated component inside the <router-
outlet>.

APP.COMPONENT.TS
import { Component } from '@angular/core';

@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrl: './app.component.css'
})
export class AppComponent {
title = 'my-contact-app';
}

1. Import Statement: The code begins with an import statement


that imports the Component decorator from
the @angular/core module. This decorator is used to define
Angular components.
2. Component Decorator: The @Component decorator is used to
define the metadata for the component. It takes an object
as an argument with various properties that configure the
component.
3. Selector: The selector property specifies the HTML selector
that will be used to render the component. In this case,
the selector is app-root, which means the component will be
rendered as <app-root></app-root> in the HTML.
4. Template URL: The templateUrl property specifies the location
of the HTML template file for the component. In this
example, the template file is app.component.html.
5. Style URL: The styleUrl property specifies the location of the
CSS file for the component. In this example, the CSS file
is app.component.css.
6. Class Definition: The class keyword is used to define a class
named AppComponent. This class serves as the controller for
the component and contains the component's logic and data.
7. Title Property: The title property is defined within
the AppComponent class. It is initialized with the value 'my-
contact-app'. This property can be accessed and used in the
component's template or other parts of the application

APP.MODULE.TS
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { HttpClientModule } from '@angular/common/http';
import { AppRoutingModule } from './app-routing.module';
import { ToastrModule } from 'ngx-toastr';

import { AppComponent } from './app.component';


import { PageNotFoundComponent } from './components/page-not-found/page-not-
found.component';
import { ContactManagerComponent } from './components/contact-manager/contact-
manager.component';
import { AddContactComponent } from './components/add-contact/add-
contact.component';
import { ViewContactComponent } from './components/view-contact/view-
contact.component';
import { CardviewComponent } from './components/cardview/cardview.component';
import { TableviewComponent } from './components/tableview/tableview.component';

@NgModule({
declarations: [
AppComponent,
PageNotFoundComponent,
ContactManagerComponent,
AddContactComponent,
ViewContactComponent,
CardviewComponent,
TableviewComponent
],
imports: [
BrowserModule,
AppRoutingModule,
HttpClientModule,
FormsModule,
NgbModule,
ToastrModule.forRoot({
positionClass: 'toast-bottom-right',
})
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
1. NgModule: The NgModule decorator is used to define a module
in Angular. It takes a metadata object as an argument,
which specifies the declarations, imports, providers, and
bootstrap components of the module.
2. declarations: The declarations property is an array that
contains the components, directives, and pipes that belong
to the module. In the provided code,
the AppComponent, PageNotFoundComponent, ContactManagerComponent, AddConta
ctComponent, ViewContactComponent, CardviewComponent,
and TableviewComponent are declared.
3. imports: The imports property is an array that contains the
modules that are required by the module. In the provided
code,
the BrowserModule, AppRoutingModule, HttpClientModule, FormsModule, NgbMod
ule, and ToastrModule are imported.
4. providers: The providers property is an array that contains
the services and other dependencies that are provided by
the module. In the provided code, no providers are
specified.
5. bootstrap: The bootstrap property is an array that contains
the components that should be bootstrapped when the module
is loaded. In the provided code, the AppComponent is
bootstrapped.

The code structure of the app.module.ts file is as follows:

1. Import statements: The necessary modules and components are


imported using the import keyword.
2. NgModule decorator: The @NgModule decorator is used to define
the module and its metadata.
3. Declarations: The components, directives, and pipes that
belong to the module are declared in the declarations array.
4. Imports: The required modules are imported in
the imports array.
5. Providers: The services and other dependencies provided by
the module are specified in the providers array.
6. Bootstrap: The component that should be bootstrapped is
specified in the bootstrap array.
CONTACT-MANAGER.COMPONENT.TS
import { Component, OnInit } from '@angular/core';

import { Router } from '@angular/router';


import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { ToastrService } from 'ngx-toastr';

import { IContact } from '../../models/IContact';


import { ContactService } from '../../services/contact.service';
import { AddContactComponent } from '../add-contact/add-contact.component';

@Component({
selector: 'app-contact-manager',
templateUrl: './contact-manager.component.html',
styleUrl: './contact-manager.component.css',
})
export class ContactManagerComponent implements OnInit {

1. Component: In Angular, a component is a building block of


an application that encapsulates the logic and UI of a
specific part of the application. The ContactManagerComponent is
an example of a component.
2. OnInit: The OnInit interface is implemented by
the ContactManagerComponent to define a lifecycle hook method
called ngOnInit(). This method is called after the component is
initialized and is used to perform initialization tasks.
3. Dependency Injection: The ContactManagerComponent uses dependency
injection to inject instances of
the ContactService, ToastrService, Router, and NgbModal into its
constructor. Dependency injection is a design pattern that
allows objects to be provided to a class without the class
having to create them.
4. Observable: The ContactService returns an Observable when
retrieving contacts from the server. An Observable is a stream
of data that can be subscribed to and processed
asynchronously.

public contacts: IContact[] = [];


public errorMessage: string | null = null;
isCardView: boolean = true;
• contacts: An array of IContact objects representing the contacts.
• errorMessage: A string that holds any error message that occurs
during contact retrieval or deletion.
• isCardView: A boolean flag that determines whether the
contacts are displayed in a card view or a table view.
constructor(
private contactService: ContactService,
private toastr: ToastrService,
private router: Router,
private modalService: NgbModal
) {}
constructor(
private contactService: ContactService,
private toastr: ToastrService,
private router: Router,
private modalService: NgbModal
) {}

ngOnInit(): void {
this.getAllContactsFromServer();
}

switchToCardView(): void {
this.isCardView = true;
}

switchToTableView(): void {
this.isCardView = false;
}

• ngOnInit(): This method is called when the component is


initialized. It calls the getAllContactsFromServer() method to
retrieve all contacts from the server.
• switchToCardView(): This method sets the isCardView flag to true,
indicating that the contacts should be displayed in a card
view.
• switchToTableView(): This method sets the isCardView flag to false,
indicating that the contacts should be displayed in a table
view.
public getAllContactsFromServer() {
this.contactService.getAllContacts().subscribe(
(data: IContact[]) => {
this.contacts = data;
},
(error) => {
this.errorMessage = error;
}
);
}

• getAllContactsFromServer(): This method calls the getAllContacts() method


of the ContactService to retrieve all contacts from the server.
It subscribes to the Observable returned by
the getAllContacts() method and assigns the retrieved contacts to
the contacts property.

public clickDeleteContact(contactId: string) {


if (
contactId &&
window.confirm('Are you sure you want to delete this contact?')
) {
this.contactService.deleteContact(contactId).subscribe(
() => {
this.toastr.success('Contact deleted successfully');
this.getAllContactsFromServer();
},
(error) => {
this.errorMessage = error;
}
);
}
}
• clickDeleteContact(contactId: string): This method is called when a user
clicks the delete button for a contact. It prompts the user
for confirmation and then calls the deleteContact() method of
the ContactService to delete the contact. If the deletion is
successful, it displays a success message using
the ToastrService and calls getAllContactsFromServer() to refresh the
contact list.
openAddModal() {
const currentView = this.isCardView;
const modalRef = this.modalService.open(AddContactComponent, {
centered: true,
size: 'sm',
beforeDismiss: () => {
this.isCardView = currentView;
return true;
},
});
modalRef.componentInstance.isCardView = currentView;
modalRef.result.then(
(result) => {
this.getAllContactsFromServer();
},
(reason) => {
this.router.navigateByUrl('/');
console.log('Modal is Dismissed');
this.isCardView = currentView;
}
);
}
}

• openAddModal(): This method is called when a user clicks the


add button to open the add contact modal. It creates a
modal using the NgbModal service and sets
the isCardView property of the modal component to the current
value of isCardView. It handles the result of the modal,
refreshing the contact list if a contact is successfully
added, and navigating to the home page if the modal is
dismissed.
CONTACT-MANAGER.COMPONENT.TS
<div class="container mt-5">

<div class="row">
<div class="col">
<h1>Contact Information</h1>

<div class="col d-flex flex-row justify-content-between">


<p>
Your list of contacts appears here. To add a new contact, click on the
"Add New Contact" Button.
</p>
<button (click)="openAddModal()" class="btn btn-light addbtn fw-bold">
Add New Contact
</button>
</div>
</div>

<div class="btn-group-sm mt-3 d-flex flex-row-reverse">


<button
class="btn btn-light"
type="button"
[disabled]="!isCardView"
(click)="switchToTableView()"
title="Switch to Table View"
[ngClass]="{ selected: !isCardView }"
aria-hidden="true"
>
<i class="fa fa-bars fa-2x" aria-hidden="true"></i>
</button>

<button
class="btn btn-light"
type="button"
[disabled]="isCardView"
(click)="switchToCardView()"
title="Switch to Card View"
[ngClass]="{ selected: isCardView }"
aria-hidden="true"
>
<i class="fa-solid fa-border-all fa-2x" aria-hidden="true"></i>
</button>
</div>
</div>
</div>

<div *ngIf="errorMessage">
<div class="container">
<div class="row">
<div class="col">
<p class="h4 text-danger fw-bold">{{ errorMessage }}</p>
</div>
</div>
</div>
</div>

<div class="container mt-3">


<div
*ngIf="isCardView"
class="d-flex flex-row justify-content-between flex-wrap">
<app-cardview
*ngFor="let contact of contacts"
[contact]="contact"
ngClass="col-md-4 card-height d-flex flex-column"
(deleteClick)="clickDeleteContact($event)"
></app-cardview>
</div>

<div *ngIf="!isCardView">
<app-tableview
[contacts]="contacts"
(deleteClick)="clickDeleteContact($event)"
></app-tableview>
</div>
</div>

1.
ngClass: The [ngClass] directive is used to conditionally apply
CSS classes to an element based on a certain condition. In
this case, it is used to apply the "selected" class to the
button when it is active.
2. *ngIf: The *ngIf directive is used to conditionally render
HTML elements based on a certain condition. In this case,
it is used to display an error message if there is an
error.
3. *ngFor: The *ngFor directive is used to iterate over a
collection and render HTML elements for each item in the
collection. In this case, it is used to display a card view
for each contact in the contacts array.
4. Event Binding: The (click) event binding is used to bind a
function to the click event of a button. It allows the
function to be executed when the button is clicked.
VIEW-CONTACT.TS

1. ViewChild: @ViewChild is a decorator in Angular that allows a


component to access a child component, directive, or
element in its template. It is used to interact with
elements or components within the current component.
2. ActivatedRoute: ActivatedRoute is a service in Angular that
provides access to information about a route associated
with a component. It allows us to extract route parameters,
query parameters, and other route-related information.
3. NgbModal: NgbModal is a service provided by the ng-bootstrap
library that allows us to create and manage modal dialogs
in Angular applications. It provides methods to open,
close, and interact with modals.
4. Router: Router is a service in Angular that allows navigation
between different views or components in an application. It
provides methods to navigate to a specific route or URL.
5. IContact: IContact is an interface that defines the structure
of a contact object. It specifies the properties and their
types that a contact object should have.
6. ContactService: ContactService is a service that handles the
communication with the backend API to perform CRUD
operations on contact data. It provides methods to fetch,
create, update, and delete contacts.

import { Component, OnInit, ViewChild, ElementRef } from '@angular/core';


import { ActivatedRoute, ParamMap, Router } from '@angular/router';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';

import { IContact } from '../../models/IContact';


import { ContactService } from '../../services/contact.service';

@Component({
selector: 'app-view-contact',
templateUrl: './view-contact.component.html',
styleUrls: ['./view-contact.component.css'],
})

The ViewContactComponent is defined as a TypeScript class and is


decorated with the @Component decorator. It has the following
structure:
1. Imports: The component imports necessary dependencies and
services from Angular and other libraries.
2. Component Decorator: The @Component decorator is used to
define the metadata for the component. It specifies the
selector, template URL, and style URLs for the component.
3. Class Definition: The component class is defined with the
necessary properties and methods.
4. Constructor: The constructor is used to inject the required
services and dependencies into the component.

export class ViewContactComponent implements OnInit {


@ViewChild('modalContent') modalContent!: ElementRef;
contact: IContact = {} as IContact;
errorMessage: string | null = null;

constructor(
private activatedRoute: ActivatedRoute,
private contactService: ContactService,
private modalService: NgbModal,
private router: Router
) {}

ngOnInit(): void {
this.activatedRoute.paramMap.subscribe((param: ParamMap) => {
const contactId = param.get('contactId');
if (contactId) {
this.contactService.getContact(contactId).subscribe(
(data: IContact) => {
this.contact = data;
this.viewModal(this.modalContent);
},
(error) => {
this.errorMessage = error;
this.router.navigate(['/']);
}
);
}
});
}

viewModal(content: any) {
this.modalService
.open(content, { size: 'xl', backdrop: 'static' })
.result.then(
(result) => {
this.closeModal();
},
(reason) => {
console.log('Modal dismissed.');
}
);
}

closeModal() {
this.modalService.dismissAll('Modal Closed');
}

isNotEmpty() {
return Object.keys(this.contact).length > 0;
}
}

1. Lifecycle Hook: The ngOnInit method is a lifecycle hook that


is called when the component is initialized. It subscribes
to the paramMap observable of the ActivatedRoute to get
the contactId parameter from the URL. It then calls
the getContact method of the ContactService to fetch the contact
details based on the contactId. If successful, it assigns the
fetched contact data to the contact property and opens the
modal view using the viewModal method. If there is an error,
it sets the errorMessage property and navigates back to the
home page.
2. Modal Methods: The viewModal method is responsible for opening
the modal view using the NgbModal service. It takes the modal
content as a parameter and configures the modal options. It
also defines the callback functions for when the modal is
closed or dismissed.
3. Close Modal: The closeModal method is called when the modal is
closed. It uses the dismissAll method of the NgbModal service to
dismiss all open modals.
4. Helper Method: The isNotEmpty method is a helper method that
checks if the contact object is not empty. It returns true if
the contact object has any properties, indicating that there
is contact data to display.
<div class="container">
<div *ngIf="errorMessage">
<div class="container">
<div class="row">
<div class="col">
<p class="h4 text-danger fw-bold">{{ errorMessage }}</p>
</div>
</div>
</div>
</div>

<ng-template #modalContent>
<div class="container mt-5 modal-view">
<div class="row">
<div class="col d-flex flex-row">
<button class="btn btn-light" routerLink="/" (click)="closeModal()">
<i class="fa fa-arrow-alt-circle-left"></i>
</button>
<p class="h3 fw-bold">Contact Information</p>
</div>
</div>
</div>

<div class="container mt-3" *ngIf="isNotEmpty()">


<div class="row align-items-center d-flex flex-column">
<div class="col d-flex flex-row justify-content-between">
<p class="fw-bold headers">Name</p>
<p class="fw-bold headers-e">Email</p>
<p class="fw-bold headers">Mobile</p>
</div>
<div class="col d-flex flex-row justify-content-between details">
<p class="h4 fw-bold">{{ contact.name }}</p>
<p class="h4 fw-bold">{{ contact.email }}</p>
<p class="h4 fw-bold">Mobile : {{ contact.mobile }}</p>
</div>
</div>
</div>
</ng-template>
</div>

1.
*ngIf Directive: This directive is used to conditionally
render elements in the template based on a given
expression. In this code, it is used to display an error
message if there is any error.
2. ng-template Directive: This directive is used to define a
template that can be used later in the code. In this code,
it is used to define the modal content that will be
displayed when viewing the contact information.
3. *ngIf="isNotEmpty()": This expression is used with the ngIf
directive to check if the contact object is not empty. If
it is not empty, the contact information will be displayed.
Angular Component:
AddContactComponent
Introduction
The AddContactComponent is an Angular component that is responsible
for adding and updating contacts in a contact management
application. It provides a form for users to enter contact
information and submit the form to create or update a contact.
This component interacts with the ContactService to perform CRUD
operations on contacts.

Key Concepts
1. @ViewChild: This decorator is used to get a reference to a
child component or element in the template. In this
case, @ViewChild('contactForm', { static: false }) contactForm: NgForm; is used to
get a reference to the NgForm instance of the contact form
in the template.
2. OnInit: This interface is implemented by the component to
define the ngOnInit lifecycle hook. The ngOnInit method is called
after the component is initialized and is used to perform
any initialization logic.
3. IContact: This is an interface that defines the structure of a
contact object. It includes properties such as name, email,
and mobile.
4. ContactService: This is a service that provides methods for
performing CRUD operations on contacts. It is injected into
the AddContactComponent constructor to enable communication with
the backend server.
5. Router: This is a service provided by Angular that is used
for navigation between different routes in the application.
It is injected into the AddContactComponent constructor to
enable navigation to the home route after closing the
modal.
6. ToastrService: This is a service that provides methods for
displaying toast notifications. It is injected into
the AddContactComponent constructor to display success messages
after adding or updating a contact.
import { Component, ViewChild, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { NgForm } from '@angular/forms';
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
import { ToastrService } from 'ngx-toastr';

import { IContact } from '../../models/IContact';


import { ContactService } from '../../services/contact.service';

@Component({
selector: 'app-add-contact',
templateUrl: './add-contact.component.html',
styleUrls: ['./add-contact.component.css'],
})
export class AddContactComponent implements OnInit {
@ViewChild('contactForm', { static: false }) contactForm: NgForm;

contact: IContact = { name: '', email: '', mobile: '' };


errorMessage: string | null = null;
contacts: IContact[] = [];
isEditMode: boolean = false;

constructor(
private contactService: ContactService,
private router: Router,
private toastr: ToastrService,
private activeModal: NgbActiveModal
) {}

The component class has the following properties:

• contactForm: A reference to the NgForm instance of the contact


form in the template.
• contact: An object of type IContact that represents the contact
being added or updated.
• errorMessage: A string that holds any error message that occurs
during form submission.
• contacts: An array of IContact objects that holds all the
contacts fetched from the server.
• isEditMode: A boolean flag that indicates whether the
component is in edit mode or add mode.
ngOnInit(): void {
this.isEditMode = !!this.contact.id;
if (this.isEditMode) {
this.contactService.getContact(this.contact.id).subscribe(
(fetchedContact: IContact) => {
this.contact = fetchedContact;
},
(error) => {
console.error('Error fetching contact:', error);
}
);
}
}

• ngOnInit(): This method is called when the component is


initialized. It checks if the component is in edit mode and
fetches the contact details from the server if necessary.

public getAllContactsFromServer() {
this.contactService.getAllContacts().subscribe(
(data: IContact[]) => {
this.contacts = data;
},
(error) => {
this.errorMessage = error;
}
);
}
• getAllContactsFromServer(): This method is used to fetch all the
contacts from the server and update the contacts array.

public submitForm() {
if (this.isEditMode) {
//MY UPDATE HERE
this.contactService
.updateContact(this.contact, this.contact.id)
.subscribe(
() => {
this.handleFormSubmission();
},
(error) => {
this.errorMessage = error;
}
);
} else {
//MY ADD HERE
this.contactService.createContact(this.contact).subscribe(
() => {
this.handleFormSubmission();
},
(error) => {
this.errorMessage = error;
}
);
}
}
• submitForm(): This method is called when the form is submitted.
If the component is in edit mode, it calls
the updateContact method of the ContactService to update the
contact. Otherwise, it calls the createContact method of
the ContactService to create a new contact.

public closeModal() {
this.router.navigate(['/']);
this.activeModal.close('Modal Closed');
this.contactForm.reset();
}
• closeModal(): This method is called when the modal is closed.
It navigates to the home route, closes the modal, and
resets the contact form.

private handleFormSubmission() {
this.closeModal();
this.getAllContactsFromServer();
this.toastr.success(
this.isEditMode ? 'Changes saved' : 'Successfully Added New Contact'
);
}
• handleFormSubmission(): This method is called after a successful
form submission. It closes the modal, fetches all the
contacts from the server, and displays a success message
using the ToastrService.
ADD-CONTACT.COMPONENT.HTML

<div *ngIf="errorMessage">
<div class="container">
<div class="row">
<div class="col">
<p class="h4 text-danger fw-bold">{{ errorMessage }}</p>
</div>
</div>
</div>
</div>

<div class="container p-3 d-flex justify-content-center">


<div class="col-md-11">
<button class="cancel-modal"><span class="sr-only">Cancel</span></button>
<form #contactForm="ngForm" (ngSubmit)="submitForm()">
<div class="mb-2">
<label for="name" class="form-label fw-bold">Name</label>
<input
[(ngModel)]="contact.name"
name="name"
#name="ngModel"
type="text"
class="form-control"
required
pattern="^[a-zA-Z0-9 ]+$"
aria-label="Enter contact name"
[value]="isEditMode ? contact.name : ''"
/>
<div *ngIf="name.invalid && (name.dirty || name.touched)">
<p *ngIf="name.errors?.required" class="text-danger fw-bold">
Please enter a name
</p>
<p *ngIf="name.errors?.pattern" class="text-danger fw-bold">
Name should be alphanumeric
</p>
</div>
</div>

<div class="mb-2 mt-2">


<label for="name" class="form-label fw-bold mt-2">Contact Number</label>
<input
[(ngModel)]="contact.mobile"
name="mobile"
#mobile="ngModel"
type="text"
class="form-control"
aria-label="Enter contact number"
required
pattern="^0[0-9]{10}$"
[value]="isEditMode ? contact.mobile : ''"
/>
<div *ngIf="mobile.invalid && (mobile.dirty || mobile.touched)">
<p *ngIf="mobile.errors?.required" class="text-danger fw-bold">
Please enter a contact number
</p>
<p *ngIf="mobile.errors?.pattern" class="text-danger fw-bold">
Contact number should be 11 digits
</p>
</div>
</div>

<div class="mb-2 mt-3">


<label for="email" class="form-label fw-bold mt-2">Email address</label>
<input
[(ngModel)]="contact.email"
name="email"
#email="ngModel"
type="email"
class="form-control"
required
email
aria-label="Enter contact email"
[value]="isEditMode ? contact.email : ''"
/>
<div *ngIf="email.invalid && (email.dirty || email.touched)">
<p *ngIf="email.errors?.required" class="text-danger fw-bold">
Please enter an Email address
</p>
<p *ngIf="email.errors?.email" class="text-danger fw-bold">
Please enter a valid Email address
</p>
</div>
</div>

<div class="mb-4 mt-4 d-flex flex-row justify-content-between">


<!--CANCEL BUTTON-->
<button
type="button"
class="btn btn-light cancel-btn"
routerLink="/"
(click)="closeModal()"
>
Cancel
</button>

<!--ADD BUTTON-->
<button
[disabled]="!contactForm.valid"
class="btn btn-danger ms-3 add-btn fw-bold"
type="submit"
>
{{ isEditMode ? "Save Changes" : "Add Contact" }}
</button>
</div>
</form>
</div>
</div>

1. *ngIf directive: This directive is used to conditionally


render HTML elements based on a given expression. In the
code provided, it is used to display an error message if
there is an error while submitting the form.
2. [(ngModel)] directive: This directive is used for two-way data
binding in Angular. It binds the value of an input field to
a property in the component class. In the code provided, it
is used to bind the input fields to the contact object
properties.
3. #name, #mobile, #email template reference variables: These
variables are used to reference the input fields in the
template. They are used for form validation and accessing
the input field values in the component class.
4. ngForm directive: This directive is used to create an Angular
form and handle form submission. In the code provided, it
is used to create the contactForm form and bind it to
the submitForm() method.
5. Form validation: The template includes various form
validation techniques such as required fields, pattern
validation, and email validation. Error messages are
displayed based on the validation status of the input
fields.
6. Buttons: The template includes two buttons - a cancel
button and an add button. The cancel button redirects to
the home page, while the add button is disabled until the
form is valid.
• Error message section: This section displays an error
message if there is an error while submitting the form.
• Form section: This section contains the form elements such
as input fields for name, contact number, and email
address. It also includes the necessary validation and
error message display logic.
• Button section: This section includes the cancel button and
the add button. The cancel button redirects to the home
page, while the add button is disabled until the form is
valid.

CARDVIEW.COMPONENT.TS

Key Concepts
1. @Input: The @Input decorator is used to pass data from a
parent component to a child component. In this case,
the contact property is an input property that receives the
contact information from the parent component.
2. @Output: The @Output decorator is used to emit events from a
child component to a parent component. In this case,
the deleteClick event is emitted when the user clicks on the
delete button, and the contact ID is passed as the event
payload.
3. NgbModal: The NgbModal service is provided by the ng-
bootstrap library and is used to open and control modal
windows in an Angular application. It allows the user to
edit contact details in a modal window.
4. Router: The Router service is provided by the Angular Router
module and is used for navigation between different views
or components in an Angular application. It is used to
navigate to the edit contact route and the home route.
The Cardview component is defined as a TypeScript class with
the @Component decorator. It has an HTML template and a CSS file
associated with it.

The component has an @Input property named contact that receives the
contact information from the parent component. It also has
an @Output property named deleteClick that emits the deleteClick event
when the user clicks on the delete button.

The component has a constructor that injects the NgbModal service


and the Router service. It also has three
methods: openEditModal, clickDeleteContact, and formatPhoneNumber.

import { Component, EventEmitter, Input, Output } from '@angular/core';


import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { Router } from '@angular/router';

import { IContact } from '../../models/IContact';


import { AddContactComponent } from '../add-contact/add-contact.component';

@Component({
selector: 'app-cardview',
templateUrl: './cardview.component.html',
styleUrl: './cardview.component.css',
})
export class CardviewComponent {
@Input() contact: IContact | undefined;
@Output() deleteClick: EventEmitter<string> = new EventEmitter<string>();

openEditModal(contact: IContact): void {


const contactId = contact.id;
const editContactRoute = `/edit/${contactId}`;

this.router.navigateByUrl(editContactRoute).then(() => {
const modalRef = this.modalService.open(AddContactComponent, {
centered: true,
size: 'sm',
});
modalRef.componentInstance.contact = contact;

modalRef.result.then(
(result) => {
this.router.navigate(['/']);
},
(reason) => {
console.log('Modal is dismissed.');
this.router.navigate(['/']);
}
);
});
}
This method is called when the user clicks on the edit button in the
card. It navigates to the edit contact route and opens a modal window
using the NgbModal service. The AddContactComponent is used as the content
of the modal, and the selected contact is passed to it. After the
modal is closed, the method navigates back to the home route.

clickDeleteContact(contactId: string): void {


this.deleteClick.emit(contactId);
}
This method is called when the user clicks on the delete button in the
card. It emits the deleteClick event with the contact ID as the payload.
The parent component can listen to this event and handle the deletion
of the contact.

formatPhoneNumber(phoneNumber: string): string {


if (!phoneNumber) {
return '';
}
return phoneNumber.replace(/(\d{4})(\d{3})(\d{2})(\d{2})/, '$1-$2-$3-$4');
}
This method is used to format phone numbers in a specific pattern. It
takes a phone number as input and returns the formatted phone number.
If the phone number is empty, it returns an empty string.
The replace method is used with a regular expression to insert dashes at
specific positions in the phone number.

The Cardview component in Angular is a reusable component that


displays contact information in a card format. It allows users to
view, edit, and delete contact details. It utilizes
the @Input and @Output decorators, the NgbModal service, and
the Router service to provide these functionalities. The component also
includes methods for opening the edit modal, deleting a contact, and
formatting phone numbers.

You might also like