Here is my thymeleaf
<!DOCTYPE html>
<html xmlns:th="www.thymeleaf.org">
<head>
<meta charset="ISO-8859-1">
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
<!-- jQuery library -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<!-- Latest compiled JavaScript -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>
<script type="text/javascript">
$(function(){
$("#submit").on("click" , function(){
var frmData = $("#frm").serialize();
$.ajax({
url : "./getData",
data : frmData,
cache : false,
method : "POST",
success : function(data){
if(data == "success"){
location.reload();
}
},
error : function(jhXHR){
alert(" Something went wrong!! "+jhXHR);
}
})
})
})
$(function(){
$("#delete").on("click" , function(){
var frmData = $("#frm").serialize();
$.ajax({
url : "./deleteData",
data : frmData,
cache : false,
method : "POST",
success : function(data){
if(data == "deleted"){
location.reload();
}
},
error : function(jhXHR){
alert(" Something went wrong!! "+jhXHR);
}
})
})
})
$(function(){
var i = 0;
$("#add").on("click" , function(){
var tbody=document.getElementById("table_id").getElementsByTagName("tbody").item(0);
var tr=document.createElement("tr");
td = document.createElement("td");
td.innerHTML= '';
tr.appendChild(td);
td=document.createElement("td");
td.setAttribute('style', 'text-align:center');
td.innerHTML="<input type='checkbox' checked onclick='return false;'>";
tr.appendChild(td);
td=document.createElement("td");
td.setAttribute('style', 'text-align:center');
td.innerHTML="";
tr.appendChild(td);
td=document.createElement("td");
td.setAttribute('style', 'text-align:center');
td.setAttribute('nowrap', 'nowrap');
td.innerHTML="<input type='text' class='form-control' name='bookList["+i+"].bookName'>";
tr.appendChild(td);
td=document.createElement("td");
td.setAttribute('style', 'text-align:center');
td.setAttribute('nowrap', 'nowrap');
td.innerHTML="<input type='text' class='form-control' name='bookList["+i+"].bookPrice'>";
tr.appendChild(td);
tbody.appendChild(tr);
i++;
});
});
$(function(){
$(':checkbox').click(function() {
var checkbox = $(this);
var row = checkbox.closest('tr');
if (checkbox.is(':checked')) {
row.children().find('label').hide();
row.children().find('input').show();
row.children().find('input').prop("disabled" , false)
}
else {
row.children().find('label').show();
row.children().find('input').not("input[type='checkbox']").hide();
row.children().find('input').not("input[type='checkbox']").prop("disabled" , true)
}
});
})
</script>
<style type="text/css">
.margin-gutter{
margin-top: 10px;
}
input[type="text"].form-control.edit{
display: none;
}
.form-control{
width: auto;
}
</style>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-12 page-header">
<h4 class="text-center">Book Details</h4>
</div>
</div>
<form id="frm" method="post" th:object="${booksemp}">
<div class="row">
<div class="col-md-4">
<button type="button" id="add" class="btn btn-primary">Add</button>
<button type="button" id="delete" class="btn btn-warning">Delete</button>
<button type="button" id="submit" class="btn btn-warning">Submit</button>
</div>
</div>
<div class="row margin-gutter">
<div class="col-md-12">
<table class="table table-striped table-bordered" id="table_id">
<thead>
<tr>
<th>Sl no</th>
<th>Checkbox</th>
<th>Book id</th>
<th>Book Name</th>
<th>Book Price</th>
</tr>
</thead>
<tbody>
<tr th:each="book, bookStat : ${books}">
<td th:text="${bookStat.count}"></td>
<td style="text-align: center;"><input type="checkbox"></td>
<td>
<label th:text="${book.bookId}"></label>
<input type="text" disabled readonly class="form-control edit" th:name="|bookListNew[${bookStat.index}].bookId|" th:value="${book.bookId}">
</td>
<td><label th:text="${book.bookName}"></label>
<input type="text" disabled class="form-control edit" th:name="|bookListNew[${bookStat.index}].bookName|" th:value="${book.bookName}">
</td>
<td>
<label th:text="${book.bookPrice}"></label>
<input type="text" disabled class="form-control edit" th:name="|bookListNew[${bookStat.index}].bookPrice|" th:value="${book.bookPrice}">
</td>
</tr>
</tbody>
</table>
</div>
</div>
</form>
</div>
</body>
</html>
Here is my Book class and BookGroup class
package com.project.binding;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import lombok.Data;
@Data
@Entity
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private String bookId;
private String bookName;
private String bookPrice;
}
package com.project.binding;
import java.util.ArrayList;
import java.util.List;
import lombok.Data;
@Data
public class BookGroup {
private List<Book> bookList;
private List<Book> bookListNew;
public BookGroup() {
bookList = new ArrayList<Book>();
bookListNew = new ArrayList<Book>();
}
}
Here is my controller
package com.project.controller;
import java.util.Collections;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.project.binding.Book;
import com.project.binding.BookGroup;
import com.project.service.BookService;
@Controller
public class BookController {
@Autowired
private BookService bookService;
@GetMapping(value = "/books")
public String displayAllBooks(Model model) {
List<Book> allBooks = bookService.getAllBook();
BookGroup allBooksGroup = new BookGroup();
model.addAttribute("books", allBooks);
model.addAttribute("booksemp", allBooksGroup);
return "books";
}
@PostMapping(value = "/getData")
public @ResponseBody String getData(@ModelAttribute("books") BookGroup book, Model model) {
System.out.println(book.getBookList().removeAll(Collections.singleton(null)));
System.out.println(book.getBookListNew().removeAll(Collections.singleton(null)));
if(null != book.getBookList() || !book.getBookList().isEmpty()) {
bookService.saveBook(book.getBookList());
}
if(null !=book.getBookListNew() || !book.getBookListNew().isEmpty()) {
System.out.println(book.getBookListNew());
bookService.saveBook(book.getBookListNew());
}
return "success" ;
}
}
When adding new row it's working good and fine as expected. When it comes to to edit, the problem I am facing is suppose I checked serial no 2 or 3 rather anything greater than 1, those many empty objects are going with it and getting inserted.
So here i am editing row number 3 and when I submit book 3 gets edit successfully but 2 more rows add up because of 2 empty List object
As you can see over here in console printing List Object [Book(bookId=null, bookName=null, bookPrice=null), Book(bookId=null, bookName=null, bookPrice=null), Book(bookId=3, bookName=BOOK3, bookPrice=1500$)]
What mistake am I doing , Any suggestion with be very much helpful
So i solved it , but I don't know that is it the most efficient way to do it or not
So here is my updated Book class where i have declared isChecked as a Transient so that it does not maps to entity.
package com.project.binding;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Transient;
import lombok.Data;
@Data
@Entity
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private String bookId;
private String bookName;
private String bookPrice;
@Transient
private String isChecked;
}
Here is my controller. Here I am basically taking the List and iterating with Book type and getting each object and seeing that the checkbox is checked or not. If checked that is not null that adding it to a new List that is sendForEdit and sending for update. Hope it helps someone. :) Please do improve my answer if there is a place of improvement.
package com.project.controller;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.project.binding.Book;
import com.project.binding.BookGroup;
import com.project.service.BookService;
@Controller
public class BookController {
@Autowired
private BookService bookService;
@GetMapping(value = "/books")
public String displayAllBooks(Model model) {
List<Book> allBooks = bookService.getAllBook();
BookGroup allBooksGroup = new BookGroup();
model.addAttribute("books", allBooks);
model.addAttribute("booksemp", allBooksGroup);
return "books";
}
@PostMapping(value = "/getData")
public @ResponseBody String getData(@ModelAttribute("books") BookGroup book, Model model) {
if(null != book.getBookList() || !book.getBookList().isEmpty()) {
bookService.saveBook(book.getBookList());
}
List<Book> sendForEdit = new ArrayList<Book>();
if(null !=book.getBookListNew() || !book.getBookListNew().isEmpty()) {
for(Book object : book.getBookListNew()) {
if(object.getIsChecked() != null) {
sendForEdit.add(object);
}
}
bookService.saveBook(sendForEdit);
}
return "success" ;
}
}