Search Tutorials


E-commerce Website - Online Book Store - Create User Admin Module - Part 2 | JavaInUse
     
 

   
   

E-commerce Website - Online Book Store - Create Book Admin Module - Part 2

In this tutorial series we will be developing an E-commerce website for buying books. In a previous tutorial we created the books module to list the existing books. In this tutorial we will be creating the add books component for adding new books.

E-commerce Website - Online Book Store using Angular 8 + Spring Boot

E-commerce Website - Online Book Store using Angular 8 + Spring Boot Introduction E-commerce Website - Online Book Store - Create Menu Module E-commerce Website - Online Book Store - Create User Admin Module - Part 1 E-commerce Website - Online Book Store - Create User Admin Module - Part 2 E-commerce Website - Online Book Store - Create User Admin Module - Part 3 E-commerce Website - Online Book Store - Create Book Admin Module - Part 1 E-commerce Website - Online Book Store - Create Book Admin Module - Part 2 E-commerce Website - Online Book Store - Create Book Admin Module - Part 3 E-commerce Website - Online Book Store - Create Book Admin Module - Part 4 Ecommerce Website - Online Book Store - Create Book Shopping Module

Video

This tutorial is explained in the below Youtube Video.

Spring Boot Application

We will be modifying the Spring Boot Application we had created in the previous tutorial. In the controller class add a method for saving the Book. In another tutorial we had created an application for selecting an image using Angular8 UI. This image will then be sent to the Spring Boot back end by making REST call. Later this image will be stored in MySQL database. We will be adding two methods - one for getting the book details to save and the other for getting the image bytes for the book.
package com.javainuse.controller;

import java.io.IOException;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import com.javainuse.db.BookRepository;
import com.javainuse.model.Book;

@RestController
@CrossOrigin(origins = "http://localhost:4200")
@RequestMapping(path = "books")
public class BookController {

	private byte[] bytes;

	@Autowired
	private BookRepository bookRepository;

	@GetMapping("/get")
	public List<Book> getBooks() {
		return bookRepository.findAll();
	}

	@PostMapping("/upload")
	public void uploadImage(@RequestParam("imageFile") MultipartFile file) throws IOException {
		this.bytes = file.getBytes();
	}

	@PostMapping("/add")
	public void createBook(@RequestBody Book book) throws IOException {
		book.setPicByte(this.bytes);
		bookRepository.save(book);
		this.bytes = null;
	}
	

}

Angular Code

The page Structure for the Book-Admin Page is as follows-

The Books Component will be having two child components - Add Book Component and View Book Component.

In this tutorial we will be implementing the add book component. Create a new component named add book.
ng generate component admin/books/addbook


We want the add book page to be displayed on the same page as the list users page. What we want is based on the url parameter either the add book page should get displayed  or the list books page. So whenever we will load the books component we will also be checking the url component  
  •  if the url has action=add then the adduser page will be displayed to add new book.  
  • If the url has the  action=view then the view book page will be displayed.  
In Book component, we will be  
  • In the constructor add the activatedRoute and router.
    • ActivatedRoute - Gives us access to the current route instance.
    • Router - using this we can navigate to another page.
  • In the ngOnInit function  we will be  using the ActivatedRoute to check the current route parameters. Check if it contains the action parameter  
  • Implement the addBook function. In this function using the Router we will be  again navigating to the same page - /admin/books,  but with additional parameters of action=add.
import { Component, OnInit } from '@angular/core';
import { Book } from '../../model/Book';
import { HttpClientService } from '../../service/http-client.service';
import { ActivatedRoute, Router } from '@angular/router';

@Component({
  selector: 'app-books',
  templateUrl: './books.component.html',
  styleUrls: ['./books.component.css']
})
export class BooksComponent implements OnInit {

  books: Array<Book>;
  selectedBook: Book;
  action: string;

  constructor(private httpClientService: HttpClientService,
    private activedRoute: ActivatedRoute,
    private router: Router) { }

  ngOnInit() {
    this.httpClientService.getBooks().subscribe(
      response => this.handleSuccessfulResponse(response)
    );
    this.activedRoute.queryParams.subscribe(
      (params) => {
        this.action = params['action'];
      }
    );
  }

  handleSuccessfulResponse(response) {
    this.books = response;
  }

  addBook() {
    this.selectedBook = new Book();
    this.router.navigate(['admin', 'books'], { queryParams: { action: 'add' } });
  }
}



In case of add book we will be adding a button in the books page.For displaying the add books page in the books page we will be making use of the selector tag of the add book component. We will be showing the add books page only when we have the action parameter as add.  Also we will be specifying the book as selectedBook which will be passed as input to the add book component.
<h1>Books Admin</h1>
<a class="btn btn-primary mb-3" (click)="addBook()">Add New Book</a>
<div class="container row">
  <div class="col-md-6">
    <table class="table">
      <thead>
        <tr>
          <th>ID</th>
          <th>Book Name</th>
          <th></th>
        </tr>
      </thead>
      <tbody>
        <tr *ngFor="let book of books">
          <td>{{book.id}}</td>
          <td>{{book.name}}</td>
          <td>
            <button type="button" class="btn btn-primary">Show Details</button>
          </td>
        </tr>
      </tbody>
    </table>
  </div>
  <div class="col-md-6">
    <app-addbook *ngIf="action === 'add'" [book]="selectedBook"></app-addbook>
  </div>

</div>
Also we want to pass the new book that we have created to the child component i.e add book component.  We achieve this using the input tag.    

Finally in the add-books component we create the Book named book and decorate it with the Input annotation. This specifies that the book instance will be provided by the parent component to the add user child component whenever it gets called.
import { Component, OnInit,Input } from '@angular/core';
import { Book } from '../../../model/Book';

@Component({
  selector: 'app-addbook',
  templateUrl: './addbook.component.html',
  styleUrls: ['./addbook.component.css']
})
export class AddbookComponent implements OnInit {

  @Input()
  book: Book;

  constructor() { }

  ngOnInit() {
  }

}

Let us now start the application and goto the books page. Here we can see the list as follows-

Click on the Add button. We can see that the url has changed with the parameter action=add and also the add book page can now be seen.

Next we will be modifying the Add Book Component to take book details and call the spring boot Back end to save the new book. We will be making use of the ngModel directive to bind the form elements to Book object recieved from the parent Books Component.
<h1>Add Book Admin</h1>

<form>
  <label for="name">Name</label>
  <input type="text" class="form-control" id="name" placeholder="Book Name" [(ngModel)]="book.name" name="name">

  <label for="author">Author</label>
  <input type="text" class="form-control" id="author" placeholder="author" name="author" [(ngModel)]="book.author">

  <label for="author">Price</label>
  <input type="text" class="form-control" id="price" placeholder="price" name="price" [(ngModel)]="book.price">


    <br>
    <input type="file" (change)="onFileChanged($event)">
    <img [src]="imgURL" height="200" width="200" *ngIf="imgURL">

  <br>
  <button type="button" class="btn btn-success" (click)="saveBook()">Save Book</button>
</form>
Next in the HttpClientService we will be adding the addBook method. This method will be making a POST call to Spring backend to save the book.
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { User } from '../model/User';
import { Book } from '../model/Book';


@Injectable({
  providedIn: 'root'
})
export class HttpClientService {

  constructor(private httpClient: HttpClient) {
  }

  getUsers() {
    return this.httpClient.get<User[]>('http://localhost:8080/users/get');
  }

  addUser(newUser: User) {
    return this.httpClient.post<User>('http://localhost:8080/users/add', newUser);
  }

  deleteUser(id) {
    return this.httpClient.delete<User>('http://localhost:8080/users/' + id);
  }

  getBooks() {
    return this.httpClient.get<Book[]>('http://localhost:8080/books/get');
  }

 
  addBook(newBook: Book) {
    return this.httpClient.post<Book>('http://localhost:8080/books/add', newBook);
  }
}
Next we will be modifying the add user component typescript file -
  • In the constructor add the HttpClientService and the Router
  • Define the saveBook function to add new book
We will first be saving the image. If the image has been successfully saved we will then be saving the other book details. In another tutorial we had created an application for saving an image using Angular8 UI.
import { Component, OnInit, Input } from '@angular/core';
import { Book } from '../../../model/Book';
import { HttpClientService } from '../../../service/http-client.service';
import { ActivatedRoute, Router } from '@angular/router';
import { HttpClient } from '@angular/common/http';


@Component({
  selector: 'app-addbook',
  templateUrl: './addbook.component.html',
  styleUrls: ['./addbook.component.css']
})
export class AddbookComponent implements OnInit {

  @Input()
  book: Book;
  private selectedFile;
  imgURL: any;

  constructor(private httpClientService: HttpClientService,
    private activedRoute: ActivatedRoute,
    private router: Router,
    private httpClient: HttpClient) { }

  ngOnInit() {
  }

  public onFileChanged(event) {
    console.log(event);
    this.selectedFile = event.target.files[0];

    let reader = new FileReader();
    reader.readAsDataURL(event.target.files[0]);
    reader.onload = (event2) => {
      this.imgURL = reader.result;
    };

  }

  saveBook() {

    const uploadData = new FormData();
    uploadData.append('imageFile', this.selectedFile, this.selectedFile.name);
    this.selectedFile.imageName = this.selectedFile.name;

    this.httpClient.post('http://localhost:8080/books/upload', uploadData, { observe: 'response' })
      .subscribe((response) => {
        if (response.status === 200) {
          this.httpClientService.addBook(this.book).subscribe(
            (book) => {
              this.router.navigate(['admin', 'books']);
            }
          );
          console.log('Image uploaded successfully');
        } else {
          console.log('Image not uploaded successfully');
        }
      }
      );
  }
}
If we now run the application Navigate to the add page - enter the details and click on save

We can see that the list books is still not showing the added book.
If we refresh the books page then we can see the new book.

If we check the database we can see that new book has been added.

So we need to tell the parent books component that child add book component has added a new Book so fetch the books list again. We do this using the Output annotation.

In the add books component file add the Eventemitter. Also if the book is successfully added then we send a signal to the parent books component.
import { Component, OnInit, Input, EventEmitter, Output } from '@angular/core';
import { Book } from '../../../model/Book';
import { HttpClientService } from '../../../service/http-client.service';
import { ActivatedRoute, Router } from '@angular/router';
import { HttpClient } from '@angular/common/http';


@Component({
  selector: 'app-addbook',
  templateUrl: './addbook.component.html',
  styleUrls: ['./addbook.component.css']
})
export class AddbookComponent implements OnInit {

  @Input()
  book: Book;

  @Output()
  bookAddedEvent = new EventEmitter();

  public selectedFile;
  imgURL: any;

  constructor(private httpClientService: HttpClientService,
    private activedRoute: ActivatedRoute,
    private router: Router,
    private httpClient: HttpClient) { }

  ngOnInit() {
  }

  public onFileChanged(event) {
    console.log(event);
    this.selectedFile = event.target.files[0];

    let reader = new FileReader();
    reader.readAsDataURL(event.target.files[0]);
    reader.onload = (event2) => {
      this.imgURL = reader.result;
    };

  }

  saveBook() {
    const uploadData = new FormData();
    uploadData.append('imageFile', this.selectedFile, this.selectedFile.name);
    this.selectedFile.imageName = this.selectedFile.name;

    this.httpClient.post('http://localhost:8080/books/upload', uploadData, { observe: 'response' })
      .subscribe((response) => {
        if (response.status === 200) {
          this.httpClientService.addBook(this.book).subscribe(
            (book) => {
              this.bookAddedEvent.emit();
              this.router.navigate(['admin', 'books']);
            }
          );
          console.log('Image uploaded successfully');
        } else {
          console.log('Image not uploaded successfully');
        }
      }
      );
  }
}
In the books component we specify the function to be called on bookAddedEvent.
<h1>Books Admin</h1>
<a class="btn btn-primary mb-3" (click)="addBook()">Add New Book</a>
<div class="container row">
  <div class="col-md-6">
    <table class="table">
      <thead>
        <tr>
          <th>ID</th>
          <th>Book Name</th>
          <th></th>
        </tr>
      </thead>
      <tbody>
        <tr *ngFor="let book of books">
          <td>{{book.id}}</td>
          <td>{{book.name}}</td>
          <td>
            <button type="button" class="btn btn-primary">Show Details</button>
          </td>
        </tr>
      </tbody>
    </table>
  </div>
  <div class="col-md-6">
    <app-addbook *ngIf="action === 'add'" [book]="selectedBook" (bookAddedEvent)="refreshData()"></app-addbook>
  </div>
</div>
Finally in the books component we define the refreshData function. This function will fetch the user list from the Spring Boot Application.
import { Component, OnInit } from '@angular/core';
import { Book } from '../../model/Book';
import { HttpClientService } from '../../service/http-client.service';
import { ActivatedRoute, Router } from '@angular/router';

@Component({
  selector: 'app-books',
  templateUrl: './books.component.html',
  styleUrls: ['./books.component.css']
})
export class BooksComponent implements OnInit {

  books: Array<Book>;
  selectedBook: Book;
  action: string;

  constructor(private httpClientService: HttpClientService,
    private activedRoute: ActivatedRoute,
    private router: Router) { }

  ngOnInit() {
    this.refreshData();
  }

  refreshData() {
    this.httpClientService.getBooks().subscribe(
      response => this.handleSuccessfulResponse(response)
    );
    this.activedRoute.queryParams.subscribe(
      (params) => {
        this.action = params['action'];
      }
    );
  }

  handleSuccessfulResponse(response) {
    this.books = response;
  }

  addBook() {
    this.selectedBook = new Book();
    this.router.navigate(['admin', 'books'], { queryParams: { action: 'add' } });
  }
}
Now if we go to localhost:4200/admin/books and add a new book-

The new book gets immediately reflected in the users list.


Download Source Code

Download it -
Spring Boot + E-commerce
Angular8 Online Book Store

 

See Also

Spring Boot Hello World Application- Create simple controller and jsp view using Maven Spring Boot Tutorial-Spring Data JPA Spring Boot + Simple Security Configuration Pagination using Spring Boot Simple Example Spring Boot + ActiveMQ Hello world Example Spring Boot + Swagger Example Hello World Example Spring Boot + Swagger- Understanding the various Swagger Annotations Spring Boot Main Menu Spring Boot Interview Questions