Getting Started | Building an Application with Angular
Angular 10 Front-end
In this tutorial We are going to build an application Ecommerce. The project content user, category, product, comment and cart for the user, we will create to parts backend use Spring boot and frontend use Angular. The admin can add, modfily and delete, the user after login can add a comment or buy products as will choose to delivery also can add to his cart. Front-end side is made with Angular 10, HTTPClient & Router.
Technology
Angular 10
Angular HTTPClient
Angular Router
Angular CLI version (10.1.1)
ng new
The Angular CLI makes it easy to create an application that already works, right out of the box. It already follows our best practices!
ng generate
Generate components, routes, services and pipes with a simple command. The CLI will also create simple test shells for all of these.
ng serve
Easily test your app locally while developing.
$ npm install -g @angular/cli
$ ng new ecommerce
$ cd ecommerce
$ ng serve
Project Structure
Install Bootstrap CSS framework
Bootstrap a framework to encourage consistency across internal tools. Before Bootstrap, various libraries were used for interface development, which led to inconsistencies and a high maintenance burden. According to Twitter developer Mark Otto:
Use the following command to install bootstrap in the project.
$ npm install bootstrap --save
Angular Material Tutorial
The idea of this is article is to teach you how to use Angular Material through a hands-on exercise. First, you will check the dependencies that you need on your computer to use and develop with Angular Material, then you will learn how to configure and use different components.
Install Angular Material
Angular Material In addition to the install schematic, Angular Material comes with several schematics (like nav, table, address-form, etc.) that can be used to easily generate pre-built components in your application.
$ ng add @angular/material
MaterialModule
In earlier versions of Angular Material, we could import MaterialModule which imports all components from Angular Material and makes them available in our app. Most of the time we will need to import the most of material.
src/app/material-module.ts
import { NgModule } from '@angular/core';
import { MatAutocompleteModule } from '@angular/material/autocomplete';
import { MatBadgeModule } from '@angular/material/badge';
import { MatBottomSheetModule } from '@angular/material/bottom-sheet';
import { MatButtonModule } from '@angular/material/button';
import { MatButtonToggleModule } from '@angular/material/button-toggle';
import { MatCardModule } from '@angular/material/card';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatChipsModule } from '@angular/material/chips';
import { MatStepperModule } from '@angular/material/stepper';
import { MatDatepickerModule } from '@angular/material/datepicker';
import { MatDialogModule } from '@angular/material/dialog';
import { MatDividerModule } from '@angular/material/divider';
import { MatExpansionModule } from '@angular/material/expansion';
import { MatGridListModule } from '@angular/material/grid-list';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatListModule } from '@angular/material/list';
import { MatMenuModule } from '@angular/material/menu';
import { MatNativeDateModule, MatRippleModule } from '@angular/material/core';
import { MatPaginatorModule } from '@angular/material/paginator';
import { MatProgressBarModule } from '@angular/material/progress-bar';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatRadioModule } from '@angular/material/radio';
import { MatSelectModule } from '@angular/material/select';
import { MatSidenavModule } from '@angular/material/sidenav';
import { MatSliderModule } from '@angular/material/slider';
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
import { MatSnackBarModule } from '@angular/material/snack-bar';
import { MatSortModule } from '@angular/material/sort';
import { MatTableModule } from '@angular/material/table';
import { MatTabsModule } from '@angular/material/tabs';
import { MatToolbarModule } from '@angular/material/toolbar';
import { MatTooltipModule } from '@angular/material/tooltip';
import { MatTreeModule } from '@angular/material/tree';
@NgModule({
exports: [
MatAutocompleteModule,
MatBadgeModule,
MatBottomSheetModule,
MatButtonModule,
MatButtonToggleModule,
MatCardModule,
MatCheckboxModule,
MatChipsModule,
MatStepperModule,
MatDatepickerModule,
MatDialogModule,
MatDividerModule,
MatExpansionModule,
MatGridListModule,
MatIconModule,
MatInputModule,
MatListModule,
MatMenuModule,
MatNativeDateModule,
MatPaginatorModule,
MatProgressBarModule,
MatProgressSpinnerModule,
MatRadioModule,
MatRippleModule,
MatSelectModule,
MatSidenavModule,
MatSliderModule,
MatSlideToggleModule,
MatSnackBarModule,
MatSortModule,
MatTableModule,
MatTabsModule,
MatToolbarModule,
MatTooltipModule,
MatTreeModule,
],
imports: [
MatAutocompleteModule,
MatBadgeModule,
MatBottomSheetModule,
MatButtonModule,
MatButtonToggleModule,
MatCardModule,
MatCheckboxModule,
MatChipsModule,
MatStepperModule,
MatDatepickerModule,
MatDialogModule,
MatDividerModule,
MatExpansionModule,
MatGridListModule,
MatIconModule,
MatInputModule,
MatListModule,
MatMenuModule,
MatNativeDateModule,
MatPaginatorModule,
MatProgressBarModule,
MatProgressSpinnerModule,
MatRadioModule,
MatRippleModule,
MatSelectModule,
MatSidenavModule,
MatSliderModule,
MatSlideToggleModule,
MatSnackBarModule,
MatSortModule,
MatTableModule,
MatTabsModule,
MatToolbarModule,
MatTooltipModule,
MatTreeModule,
]
})
export class MaterialModule { }
Style.css
First of all, we need to create the various entry point CSS files of course. You can just create further scss files at the level where the styles.scss is in your project.
src/style.css
/* You can add global styles to this file, and also import other style files */
@import "~bootstrap/dist/css/bootstrap.min.css";
html,
body {
height: 100%;
}
body {
margin: 0;
background: linear-gradient(
to right,
#d1d8e0,
#d980fa,
#c8d6e5,
#9980fa,
#d1d8e0
);
font-family: initial !important;
color: #222f3e;
}
.title-page {
border-bottom: 1px solid;
font-family: initial;
padding: 10px 20px;
border-radius: 10px;
width: 230px;
text-align: center;
color: #2c3e50;
}
.container {
margin-top: 40px;
}
.fa-info-circle {
color: #4b6584;
font-size: 20px;
cursor: pointer;
margin-left: 22px;
}
#btn {
background: #fff;
border: 0;
}
.modal {
display: none;
position: fixed;
z-index: 1;
padding-top: 100px;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgb(0, 0, 0);
background-color: rgba(0, 0, 0, 0.4);
}
.modal-content {
background-color: #ffd;
margin: auto;
padding: 10px;
border: 1px solid #888;
width: 80%;
}
#price {
color: #c23616;
font-weight: bold;
padding: 10px 20px;
border: 1px solid antiquewhite;
font-size: 16px;
display: inline-block;
}
.fa-eye-slash {
font-size: 22px;
color: #4b6584;
}
.btn-primary {
font-size: 12px;
}
Modals interfaces
The purpose of this class is to map the specified fields with the fields of Spring entity class.
Angular NgModules differ from and complement JavaScript (ES2015) modules. An NgModule declares a compilation context for a set of components that is dedicated to an application domain, a workflow, or a closely related set of capabilities. An NgModule can associate its components with related code, such as services, to form functional units.
Every Angular app has a root module, conventionally named AppModule, which provides the bootstrap mechanism that launches the application. An app typically contains many functional modules.
src/app/Modal.ts
export class User {
id: number;
username: string;
password: string;
address: string;
admin: boolean;
cardNumber: string;
cvv: any;
email: string;
nameOnCard: string;
}
export interface Cart {
id: number;
name: string;
price: number;
quantity: number;
pictureUrl: string;
}
export interface Category {
id: number;
name: string;
}
export interface Comment {
id: number;
addedAt: any;
addedBy: string;
message: string;
title: string;
}
export interface Order {
id: number;
dateCreated: any;
status: any;
}
export class ProductOrder {
id: number;
product: Product;
quantity: number = 1;
constructor(product: Product, quantity: number) {
this.product = product;
this.quantity = quantity = 1;
}
}
export class ProductOrders {
productOrders: ProductOrder[] = [];
}
export interface Product {
id: number;
description: string;
name: string;
pictureUrl: string;
price: number;
}
export interface Tag {
id: number;
name: string;
products: any;
}
export class Item {
name: string;
value: string;
price: number;
}
export class UpdateProduct {
id: number;
name: string;
description: string;
price: number;
pictureUrl: string;
}
export const ITEMS: Item[] = [
{
name: 'TakAway ',
value: 'item_1',
price: 1.99,
},
{
name: 'Relay ',
value: 'item_2',
price: 2.99,
},
{
name: 'Express ',
value: 'item_3',
price: 3.99,
},
{
name: 'Direct ',
value: 'item_4',
price: 4.99,
},
];
Angular Services
Angular services are singleton objects that get instantiated only once during the lifetime of an application. They contain methods that maintain data throughout the life of an application, i.e. data does not get refreshed and is available all the time. The main objective of a service is to organize and share business logic, models, or data and functions with different components of an Angular application.
An example of when to use services would be to transfer data from one controller to another custom service.
Why Should We Use Services in Angular? The separation of concerns is the main reason why Angular services came into existence. An Angular service is a stateless object and provides some very useful functions. These functions can be invoked from any component of Angular, like Controllers, Directives, etc. This helps in dividing the web application into small, different logical units which can be reused.
Create Data services
This service will use Angular HTTPClient to send HTTP requests. You can see that its functions includes CRUD operations and finder method
Create these services in the service package
- CartService- CategoryService
- CommentService
- OrderService
- ProductService
- TagService
- UserService
$ ng generate service service/cart
$ ng generate service service/category
$ ng generate service service/comment
$ ng generate service service/order
$ ng generate service service/product
$ ng generate service service/tag
$ ng generate service service/user
src/app/service/cart.service.ts
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { Cart } from '../model/Model';
const NAME_KEY = 'NAME';
@Injectable({
providedIn: 'root'
})
export class CartService {
constructor(private http: HttpClient) { }
addCartToUser(cart: Cart, idUser: number): Observable<Cart> {
return this.http.post<any>(`http://localhost:8080/api/addCartToUser/${idUser}`, cart);
}
removeFromCart(idCart: number, idUser: number): Observable<Cart> {
return this.http.delete<Cart>(`http://localhost:8080/api/removeFromCart/${idCart}/${idUser}`);
}
findCartById(id: number): Observable<Cart> {
return this.http.get<Cart>(`http://localhost:8080/api/findCartById/${id}`);
}
deleteCart(id: number): Observable<Cart> {
return this.http.delete<Cart>(`http://localhost:8080/api/deleteCart/${id}`);
}
findCartsForUser(idUser: number): Observable<Cart[]> {
return this.http.get<Cart[]>(`http://localhost:8080/api/findCartsForUser/${idUser}`);
}
findByCartName(name: string): Observable<Cart> {
return this.http.get<Cart>(`http://localhost:8080/api/findByCartName/${name}`);
}
saveCartName(name: string) {
window.sessionStorage.removeItem( NAME_KEY);
window.sessionStorage.setItem( NAME_KEY, name);
}
getCartName(): string {
return sessionStorage.getItem( NAME_KEY);
}
}
src/app/service/category.service.ts
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { Category } from '../model/Model';
@Injectable({
providedIn: 'root'
})
export class CategoryService {
constructor(private http: HttpClient) { }
addCategoryToUser(category: Category, idUser: number): Observable<Cart> {
return this.http.post<any>(`http://localhost:8080/api/addCategoryToUser/${idUser}`, category);
}
editCategory(category: Category, id: number): Observable<Category> {
return this.http.post<Category>(`http://localhost:8080/api/editCategory/${id}`, category);
}
findCategoryById(id: number): Observable<Category> {
return this.http.get<Category>(`http://localhost:8080/api/findCategoryById/${id}`);
}
deleteCategory(id: number): Observable<Category> {
return this.http.delete<Category>(`http://localhost:8080/api/deleteCategory/${id}`);
}
findCategoriesForUser(idUser: number): Observable<Category[]> {
return this.http.get<Category[]>(`http://localhost:8080/api/findCategoriesForUser/${idUser}`);
}
findAllCategories(): Observable<Category[]> {
return this.http.get<Category[]>(`http://localhost:8080/api/findAllCategories/`);
}
}
src/app/service/comment.service.ts
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { Comment } from '../model/Model';
@Injectable({
providedIn: 'root'
})
export class CommentService {
constructor(private http: HttpClient) { }
addCommentToProduct(comment: Comment, idProduct: number): Observable<Comment> {
return this.http.post<Comment>(`http://localhost:8080/api/addCommentToProduct/${idProduct}`, comment);
}
editComment(comment: Comment, id: number): Observable<Comment> {
return this.http.post<Comment>(`http://localhost:8080/api/editComment/${id}`, comment);
}
findCommentById(id: number): Observable<Comment> {
return this.http.get<Comment>(`http://localhost:8080/api/findCommentById/${id}`);
}
deleteComment(id: number): Observable<Comment> {
return this.http.delete<Comment>(`http://localhost:8080/api/deleteComment/${id}`);
}
findCommentsForProduct(idProduct: number): Observable<Comment[]> {
return this.http.get<Comment[]>(`http://localhost:8080/api/findCommentsForProduct/${idProduct}`);
}
findAllComments(): Observable<Comment[]> {
return this.http.get<Comment[]>(`http://localhost:8080/api/findAllComments/`);
}
}
src/app/service/order.service.ts
import { HttpClient } from '@angular/common/http' ;
import { Injectable } from '@angular/core' ;
import { Subject } from 'rxjs' ;
import { ProductOrder, ProductOrders } from '../modal/Modal' ;
@Injectable({
providedIn: 'root',
})
export class OrderService {
private ordersUrl = '/api/orders';
private productOrder: ProductOrder;
private orders: ProductOrders = new ProductOrders();
private productOrderSubject = new Subject();
private ordersSubject = new Subject();
private totalSubject = new Subject();
private total: number;
ProductOrderChanged = this.productOrderSubject.asObservable();
OrdersChanged = this.ordersSubject.asObservable();
TotalChanged = this.totalSubject.asObservable();
constructor(private http: HttpClient) {}
saveOrder(order: ProductOrders) {
return this.http.post(this.ordersUrl, order);
}
set SelectedProductOrder(value: ProductOrder) {
this.productOrder = value;
this.productOrderSubject.next();
}
get SelectedProductOrder() {
return this.productOrder;
}
set ProductOrders(value: ProductOrders) {
this.orders = value;
this.ordersSubject.next();
}
get ProductOrders() {
return this.orders;
}
get Total() {
return this.total;
}
set Total( value: number) {
this.total = value;
this.totalSubject.next();
}
}
src/app/service/product.service.ts
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { Product } from '../model/Model';
@Injectable({
providedIn: 'root'
})
export class ProductService {
constructor(private http: HttpClient) { }
addProductToCategory(product: Product, idCategory: number): Observable<Product> {
return this.http.post<Comment>(`http://localhost:8080/api/addProductToCategory/${idCategory}`, product);
}
editProduct(product: Product, id: number): Observable<Product> {
return this.http.post<Product>(`http://localhost:8080/api/editProduct/${id}`, product);
}
findProductById(id: number): Observable<Product> {
return this.http.get<Product>(`http://localhost:8080/api/findProductById/${id}`);
}
deleteProduct(id: number): Observable<Product> {
return this.http.delete<Product>(`http://localhost:8080/api/deleteProduct/${id}`);
}
findProductsForCategory(idCategory: number): Observable<Product[]> {
return this.http.get<Product[]>(`http://localhost:8080/api/findProductsForCategory/${idCategory}`);
}
findAllProducts(): Observable<Product[]> {
return this.http.get<Product[]>(`http://localhost:8080/api/findAllProducts/`);
}
findByName(name: string): Observable<Product> {
return this.http.get<Product>(`http://localhost:8080/api/findByName/${name}`);
}
deleteProductFromTag(idProduct: number, idTag: number): Observable<Product> {
return this.http.delete<Product>(`http://localhost:8080/api/deleteProductFromTag/${idProduct}/${idTag}`);
}
}
src/app/service/tag.service.ts
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { Product, Tag } from '../model/Model';
@Injectable({
providedIn: 'root'
})
export class TagService {
constructor(private http: HttpClient) { }
addTagToProduct(idProduct: number, idTag: number): Observable<Tag> {
return this.http.post<Tag>(`http://localhost:8080/api/addTagToProduct/${idProduct}/${idTag}`);
}
editTag(tag: Tag, id: number): Observable<Tag> {
return this.http.put<Tag>(`http://localhost:8080/api/editTag/${id}`, tag);
}
addTag(tag: Tag): Observable<Tag> {
return this.http.post<Product>(`http://localhost:8080/api/addTag/`, tag);
}
findTagById(id: number): Observable<Tag> {
return this.http.get<Tag>(`http://localhost:8080/api/findTagById/${id}`);
}
deleteTag(id: number): Observable<Tag> {
return this.http.delete<Tag>(`http://localhost:8080/api/deleteTag/${id}`);
}
findTagsForProduct(idProduct: number): Observable<Tag[]> {
return this.http.get<Tag[]>(`http://localhost:8080/api/findTagsForProduct/${idProduct}`);
}
findAllTags(): Observable<any[]> {
return this.http.get<any[]>(`http://localhost:8080/api/findAllTags/`);
}
findByName(name: string): Observable<Product> {
return this.http.get<Product>(`http://localhost:8080/api/findByName/${name}`);
}
deleteProductFromTag(idProduct: number, idTag: number): Observable<Product> {
return this.http.delete<Product>(`http://localhost:8080/api/deleteProductFromTag/${idProduct}/${idTag}`);
}
findAllTagByName(name: number): Observable<Tag> {
return this.http.get<Tag>(`http://localhost:8080/api/findAllTagByName/${name}`);
}
findProductsForTag(idTag: number): Observable<Product[]> {
return this.http.get<Product[]>(`http://localhost:8080/api/findProductsForTag/${idTag}`);
}
}
src/app/service/user.service.ts
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { User } from '../model/Model';
const USERNAME_KEY = 'USERNAME';
@Injectable({
providedIn: 'root'
})
export class UserService {
constructor(private http: HttpClient) { }
addUser(user: User): Observable<User> {
return this.http.post<User>(`http://localhost:8080/api/addUser`, user);
}
editUser(user: User, idUser: number): Observable<User> {
return this.http.post<User>(`http://localhost:8080/api/editUser/${idUser}`, user);
}
findUserById(id: number): Observable<User> {
return this.http.get<User>(`http://localhost:8080/api/findUserById/${id}`);
}
deleteUser(id: number): Observable<User> {
return this.http.delete<User>(`http://localhost:8080/api/deleteUser/${id}`);
}
findAllUsers(): Observable<User[]> {
return this.http.get<User[]>(`http://localhost:8080/api/findAllUsers/`);
}
findByUsername(name: string): Observable<User> {
return this.http.get<User>(`http://localhost:8080/api/findByUsername/${name}`);
}
saveUsername(username: string) {
window.sessionStorage.removeItem( USERNAME_KEY);
window.sessionStorage.setItem( USERNAME_KEY, username);
}
getUsername(): string {
return sessionStorage.getItem( USERNAME_KEY);
}
}
Font Awesome
Font Awesome is a font and icon toolkit based on CSS and Less. It was made by Dave Gandy for use with Bootstrap, and later was incorporated into the BootstrapCDN. Font Awesome has a 38% market share among those websites that use third-party font scripts on their platform, ranking it second place after Google Fonts.
We need to add these links in index.html
src/app/index.html
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.2.0/css/all.css" integrity="sha384-hWVjflwFxL6sNzntih27bfxkr27PmbbK/iSvJ+a4+0owXq79v+lsFkW54bOGbiDQ" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
Components
The @Component decorator identifies the class immediately below it as a component class, and specifies its metadata. In the example code below, you can see that Component is just a class, with no special Angular notation or syntax at all. It's not a component until you mark it as one with the @Component decorator.
The metadata for a component tells Angular where to get the major building blocks that it needs to create and present the component and its view. In particular, it associates a template with the component, either directly with inline code, or by reference. Together, the component and its template describe a view.
In addition to containing or pointing to the template, the @Component metadata configures, for example, how the component can be referenced in HTML and what services it requires.
- app.component.ts
- app.component.html
- app.component.css
The above files were created by default when we created new project using the angular-cli command.
src/app/app.component.ts
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { User, Category } from '../model/Model';
import { CategoryService } from '../service/category.service';
import { UserService } from '../service/user.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
categories: Category[];
user: User = {} as User;
constructor(public dialog: MatDialog, private userService: UserService, private categoryService: CategoryService) {
this.userService.findByUsername(userService.getUsername()).subscribe(user => {
this.user = user;
})
}
ngOnInit(): void {
this.categoryService.findAllCategories().subscribe(categories => {
this.categories = categories;
});
}
login() {
this.dialog.open(LoginComponent);
}
}
src/app/app-component.html
<div class="toolbar" role="banner">
<i class="fas fa-shopping-cart"></i>
<span [routerLink]="['/dashboard']">Welcome to your eccommerce</span>
<button mat-button [matMenuTriggerFor]="menu">Menu <i class="fas fa-caret-down"></i></button>
<mat-menu #menu="matMenu">
<button *mat-menu-item ngFor="let cat of categories"
[routerLink]="['/dsiplay-category/', cat.id]">{{cat.name}}</button>
</mat-menu>
<div class="spacer"></div>
<a type="button" [routerLink]="['/dashboard']">
Home <i class="fas fa-home"></i>
</a>
<a type="button" *ngIf="user" [routerLink]="['/profile/', user.id]">
Profile <i class="fas fa-user"></i>
</a>
<a type="button" (click)="login()" *ngIf="!user">
login
</a>
</div><br
src/app/app-component.css
body {
font-family: initial;
font-size: 16px;
}
.spacer {
flex: 1;
}
a {
margin-right: 20px;
color: aliceblue;
}
.toolbar {
position: absolute;
top: 0;
left: 0;
right: 0;
height: 60px;
display: flex;
align-items: center;
background-color: #1976d2;
color: white;
font-weight: 600;
}
.toolbar img {
margin: 0 16px;
}
.toolbar #twitter-logo {
height: 40px;
margin: 0 16px;
}
.toolbar #twitter-logo:hover {
opacity: 0.8;
}
.fa-shopping-cart {
font-size: 50px;
margin: 10px;
}
We are going to create a login page to login.
- login.component.ts
- login.component.html
src/app/login-component.ts
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { User } from '../model/Model';
import { UserService } from '../service/user.service';
@Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {
user: User = {} as User;
progressBar = true;
constructor(private userService: UserService) {}
ngOnInit(): void { }
addUser() {
this.progressBar = true;
this.userService.addUser(this.user).subscribe((user) => {
this.user = user;
this.userService.saveUsername(user.username);
window.location.replace('/');
});
}
}
src/app/login-component.html
<div class="card" style="width: 18rem;">
<div class="card-body">
<h5 class="card-title">Login</h5>
<form name="user">
<mat-progress-bar mode="buffer" *ngIf="progressBar"></mat-progress-bar>
<div class="mb-3">
<label for="exampleInputEmail1" class="form-label">Username</label>
<input type="text" class="form-control" name="username" [(ngModel)]="user.username">
</div>
<div class="mb-3">
<label for="exampleInputPassword1" class="form-label">Password</label>
<input type="password" class="form-control" name="password" [(ngModel)]="user.password">
</div>
<button type="submit" class="btn btn-primary" (click)="addUser()">Login</button>
</form>
</div>
</div>
We are going to create a profile page to display the profile information.
- profile.component.ts
- profile.component.html
src/app/profile-component.ts
import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { User, Cart, Category } from '../model/Model';
import { AddTagComponent } from './../admin/add-tag/add-tag.component';
import { CategoryService } from './../service/category.service';
import { MatDialog } from '@angular/material/dialog';
import { UserService } from './../service/user.service';
import { AddCategoryComponent } from '../admin/add-category/add-category.component';
import { AddProductComponent } from '../admin/add-product/add-product.component';
import { UpdateProfileComponent } from '../update-profile/update-profile.component';
import { CartService } from '../service/cart.service';
@Component({
selector: 'app-profile',
templateUrl: './profile.component.html',
styleUrls: ['./profile.component.css']
})
export class HomeComponent implements OnInit {
categories: Category[];
carts: Carts[];
user: User = {} as User;
cartLength = 0;
constructor(
private userService: UserService,
private route: ActivatedRoute,
private dialog: MatDialog,
private categoryService: CategoryService,
private cartService: CartService,
private router: Router
) {
this.route.params.subscribe((params) => {
this.userService
.findByUsername(this.userService.getUsername())
.subscribe((user) => {
this.user = user;
this.cartService.findCartsForUser(this.user.id).subscribe((carts) => {
this.carts = carts;
this.cartLength = this.carts.length;
});
this.categoryService.findAllCategories().subscribe((categories) => {
this.categories = categories;
});
});
});
}
ngOnInit(): void { }
logout(id: number) {
window.location.replace('/dashboard');
this.userService.signOut();
}
addCategory(idUser: number) {
this.dialog.open(AddCategoryComponent, {
data: { idUser },
});
}
addProduct(idCategory) {
this.dialog.open(AddProductComponent, {
data: { idCategory },
});
}
addTag() {
this.dialog.open(AddTagComponent);
}
updateProfile() {
this.dialog.open(UpdateProfileComponent);
}
deleteCart(idPro, idUser) {
if (confirm('Are you sure')) {
this.cartService.removeFromCart(idPro, idUser).subscribe(() => {
window.location.reload();
});
}
}
sangleProduct(name) {
this.router.navigate(['/puy/product/', name]);
}
}
src/app/profile-component.html
<div class="row" style="margin-top: 40px;">
<div class="col-lg-4">
<div class="card">
<div class="card-body" style="background-color: #e15f41; padding: 0px; padding-left: 10px; color: wheat;">
<h2 class="card-title">Your Profile {{user.username}}</h2>
</div>
<ul class="list-group list-group-flush" *ngIf="user.email || user.nameOnCard || user.cardNumber">
<li class="list-group-item">{{user.nameOnCard}}</li>
<li class="list-group-item">{{user.email}}</li>
<li class="list-group-item">{{user.address}}</li>
</ul>
<div *ngIf="user.admin==true">
<ul class="list-group" *ngFor="let cat of categories">
<li class="list-group-item d-flex justify-content-between align-items-center">
<span style="cursor: pointer;" [ routerLink]="['/profile/' + user.id + '/categories/', cat.id]"
routerLinkActive="router-link-active"> {{cat.name}} </span>
<button type="button" class="btn btn-outline-primary btn-sm" (click)="addProduct(cat.id)">Add
product</button>
</li>
</ul>
</div>
<div class="card-body">
<button type="button" class="btn btn-danger" (click)="logout(user.id)">Logout</button>
<button type="button" class="btn btn-warning" style="margin: 0 10px;"
(click)="updateProfile(user.id)">Update</button>
<button type="button" class="btn btn-info" ( click)="addCategory(user.id)" *ngIf="user.admin">Add
category</button>
<button type="button" class="btn btn-success" (click)="addTag()" style="margin-left: 10px;"
*ngIf="user.admin">Add tag</button>
</div>
</div>
</div>
<div class="col-lg-8">
<div class="card" *ngIf="cartLength>=1" style="margin-top: 30px;">
<div class="card-body">
<mat-accordion>
<mat-expansion-panel hideToggle>
<mat-expansion-panel-header>
<mat-panel-title>
Your product
</mat-panel-title>
<mat-panel-description>
This is a product saved
</mat-panel-description>
</mat-expansion-panel-header>
<table class="table">
<thead>
<tr>
<th scope="col">Product</th>
<th scope="col">Name</th>
<th scope="col">Price</th>
<th scope="col">Quantity</th>
<th scope="col">Delete</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let cart of carts">
<th scope="row"><img src="{{cart.pictureUrl}}" class="img-thumbnail" style="width: 100px;"></th>
<td>{{cart.name}}</td>
<td>{{cart.price}}</td>
<td>{{cart.quantity}}</td>
<td><span class="material-icons" (click)="deleteCart(cart.id, user.id)"
style="cursor: pointer; color: #e15f41;">delete_forever</span></td>
</tr>
</tbody>
</table>
</mat-expansion-panel>
</mat-accordion>
</div>
</div>
<router-outlet></router-outlet>
</div>
</div>
We are going to create a update-profile page to update informaion.
- update-profile.component.ts
- update-profile.component.html
src/app/update-profile.component.ts
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { User } from '../model/Model';
import { UserService } from '../service/user.service';
@Component({
selector: 'app-update-profile',
templateUrl: './update-profile.component.html',
styleUrls: ['./update-profile.component.css']
})
export class LoginComponent implements OnInit {
user: User = {} as User;
progressBar = true;
constructor(private userService: UserService) {}
ngOnInit(): void {
this.userService.findByUsername(this.userService.getUsername()).subscribe(user => {
this.user = user;
})
}
updateUser(idUser) {
this.progressBar = true;
this.userService.editUser(this.user).subscribe((user) => {
this.user = user;
this.userService.saveUsername(user.username);
window.location.reload();
});
}
}
src/app/update-profile.component.html
<div class="card" style="width: 18rem;">
<div class="card-body">
<h5 class="card-title">Update profile</h5>
<form name="user">
<mat-progress-bar mode="buffer" *ngIf="progressBar"></mat-progress-bar>
<div class="mb-3">
<label for="exampleInputEmail1" class="form-label">Username</label>
<input type="text" class="form-control" name="username" [(ngModel)]="user.username">
</div>
<div class="mb-3">
<label for="exampleInputPassword1" class="form-label">Password</label>
<input type="password" class="form-control" name="password" [(ngModel)]="user.password">
</div>
<div class="mb-3">
<label for="exampleInputEmail1" class="form-label">Card Number</label>
<input type="number" class="form-control" name="cardNumber" [(ngModel)]="user.cardNumber">
</div>
<div class="mb-3">
<label for="exampleInputEmail1" class="form-label">Cvv</label>
<input type="number" class="form-control" name="cvv" [(ngModel)]="user.cvv">
</div>
<div class="mb-3">
<label for="exampleInputEmail1" class="form-label">Name On Card</label>
<input type="text" class="form-control" name="nameOnCard" [(ngModel)]="user.nameOnCard">
</div>
<div class="mb-3">
<label for="exampleInputEmail1" class="form-label">Email</label>
<input type="text" class="form-control" name="email" [(ngModel)]="user.email">
</div>
<div class="mb-3">
<label for="exampleInputEmail1" class="form-label">Address</label>
<input type="text" class="form-control" name="address" [(ngModel)]="user.address">
</div>
<button type="submit" class="btn btn-primary" (click)="updateUser(user.id)()">Save</button>
</form>
</div>
</div>
We are going to create a categories page to display informaion.
- categories.component.ts
- categories.component.html
src/app/admin/categories.component.ts
import { Component, OnInit } from '@angular/core';
import { AddTagToProductComponent} from './../add-tag-to-product/add-tag-to-product.component';
import { Product, Category, User, Tag, Comment} from './../../modal/Modal';
import { AddProductComponent} from './../add-product/add-product.component';
import { AddCategoryComponent} from './../add-category/add-category.component';
import { UserService} from './../../service/user.service';
import { MatDialog} from '@angular/material/dialog';
import { CategoryService} from './../../service/category.service';
import { ProductService} from './../../service/product.service';
import { ActivatedRoute} from '@angular/router';
import { TagService} from 'src/app/service/tag.service';
import { CommentService} from 'src/app/service/comment.service';
@Component({
selector: 'app-categories',
templateUrl: './categories.component.html',
styleUrls: ['./categories.component.css']
})
export class CategoriesComponent implements OnInit {
products: Product[];
user: User = {} as User;
category: Category = {} as Category;
idCategory: number;
panelOpenState: boolean;
tags: Tag[];
comments: Comment[];
constructor(private productService: ProductService, private categoryService: CategoryService,
private route: ActivatedRoute, private dialog: MatDialog, private userService: UserService,
private tagService: TagService, private commentService: CommentService) {
this.route.params.subscribe(
params => {
this.idCategory = this.route.snapshot.params.idCategory;
this.categoryService.findCategoryById(this.idCategory).subscribe(category => {
this.category = category;
this.productService.findProductsForCategory(this.idCategory).subscribe(products => {
this.products = products;
});
})
this.userService.findByUsername(this.userService.getUsername()).subscribe(user => {
this.user = user;
})
this.commentService.findAllComments().subscribe(comments => {
this.comments = comments;
})
}
)
}
ngOnInit(): void { }
addTag(idProduct) {
this.dialog.open(AddTagToProductComponent, {
data: { idProduct }
})
}
showTags(idProduct) {
this.tagService.findTagsForProduct(idProduct).subscribe(tags => {
this.tags = tags;
})
}
deleteCategory(idCategory, idUser) {
if (confirm("Are you sure")) {
this.categoryService.deleteCategory(idCategory).subscribe(() => {
window.location.replace(`/profile/${idUser}`)
})
}
}
editCategory(idCategory) {
this.dialog.open(AddCategoryComponent, {
data: { idCategory }
})
}
deleteProduct(idProduct, idUser) {
if (confirm("Are you sure")) {
this.productService.deleteProduct(idProduct).subscribe(() => {
window.location.replace(`/profile/${idUser}`)
})
}
}
editProduct(idProduct) {
this.dialog.open(AddProductComponent, {
data: { idProduct }
})
}
deleteComment(id) {
this.commentService.deleteComment(id).subscribe(() => {
window.location.reload();
})
}
}
src/app/admin/categories.component.html
<br>
<mat-card>
<div class="row">
<div class="col-md-6">
<mat-card-header>
<div mat-card-avatar></div>
<mat-card-title>{{category.name}}</mat-card-title>
<mat-card-subtitle>All products for this category</mat-card-subtitle>
</mat-card-header>
<mat-card-content>
<div class="card mb-3" *ngFor="let pro of products">
<div class="row g-0">
<div class="col-md-4">
<img src="{{pro.pictureUrl}}" alt="{{pro.name}}" style="width: 150px;">
</div>
<div class="col-md-8">
<div class="card-body">
<h5 class="card-title">{{pro.name}}</h5>
<p class="card-text" style="color: firebrick; font-weight: bold;">Price: {{pro.price}}
</p>
<p class="card-text"><small class="text-muted">{{pro.description}}</small></p>
<ul class="list-group list-group-horizontal">
<li class="list-group-item" style="cursor: pointer;" (click)="editProduct(pro.id)">Update</li>
<li class="list-group-item" style="cursor: pointer;" (click)="deleteProduct(pro.id, user.id)">Delete
</li>
</ul>
</div>
</div>
</div><br>
<div class="row">
<div class="col-md-4">
<span class="badge rounded-pill bg-dark"
style="cursor: pointer; margin: 10px; color: aliceblue; padding: 5px 10px;" (click)="addTag(pro.id)">
Add Tag</span>
</div>
<div class="col-md-8">
<mat-accordion>
<mat-expansion-panel hideToggle>
<mat-expansion-panel-header (click)="showTags(pro.id)">
<mat-panel-title>
Tags
</mat-panel-title>
<mat-panel-description>
for this product
</mat-panel-description>
</mat-expansion-panel-header>
<mat-chip-list class="example-chip" cdkDropList cdkDropListOrientation="horizontal">
<mat-chip class="example-box" cdkDrag *ngFor="let tag of tags">
{{tag.name}}
</mat-chip>
</mat-chip-list>
</mat-expansion-panel>
</mat-accordion><br>
</div>
</div>
</div>
</mat-card-content>
<mat-card-actions>
<button mat-button color="primary" (click)="editCategory(category.id)">Update category</button>
<button mat-button color="accent" (click)="deleteCategory(category.id, user.id)">Delete
category</button>
</mat-card-actions>
</div>
<div class="col-lg-md">
<h2 style="margin-left: 25px;">Comments</h2>
<mat-accordion>
<mat-expansion-panel hideToggle>
<mat-expansion-panel-header>
<mat-panel-title>
Management
</mat-panel-title>
<mat-panel-description>
The comments
</mat-panel-description>
</mat-expansion-panel-header>
<mat-card class="example-card" *ngFor="let comment of comments" style="margin-bottom: 10px;">
<mat-card-header>
<div mat-card-avatar class="example-header-image"></div>
<mat-card-title>Added By {{comment.addedBy}}</mat-card-title>
<mat-card-subtitle>Added At {{comment.addedBy}}</mat-card-subtitle>
</mat-card-header>
<mat-card-content>
<strong>{{comment.title}}</strong>
<p style="width: 350px;">{{comment.message}}</p>
</mat-card-content>
<mat-card-actions>
<button mat-button color="accent" (click)="deleteComment(comment.id)">Delete</button>
</mat-card-actions>
<hr>
</mat-card>
</mat-expansion-panel>
</mat-accordion>
</div>
</div>
</mat-card>
We are going to create a add-category page to add a category.
- add-category.component.ts
- add-category.component.html
src/app/admin/add-category.component.ts
import { Component, Inject, OnInit } from '@angular/core';
import { CategoryService } from './../../service/category.service';
import { Category } from '../model/Model';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
@Component({
selector: 'app-add-category',
templateUrl: './add-category.component.html',
styleUrls: ['./add-category.component.css']
})
export class AddCategoryComponent implements OnInit {
category: Category = {} as Category;
progressBar = flase;
constructor(@Inject(MAT_DIALOG_DATA) public data: any, private categoryService: CategoryService) { }
ngOnInit(): void {
if (this.data.idCategory != null) {
this.categoryService.findCategoryById(this.data.idCategory).subscribe(category => {
this.category = category;
})
}
}
addCategory() {
this.showProgressBar = true;
if (this.data.idCategory != null) {
this.categoryService.editCategory(this.category, this.data.idCategory).subscribe(category => {
this.category = category;
window.location.reload();
})
} else {
this.categoryService.addCategoryToUser(this.category, this.data.idUser).subscribe(category => {
this.category = category;
window.location.reload();
})
}
}
}
src/app/admin/add-category.component.html
<div class="card" style="width: 18rem;">
<div class="card-body">
<h5 class="card-title">Add Category</h5>
<mat-progress-bar mode="buffer" *ngIf="progressBar"></mat-progress-bar>
<form name="category" (ngSubmit)="addCategory()">
<div class="mb-3">
<label for="name" class="form-label">Name</label>
<input type="text" class="form-control" name="name" [(ngModel)]="category.name">
</div>
<button type="submit" class="btn btn-primary">Save</button>
</form>
</div>
</div>
We are going to create a add-product page to add a product.
- add-product.component.ts
- add-product.component.html
src/app/admin/add-product.component.ts
import { Component, Inject, OnInit } from '@angular/core';
import { ProductService } from './../../service/product.service';
import { Product} from '../model/Model';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
@Component({
selector: 'app-add-product',
templateUrl: './add-product.component.html',
styleUrls: ['./add-product.component.css']
})
export class AddProductComponent implements OnInit {
product: Product = {} as Product;
progressBar = flase;
constructor(@Inject(MAT_DIALOG_DATA) public data: any, private productService: ProductService) { }
ngOnInit(): void {
if (this.data.idProduct != null) {
this.productService.findProductById(this.data.idProduct).subscribe(product => {
this.product = product;
})
}
}
addProduct() {
this.showProgressBar = true;
if (this.data.idProduct != null) {
this.productService.editProduct(this.product, this.data.idProduct).subscribe(product => {
this.product = product;
window.location.reload();
})
} else {
this.productService.addProductToCategory(this.product, this.data.idCategory).subscribe(product => {
this.product = product;
window.location.reload();
})
}
}
}
src/app/admin/add-product.component.html
<div class="card" style="width: 18rem;">
<div class="card-body">
<h5 class="card-title">Add Product</h5>
<mat-progress-bar mode="buffer" *ngIf="progressBar"></mat-progress-bar>
<form name="product" (ngSubmit)="addProduct()">
<div class="mb-3">
<label for="name" class="form-label">Name</label>
<input type="text" class="form-control" name="name" [(ngModel)]="product.name">
</div>
<div class="mb-3">
<label for="price" class="form-label">Price</label>
<input type="number" class="form-control" name="price" [(ngModel)]="product.price">
</div>
<div class="mb-3">
<label for="pictureUrl" class="form-label">Photo</label>
<input type="text" class="form-control" name="pictureUrl" [(ngModel)]="product.pictureUrl">
</div>
<div class="mb-3">
<label for="description" class="form-label">Description</label>
<textarea type="text" class="form-control" name="description" [(ngModel)]="product.description"></textarea
</div>
<button type="submit" class="btn btn-primary">Save</button>
</form>
</div>
</div>
We are going to create a add-tag page to add a tag.
- add-tag.component.ts
- add-tag.component.html
src/app/admin/add-tag.component.ts
import { Component, Inject, OnInit } from '@angular/core';
import { TagService } from './../../service/tag.service';
import { Tag} from '../model/Model';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
@Component({
selector: 'app-add-tag',
templateUrl: './add-tag.component.html',
styleUrls: ['./add-tag.component.css']
})
export class AddTagComponent implements OnInit {
tag: Tag = {} as Tag;
progressBar = flase;
constructor(@Inject(MAT_DIALOG_DATA) public data: any, private tagService: TagService) { }
ngOnInit(): void {
if (this.data.idTag != null) {
this.tagService.findTagById(this.data.idTag).subscribe(tag => {
this.tag = tag;
})
}
}
addTag() {
this.showProgressBar = true;
if (this.data.idTag != null) {
this.tagService.editTag(this.tag, this.data.idTag).subscribe(tag => {
this.tag = tag;
window.location.reload();
})
} else {
this.tagService.addTag(this.tag).subscribe(tag => {
this.tag = tag;
window.location.reload();
})
}
}
}
src/app/admin/add-product.component.html
<div class="card" style="width: 18rem;">
<div class="card-body">
<h5 class="card-title">Add Tag</h5>
<mat-progress-bar mode="buffer" *ngIf="progressBar"></mat-progress-bar>
<form name="tag" (ngSubmit)="addProduct()">
<div class="mb-3">
<label for="name" class="form-label">Name</label>
<input type="text" class="form-control" name="name" [(ngModel)]="tag.name">
</div>
<button type="submit" class="btn btn-primary">Save</button>
</form>
</div>
</div>
We are going to create a add-tag-to-product page to add a tag to product.
- add-tag-to-product.component.ts
- add-tag-to-product.component.html
src/app/admin/add-tag-to-product.component.ts
import { Component, Inject, OnInit } from '@angular/core';
import { TagService } from './../../service/tag.service';
import { Tag} from '../model/Model';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
@Component({
selector: 'app-add-tag-to-product',
templateUrl: './add-tag-to-product.component.html',
styleUrls: ['./add-tag-to-product.component.css']
})
export class AddTagToProductComponent implements OnInit {
tags: Tag[];
filterTags: Tag[];
constructor(@Inject(MAT_DIALOG_DATA) public data: any, private tagService: TagService) { }
ngOnInit(): void {
this.tagService.findAllTags().subscribe(tags => {
this.tags = tags;
this.tagService.findTagsForProduct(this.data.idProduct).subscribe(filterTags => {
this.filterTags = filterTags;
this.filterTags.forEach(t => {
this.tags = this.tags.filter(item => item.id !== t.id);
})
})
})
}
selectedValue(event: any) {
const idTag = event.value;
this.tagService.addTagToProduct(this.data.idProduct, idTag).subscribe(() => {
window.location.reload();
})
}
}
src/app/admin/add-tag-to-product.component.html
<div class="card" style="width: 18rem;">
<div class="card-body">
<mat-form-field appearance="fill">
<mat-label>Choose a tags</mat-label>
<mat-select (selectionChange)="selectedValue($event)">
<mat-option *ngFor="let tag of tags" [value]="tag.id">
{{tag.name}}
</mat-option>
</mat-select>
</mat-form-field>
</div>
</div>
Conclusion
Now we have an overview of Angular 10 Backend, We have been building the part of Admin
In the next part we will continue to build user part.