Springboot Thymeleaf : How to format row according to a condition - spring-boot

I have a page that displays the list of all the journals available. I want to write a thymeleaf expression language that highlights the already subscribed journals using there journal id . So for all the subscribed journals the text for the hyper-link href should be "Unsubscribe" and vice verse if its not subscribed.
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head lang="en">
<title>TBD</title>
<!--/*/ <th:block th:include="fragments/headinc :: head"></th:block> /*/-->
<link rel="stylesheet"
href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css" />
</head>
<body>
<div class="container">
<h1 th:text="'Hello, ' + ${user.fullname} + '!'" />
<p>Manage your subscriptions here</p>
<form role="form" id="form-search" class="form-inline"
th:action="#{/subscriptions}" method="get">
<input type="text" class="form-control" id="filter" name="filter"
placeholder="Enter filter"></input>
<button type="submit" class="btn btn-default">
<span class="glyphicon glyphicon-search"></span> Search
</button>
<a th:href="#{/logout}" class="btn btn-link" role="button">Logout</a>
</form>
<div th:if="${not #lists.isEmpty(journals)}">
<form role="form" id="form-subscribe" th:action="#{/subscribe}"
method="post">
<input type="hidden" name="journalId" id="journalId" />
</form>
<table id="table" class="table">
<thead>
<tr>
<th>Subject</th>
<th>Filename</th>
<th>Tags</th>
<th>View</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr th:each="journal : ${journals}">
<td th:text="${journal.subject}">Id</td>
<td th:text="${journal.filename}">Product Id</td>
<td th:text="${journal.tags}">Description</td>
<td><a>View</a></td>
<td><a id="href"
th:href="'javascript:subscribe(\'' + ${journal.id} + '\');'">Subscribe</a>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</body>
<script type="text/javascript">
function subscribe(journalId) {
$('#journalId').val(journalId);
$('#form-subscribe').submit();
}
</script>
<script type="text/javascript" th:inline="javascript">
/*<![CDATA[*/
$(document).ready(function() {
var modelAttributeValue = [[${subscriptions}]];
console.log(modelAttributeValue);
alert(modelAttributeValue);
var array = modelAttributeValue.split(';');
console.log(array);
alert(array);
});
/*]]>*/
</script>
</html>
Controller
#Controller
public class SubscriptionController {
#Autowired
private SubscriberService subscriberService;
#RequestMapping(value = "/subscribe", method = RequestMethod.POST)
String subscribe(Model model, #RequestParam("journalId") Integer journalId) {
JournalToken token = (JournalToken) SecurityContextHolder.getContext().getAuthentication();
Account user = (Account) token.getCredentials();
model.addAttribute("user", user);
Journal journal = this.subscriberService.findJournalById(journalId);
this.subscriberService.subscribeJournalForSubscriber(journal, user);
return "redirect:subscriptions";
}
#RequestMapping(value = "/subscriptions", method = RequestMethod.GET)
String list(Model model) {
JournalToken token = (JournalToken) SecurityContextHolder.getContext().getAuthentication();
Account user = (Account) token.getCredentials();
model.addAttribute("user", user);
ArrayList<Journal> journals = this.subscriberService.FindAllJournals();
model.addAttribute("journals", journals);
StringBuilder sub = new StringBuilder();
ArrayList<Subscription> subscribed = this.subscriberService.getSubscribedJournalsForSubscriber(user);
model.addAttribute("subscriptions", subscribed);
return "subscriptions";
}
}
Model subscriptions
#Entity
#Table(uniqueConstraints={#UniqueConstraint(columnNames={"userId", "journalId"})})
public class Subscription {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#Version
private Integer version;
private Integer userId;
private Integer journalId;
public void setId(Integer id) {
this.id = id;
}
public Integer getId() {
return this.id;
}
public void setVersion(Integer version) {
this.version = version;
}
public Integer getVersion() {
return this.version;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public Integer getUserId() {
return this.userId;
}
public void setJournalId(Integer journalId) {
this.journalId = journalId;
}
public Integer getJournalId() {
return this.journalId;
}
}

you can change your arrayList subscribed to have only the ids of journals (more optimised).
So, in the controller you can have something like this
ArrayList<Integer> subscribed =
this.subscriberService.getSubscribedJournalsForSubscriber(user); //modify it so it returns the journals ids instead of the whole object(Subscription)
then in the thymeleaf change the anchor with something like this
<a id="href" th:href="'javascript:subscribe(\'' + ${journal.id} + '\');'">
<span th:if="${#lists.contains(subscriptions, journal.id) }" th:text="Unsubscribe"> Unsubscribe </span>
<span th:if="not ${#lists.contains(subscriptions, journal.id) }" th:text="Subscribe"> Subscribe </span>
</a>
Have a look at the documentation of thymeleaf http://www.thymeleaf.org/doc/tutorials/2.1/usingthymeleaf.html
/*
* Check if element or elements are contained in list
*/
${#lists.contains(list, element)}
${#lists.containsAll(list, elements)}

Related

Thymeleaf not loading content when I add validation in Spring Boot

Intro:
My app works in the general sense. All as I want. But when I set up validation (and it does work), it won't load all my hidden content. I moved it all to its own page, and it still did not work. So I am asking here with my original set up. I understand it is messy, but I will clean it up once I figure this out.
I can't find anything related to my problem, which makes me think, the way that I am doing it might not be the best approach. So pointers are more than welcome.
Problem:
Adding Validation to my forms breaks my Thymeleaf template. Unable to hide and unhide elements. It looks stuck. But on the terminal, the program does run fine.
My controller
(it's big... I still need to learn how to break it in smaller chunks.)
#Controller
#SessionAttributes({"guess", "word", "result", "level", "attempt", "message", "credits", "name", "gameScore"})
public class GameController {
private static final String WORD_TO_GUESS_CONSTANT = "WORD_TO_GUESS";
private static final String GUESSED_WORD_CONSTANT = "GUESSED_WORD";
private static final String RESULT_CONSTANT = "RESULT_WORD";
private static final String ATTEMPTS_CONSTANT = "ATTEMPTS";
private static final String TOTAL_ATTEMPTS_CONSTANT = "TOTAL_ATTEMPTS";
private static final String MESSAGE_CONSTANT = "MESSAGE";
private static final String CREDITS_CONSTANT = "CREDITS";
private static final String SELECTED_LEVEL_CONSTANT = "SELECTED_LEVEL";
private static final String NAME_CONSTANT = "NAME";
private static final String GAME_SCORE_CONSTANT = "GAME_SCORE";
private static final String SCORE_MULTIPLIER_CONSTANT = "SCORE_MULTIPLIER";
#Autowired
private RandomWordService randomWord;
#Autowired
private WordCheckService checkGuess;
#Autowired
private IsWordCorrectService isWordCorrectService;
#Autowired
private ScoreSavingService scoreSavingService;
#ModelAttribute("gameDto")
public GameDTO guessDTOForm() {
return new GameDTO();
}
#ModelAttribute("score")
public Score score() {
return new Score();
}
// GAME METHODS
#GetMapping(value = "/index")
public String home(Model model,
final HttpServletRequest request,
final HttpSession session,
GameDTO gameDTO,
Score score) {
model.addAttribute("name", session.getAttribute(NAME_CONSTANT));
model.addAttribute("levelSelected", session.getAttribute(SELECTED_LEVEL_CONSTANT));
model.addAttribute("attempt", session.getAttribute(ATTEMPTS_CONSTANT));
model.addAttribute("credits", session.getAttribute(CREDITS_CONSTANT));
model.addAttribute("attemptStart", session.getAttribute(TOTAL_ATTEMPTS_CONSTANT));
model.addAttribute("guess", session.getAttribute(GUESSED_WORD_CONSTANT));
model.addAttribute("result", session.getAttribute(RESULT_CONSTANT));
model.addAttribute("message", session.getAttribute(MESSAGE_CONSTANT));
model.addAttribute("gameScore", session.getAttribute(GAME_SCORE_CONSTANT));
model.addAttribute("lvlName", Level.values());
return "index";
}
#PostMapping(value = "/loadgame")
public String loadWord(
final HttpSession session, final HttpServletRequest request,
#ModelAttribute("score") Score score,
#Valid GameDTO gameDTO, BindingResult bindingResult,
Model model
) throws IOException {
if (bindingResult.hasErrors()) {
model.addAttribute("lvlName", Level.values());
model.addAttribute("name", session.getAttribute(NAME_CONSTANT));
model.addAttribute("levelSelected", session.getAttribute(SELECTED_LEVEL_CONSTANT));
model.addAttribute("attempt", session.getAttribute(ATTEMPTS_CONSTANT));
model.addAttribute("credits", session.getAttribute(CREDITS_CONSTANT));
model.addAttribute("attemptStart", session.getAttribute(TOTAL_ATTEMPTS_CONSTANT));
model.addAttribute("guess", session.getAttribute(GUESSED_WORD_CONSTANT));
model.addAttribute("result", session.getAttribute(RESULT_CONSTANT));
model.addAttribute("message", session.getAttribute(MESSAGE_CONSTANT));
model.addAttribute("gameScore", session.getAttribute(GAME_SCORE_CONSTANT));
return "index";
}
// NEW GAME
String word = (String) request.getSession().getAttribute(WORD_TO_GUESS_CONSTANT);
if (word == null) {
request.getSession().setAttribute(NAME_CONSTANT, gameDTO.getPlayerName());
request.getSession().setAttribute(ATTEMPTS_CONSTANT, gameDTO.getLvlName().getAttempts());
request.getSession().setAttribute(WORD_TO_GUESS_CONSTANT, randomWord.selectRandomWord());
request.getSession().setAttribute(CREDITS_CONSTANT, gameDTO.getCredit());
request.getSession().setAttribute(SELECTED_LEVEL_CONSTANT, gameDTO.getLvlName().getLvlName());
request.getSession().setAttribute(TOTAL_ATTEMPTS_CONSTANT, gameDTO.getLvlName().getAttempts());
request.getSession().setAttribute(GAME_SCORE_CONSTANT, gameDTO.getScore());
request.getSession().setAttribute(SCORE_MULTIPLIER_CONSTANT, gameDTO.getLvlName().getMultiplier());
gameDTO.setWord((String) session.getAttribute(WORD_TO_GUESS_CONSTANT));
}
model.addAttribute("message", "");
return "redirect:/index";
}
#PostMapping(value = "/guess")
public String guessWord(
final HttpSession session,
final HttpServletRequest request,
#ModelAttribute("score") Score score,
#Valid GameDTO gameDTO, BindingResult bindingResult) throws IOException {
if (bindingResult.hasErrors()) {
return "index";
}
// variables
int attempts = (int) session.getAttribute(ATTEMPTS_CONSTANT);
int credits = (int) session.getAttribute(CREDITS_CONSTANT);
int startAttempts = (int) session.getAttribute(TOTAL_ATTEMPTS_CONSTANT);
String name = (String) session.getAttribute(NAME_CONSTANT);
// check word
String wordToGuess = (String) session.getAttribute(WORD_TO_GUESS_CONSTANT);
String guess = gameDTO.getGuess();
String result = checkGuess.resultWord(wordToGuess, guess);
String lvl = (String) session.getAttribute(SELECTED_LEVEL_CONSTANT);
// adjust score according to result
boolean wordIsCorrect = isWordCorrectService.isTheWordCorrect(result, wordToGuess);
int gameScore = (int) session.getAttribute(GAME_SCORE_CONSTANT);
int scoreMultiplier = (int) request.getSession().getAttribute(SCORE_MULTIPLIER_CONSTANT);
int wrongWord = gameDTO.getWrongWord();
int initialScore = gameDTO.getStartScore();
int finalScorePerWord = initialScore * scoreMultiplier;
// GAME LOGIC
if (!wordIsCorrect) {
String message = "";
message = "Wrong! Try again!";
request.getSession().setAttribute(MESSAGE_CONSTANT, message);
request.getSession().setAttribute(ATTEMPTS_CONSTANT, --attempts);
request.getSession().setAttribute(GAME_SCORE_CONSTANT, gameScore - wrongWord);
log(GameController.class, "Updated score: " + session.getAttribute(GAME_SCORE_CONSTANT));
if (attempts == 0) {
request.getSession().setAttribute(CREDITS_CONSTANT, --credits);
message = "Sorry, the word was: [ " + session.getAttribute(WORD_TO_GUESS_CONSTANT) + " ]";
request.getSession().setAttribute(MESSAGE_CONSTANT, message);
request.getSession().setAttribute(ATTEMPTS_CONSTANT, startAttempts);
request.getSession().setAttribute(WORD_TO_GUESS_CONSTANT, randomWord.selectRandomWord());
}
if (credits == 0) {
message = "Game over!";
request.getSession().setAttribute(MESSAGE_CONSTANT, message);
request.getSession().setAttribute(GAME_SCORE_CONSTANT, gameScore);
// SAVE SCORE
score.setGameScore(gameScore);
score.setName(name);
score.setSelectedLevelName(lvl);
scoreSavingService.saveScore(score);
log(GameController.class, "Final score: " + session.getAttribute(GAME_SCORE_CONSTANT));
}
} else {
String message = "Correct! Guess another word!";
wordToGuess = randomWord.selectRandomWord();
gameDTO.setWord(wordToGuess);
request.getSession().setAttribute(MESSAGE_CONSTANT, message);
request.getSession().setAttribute(WORD_TO_GUESS_CONSTANT, wordToGuess);
request.getSession().setAttribute(ATTEMPTS_CONSTANT, startAttempts);
request.getSession().setAttribute(GAME_SCORE_CONSTANT, gameScore + finalScorePerWord);
log(GameController.class, "Current score 2: " + session.getAttribute(GAME_SCORE_CONSTANT));
}
request.getSession().setAttribute(GUESSED_WORD_CONSTANT, guess);
request.getSession().setAttribute(RESULT_CONSTANT, result);
log(GameController.class, "Attempts are now: " + session.getAttribute(ATTEMPTS_CONSTANT));
return "redirect:/index";
}
// BUTTONS
#PostMapping(value = "/save")
public String giveUpAndSaveScore(final HttpSession session,
final HttpServletRequest request,
#ModelAttribute("score") Score score) {
score.setGameScore((Integer) session.getAttribute(GAME_SCORE_CONSTANT));
score.setName((String) session.getAttribute(NAME_CONSTANT));
score.setSelectedLevelName((String) session.getAttribute(SELECTED_LEVEL_CONSTANT));
scoreSavingService.saveScore(score);
request.getSession().invalidate();
return "index";
}
#GetMapping(value = "/scores")
public String seeScores(final HttpServletRequest request, Model model) {
List<Score> scoreList = scoreSavingService.getScore(5, 1);
model.addAttribute("score", scoreList);
return "scores";
}
// CLOSE SESSION
#PostMapping(value = "/destroy")
public String restartGame(final HttpServletRequest request) {
log(GameController.class, " Session closing. Removing the data.");
request.getSession().invalidate();
return "redirect:/index";
}
// EXCEPTION HANDLERS
#ExceptionHandler(value = ArrayIndexOutOfBoundsException.class)
public String handleArrayIndexOutOfBoundsException(final Model model) {
String text = "ERROR: Could not check empty <<guess>>.";
model.addAttribute("text", text);
return "ExceptionPage";
}
#ExceptionHandler(value = NullPointerException.class)
public String handleNullPointerException(final Model model) {
String text = "ERROR: Cannot compare words because <<word to guess>> is null";
model.addAttribute("text", text);
return "ExceptionPage";
}
}
Thymeleaf template for index.html
<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-4.dtd">
<html lang="en" xmlns:th="www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>5Letters</title>
<link th:href="#{/bootstrap.min.css}" rel="stylesheet" />
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Quicksand&display=swap" rel="stylesheet">
</head>
<body>
<div class="container-sm">
<div class="col-xs-12" align="center">
<!-- HEADER -->
<img src="https://marisabel.nl/wp-content/uploads/2020/11/avatar-e1606738130828-150x150.png"
style="margin-top:2vh;">
<h1 style="margin-top:1vh; color:hotpink;"><b>5Letter Word Game</b></h1>
<h5>AKA: Lingo.</h5>
<hr>
</div>
<form method="get" th:action="#{/scores}">
<input type="submit" value="scores" class="btn btn-primary" />
</form>
<!-- Set Up Game Form -->
<div class="col-xs-12" align="center" th:hidden="${credits} != null">
<form th:action="#{/loadgame}" th:object="${gameDTO}" method="post">
<p>
<select class="form-group" th:field="*{lvlName}" id="dropDownList">
<option th:each="lvl : ${lvlName}" th:text="${lvl.lvlName}" th:value="${lvl.lvlName}">
</option>
</select>
</p>
<p><input type="text" class="form-group mx-sm-3 mb-2" id="name" th:field="*{playerName}"
th:placeholder="name" th:value="anonymous" /></p>
<p class="alert alert-danger" th:if="${#fields.hasErrors('playerName')}" th:errors="*{playerName}">
</p>
<p><input type="submit" value="start" class="btn btn-primary" /></p>
</form>
<p></p>
</div>
<!-- This whole session will load after name and level are chosen. Credits will be set to 3, triggering them to unhide -->
<div class="row">
<div class="col-xs-12 col-md-6" align="center" th:hidden="${credits} == null">
<!-- game information : always show after game setup -->
<h3 th:text="'Hello ' + ${name} + '!'"></h3>
<h3 th:text="'Level: '+ ${levelSelected}"></h3>
<h2 th:text="'Credits : '+ ${credits} + ' | Score: '+ ${gameScore}"></h2>
<h2 th:text="${attempt} + ' / ' + ${attemptStart}"></h2>
</div>
<div class="col-md-6" align="center">
<p>
<!-- Result messages and word after guessing -->
<h4 th:text="${message}"></h4>
<h2 id="result" th:text="${result}" th:hidden="${credits} == 0"></h2>
<!-- GUESS FORM -->
<form th:action="#{/guess}" th:object="${gameDTO}" method="post" th:hidden="${credits} == null">
<input id="guessField" type="text" th:field="*{guess}" placeholder="5letters" />
<p></p>
<p class="alert alert-danger" th:if="${#fields.hasErrors('guess')}" th:errors="*{guess}"></p>
<input type="submit" value="guess" th:disabled="${credits} == 0" class="btn btn-primary" />
</form>
</p>
</div>
</div>
<div class="row" style="margin-top:10vh;">
<div class="row justify-content-center" th:hidden="${credits} == null">
<div class="col col-lg-2 align-items-center">
<!-- Destroy session data and go to index -->
<form method="post" th:action="#{/destroy}">
<input type="submit" value="play again" class="btn btn-danger" />
</form>
<p></p>
</div>
<div class="col-md-auto align-items-center" width="50%">
<!-- Display last typed word -->
<h4 th:text="'Your guess was:'" th:hidden="${attempt} == ${attemptStart}"></h4>
<h2 id="guess" th:text="${guess}" th:hidden="${attempt} == ${attemptStart}"></h2>
<p></p>
</div>
<div class="col col-lg-2 align-items-center">
<!-- Stops the game if you are bored. Usually needed with EASY mode. -->
<form method="post" th:action="#{/save}">
<input type="submit" value="i'm tired" class="btn btn-danger" />
<p></p>
</form>
</div>
</div>
</div>
</body>
</html>
I moved the content I was hiding to its own page. It loads the page, but the content remains hidden. Even after taking a break I am still unable to find what is wrong. Specially when it works 100% without validation.

how to load data in table and bind the same to view Model class in .net core mvc (without refreshing the page)

I'm trying to add data in table via server but unable to succeed. I tried many things but nothing worked. I'm getting the data from the server but unable to populate that in table.. can someone please help me out?
I'm getting this :
"DataTables warning: table id=particlarsTable - Invalid JSON response. For more information about this error, please see http://datatables.net/tn/1"
Let me explain my code first:
(In View)
When I click on "Add New Particular" button, bootstrap modal will popup with input fields, those fields will send to the server then again back to view and populate the table with the same data without refreshing the whole page.
I'm stuck on the last stage, I'm getting the data on UI/View in ajax success handler but unable to load that data in table.
here is my view :
<form class="form-horizontal" method="post" id="createAdHocForm">
<div class="row">
<div class="col-md-3">
<div class="form-group">
<label>Select Flat</label>
<select asp-for="AdHocInvoice.FlatRid" asp-items="#(new SelectList(Model.Flats,"FlatRid","FlatNumber"))" class="form-control form-control-sm selectpicker" data-live-search="true">
<option selected disabled value="">Select One</option>
</select>
</div>
</div>
</div>
<table class="table table-sm table-responsive-md nowrap w-100" id="particlarsTable">
<thead class="thead-light">
<tr>
<th>Particulars</th>
<th>Amount</th>
<th></th>
</tr>
</thead>
<tbody class="bg-white">
#foreach (var item in Model.AdHocInvoice.FlatInvoiceItems)
{
<tr>
<td>#item.Particular</td>
<td>#item.Amount</td>
<td></td>
</tr>
}
</tbody>
</table>
<button class="btn btn-sm btn-outline-info" type="button" onclick="showParticularForm()">Add New Particular</button>
<hr />
<div class="row text-center">
<button class="btn btn-sm btn-outline-success mx-auto" type="submit">Submit</button>
</div>
</form>
bootstrap modal to fill the table :
<div class="modal fade" id="particularWindow">
<div class="modal-dialog modal-dialog-centered modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Invoice Items</h4>
<button type="button" class="close" data-dismiss="modal">×</button>
</div>
<div class="modal-body">
<form class="form-horizontal" method="post" id="particularForm">
<div class="row">
<div class="col-md-5">
<div class="form-group">
<label>Enter Particular</label>
<input id="particularName" name="particular" class="form-control form-control-sm" required />
</div>
</div>
<div class="col-md-5">
<label>Amount</label>
<input id="particularAmount" name="amount" class="form-control form-control-sm" required />
</div>
<div class="col-md-2">
<button class="btn btn-sm btn-outline-success mt-4" id="btnParticularSubmit" type="button" onclick="addParticular()">Add</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
Scripts:
<link rel="stylesheet" href="~/DataTables/datatables.min.css" />
<script type="text/javascript" src="~/DataTables/datatables.min.js"></script>
<script type="text/javascript">
$(document).ready(function () {
$('#particlarsTable').DataTable();
});
function showParticularForm() {
$('#particularWindow').modal('show');
}
function addParticular() {
var particular = $('#particularName').val();
var amount = $('#particularAmount').val();
$.ajax({
url: '/FlatInvoice/AddParticular',
data: 'particular=' + particular + '&amount=' + amount,
success: function (response) {
$('#particlarsTable').DataTable().ajax.reload()
}
});
}
</script>
Action Method on controller:
public JsonResult AddParticular(string particular, decimal amount)
{
_flatInvoiceViewModel.AdHocInvoice.FlatInvoiceItems.Add(new FlatInvoiceItem { Particular = particular, Amount = amount });
return Json(_flatInvoiceViewModel);
}
Class/Model:
public class FlatInvoiceItem
{
public Guid FlatInvoiceItemRid { get; set; }
public Guid FlatInvoiceRid { get; set; }
public Guid FundRuleRid { get; set; }
public string Particular { get; set; }
public decimal Amount { get; set; }
}
NOTE: this code is in initial phase, I need that input data on server for further process(will add code later) so don't want to use $.('#table_body_id').append("<tr>..</tr>"); type of code.
1.Firstly,reload() is used to send request back to the method which display the DataTable initially(e.g. name this method Test).
2.Secondly,From your AddParticular method,you just add a data to the list but the lifetime is just one request,so when you reload to Test method,the list still contains the initial data without new data.
Conclusion: I suggest that you could save data to database.And get data from database.
Here is a working demo about how to use DataTabale:
1.Model:
public class Test
{
public int Id { get; set; }
public AdHocInvoice AdHocInvoice { get; set; }
}
public class AdHocInvoice
{
public int Id { get; set; }
public string Name { get; set; }
public List<FlatInvoiceItem> FlatInvoiceItems { get; set; }
}
public class FlatInvoiceItem
{
public int Id { get; set; }
public Guid FlatInvoiceItemRid { get; set; }
public Guid FlatInvoiceRid { get; set; }
public Guid FundRuleRid { get; set; }
public string Particular { get; set; }
public decimal Amount { get; set; }
}
public class ViewModel
{
public string Particular { get; set; }
public decimal Amount { get; set; }
}
2.View:
<form class="form-horizontal" method="post" id="createAdHocForm">
<table class="table table-sm table-responsive-md nowrap w-100" id="particlarsTable">
<thead class="thead-light">
<tr>
<th>Particulars</th>
<th>Amount</th>
<th></th>
</tr>
</thead>
#*<tbody class="bg-white">
#foreach (var item in Model.AdHocInvoice.FlatInvoiceItems)
{
<tr>
<td id="particular">#item.Particular</td>
<td id="amount">#item.Amount</td>
<td></td>
</tr>
}
</tbody>*# //DataTable no need to add this tbody
</table>
<button class="btn btn-sm btn-outline-info" type="button" onclick="showParticularForm()">Add New Particular</button>
<hr />
<div class="row text-center">
<button class="btn btn-sm btn-outline-success mx-auto" type="submit">Submit</button>
</div>
</form>
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.20/css/jquery.dataTables.css">
<script type="text/javascript" charset="utf8" src="https://cdn.datatables.net/1.10.20/js/jquery.dataTables.js"></script>
<script>
$(document).ready(function () {
//$('#particlarsTable').DataTable();
$('#particlarsTable').DataTable({
ajax: {
type: 'GET',
dataType: 'JSON',
url: '#Url.Action("Test", "Home")',
},
columns: [
{ 'data': 'particular' },
{ 'data': 'amount' }
]
})
});
function showParticularForm() {
$('#particularWindow').modal('show');
}
function addParticular() {
var particular = $('#particularName').val();
var amount = $('#particularAmount').val();
$.ajax({
url: '/Home/AddParticular',
data: 'particular=' + particular + '&amount=' + amount
}).done(function (data) {
$('#particularWindow').modal('hide');
$('#particlarsTable').DataTable().ajax.reload();
});
}
</script>
3.Controller:
public class HomeController : Controller
{
private readonly MyDbContext _context;
public HomeController(MyDbContext context)
{
_context = context;
}
public IActionResult Index()
{
return View();
}
public JsonResult Test()
{
var _flatInvoiceViewModel = _context.Test.Include(i=>i.AdHocInvoice)
.ThenInclude(a=>a.FlatInvoiceItems).Where(i => i.Id == 1).FirstOrDefault();
var list = new List<ViewModel>();
foreach (var item in _flatInvoiceViewModel.AdHocInvoice.FlatInvoiceItems)
{
var model = new ViewModel() { Amount = item.Amount, Particular = item.Particular };
list.Add(model);
}
return Json(new { data = list });
}
public void AddParticular(string particular, decimal amount)
{
var _flatInvoiceViewModel = _context.Test.Include(i => i.AdHocInvoice)
.ThenInclude(a => a.FlatInvoiceItems).Where(i => i.Id == 1).FirstOrDefault();
_flatInvoiceViewModel.AdHocInvoice.FlatInvoiceItems.Add(new FlatInvoiceItem { Particular = particular, Amount = amount });
_context.SaveChanges();
}
4.Result:
Reference:
https://datatables.net/examples/data_sources/ajax.html
https://stackoverflow.com/a/59449895/11398810

What should I do to test the business layer using Mockito?

I have a problem using mockito in my spring boot project. What should I do to test the business layer? (I only have the builder test).
public class ProductBuilder {
private Product product;
private Collection<Product> products;
public static ProductBuilder mockProductBuilder() {
ProductBuilder builder = new ProductBuilder();
builder.product = new Product("Beer", "Alcholic", "20,99", "Montez");
return builder;
}
public static ProductBuilder mockCollectionProductBuilder() {
ProductBuilder builder = new ProductBuilder();
builder.products = new ArrayList<Product>();
for (int i = 0; i < 10; i++) {
Product product = new Product("Beer " + i, "Alcholic", "20,99" + i, "Montez");
builder.products.add(product);
}
return builder;
}
// Methods
public Product getProduct() {
return this.product;
}
public Collection<Product> getProducts() {
return this.products;
}
You need to have test for your classes controllers, repositories and services. Your builder is fine, your controller should like something like
#SpringBootTest
public class ProductControllerTest {
#InjectMocks
private ProductController productController;
#Mock
private ProductService productService;
#Mock
private BindingResult bindingResult;
private Model model;
private Product productOptional;
private List<Product> products;
private Product product;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
this.productOptional = ProductBuilder.mockProductBuilder().getProduct();
this.products = (List<Product>) ProductBuilder.mockCollectionProductBuilder().getProducts();
this.product = ProductBuilder.mockProductBuilder().getProduct();
this.model = new ConcurrentModel();
}
#Test
public void index() {
Mockito.when(this.productService.searchAll()).thenReturn(this.products);
Assert.assertEquals(this.productController.index(this.model), "product/index");
Assert.assertEquals(this.model.containsAttribute("products"), true);
}
#Test
public void create() {
Assert.assertEquals(this.productController.add(product, this.model), "product/addProduct");
}
#Test
public void update() {
Mockito.when(this.productService.searchById((long) 1)).thenReturn(this.productOptional);
Assert.assertEquals(this.productController.edit((long) 0, this.model), "product/editProduct");
Assert.assertEquals(this.model.containsAttribute("product"), false);
}
#Test
public void save() {
Mockito.when(this.productService.inserir(this.product)).thenReturn(this.product);
Mockito.when(this.bindingResult.hasErrors()).thenReturn(true);
Assert.assertEquals(this.productController.save(this.product, this.bindingResult, this.model), "product/addProduct");
}
#Test
public void saveError() {
Mockito.when(this.productService.inserir(this.product)).thenReturn(this.product);
Mockito.when(this.bindingResult.hasErrors()).thenReturn(true);
Assert.assertEquals(this.productController.save(this.product, this.bindingResult, this.model), "product/addProduct");
}
#Test
public void delete() {
Assert.assertEquals(this.productController.delete((long) 1, this.model), "product/index");
Mockito.verify(this.productService, Mockito.times(1)).deletar((long) 1);
}
}
For repositories
#RunWith(SpringRunner.class)
#SpringBootTest
public class ProductRepositoryTest {
#Autowired
private ProductRepository productRepository;
private Product product;
#Before
public void setUp() {
this.product = ProductBuilder.mockProductBuilder().getProduct();
}
#After
public void after() {
this.productRepository.deleteAll();
}
#Test
public void findAll() {
Assert.assertThat(this.productRepository.findAll(), instanceOf(List.class));
}
#Test
public void findById() {
Assert.assertThat(this.productRepository.findById((long) 2), instanceOf(Optional.class));
}
#Test
public void saveCreate() {
this.product.setId((long) 1);
this.product = this.productRepository.save(this.product);
//Assert.assertNotEquals(this.product.getId(), 1);
}
#Test
public void saveUpdate() {
this.product.setId((long) 0);
this.product = this.productRepository.save(this.product);
this.product.setNome("Jonas");
Product productAtualizado = this.productRepository.save(this.product);
Assert.assertEquals(productAtualizado.getNome(), this.product.getNome());
}
#Test
public void deleteById() {
this.product = this.productRepository.save(this.product);
this.productRepository.deleteById(this.product.getId());
Assert.assertEquals(this.productRepository.findAll().size(), 0);
}
For Services
#SpringBootTest
public class ProductServiceTest {
#InjectMocks
private ProductService productService;
#Mock
private ProductRepository productRepository;
private Product productTest;
private List<Product> products;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
this.productTest = ProductBuilder.mockProductBuilder().getProduct();
this.products = (List<Product>) ProductBuilder.mockCollectionProductBuilder().getProducts();
}
#Test
public void buscarTodosTest() {
Mockito.when(this.productRepository.findAll()).thenReturn(this.products);
List<Product> productsBd = this.productService.buscarTodos();
assertEquals(productsBd, this.products);
}
#Test
public void buscarPeloId() {
Optional<Product> productOptional = Optional.of(this.productTest);
Mockito.when(this.productRepository.findById(this.productTest.getId())).thenReturn(productOptional);
Product product = this.productService.buscarPeloId(this.productTest.getId());
assertEquals(product, productOptional.get());
}
#Test
public void inserirTest() {
Mockito.when(this.productRepository.save(this.productTest)).thenReturn(this.productTest);
Product productSalvo = this.productService.inserir(this.productTest);
assertEquals(productSalvo, this.productTest);
}
#Test
public void atualizarTest() {
Optional<Product> productOptional = Optional.of(this.productTest);
this.productTest.setId((long) 1);
Mockito.when(this.productRepository.save(this.productTest)).thenReturn(this.productTest);
Mockito.when(this.productRepository.findById(this.productTest.getId())).thenReturn(productOptional);
Product productAtualizado = this.productService.atualizar(this.productTest);
assertEquals(productAtualizado, this.productTest);
}
#Test
public void deletarTest() {
Optional<Product> productOptional = Optional.of(this.productTest);
Mockito.when(this.productRepository.findById(this.productTest.getId())).thenReturn(productOptional);
this.productService.deletar(this.productTest.getId());
Mockito.verify(this.productRepository, Mockito.times(1)).findById(this.productTest.getId());
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="MyMusic">
<meta name="author" content="Victoria Brandt">
<title>Users - MyMusic</title>
</head>
<body>
<nav>
<ul>
<li>User</li>
<li>Music</li>
<li>Playlist</li>
</ul>
</nav>
<div>
<div >
<div>
<strong><h2>Users</h2></strong>
</div>
</br>
<div >
<a th:href="#{/users/addUser}" >Add new user</a>
</div>
</br>
<div>
<div >
<table >
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Location</th>
</tr>
</thead>
<tbody>
<tr th:each="user : ${users}">
<td th:text="${user.id}"></td>
<td th:text="${user.name}"></td>
<td th:text="${user.location}"></td>
<td >
<div >
<a th:href="#{/users/editUser/{id}(id=${user.id})}" >Edit</a>
<a class="delete btn btn-sm btn-danger" th:href="#{/users/deleteUser/{id}(id=${user.id})}">Delete</a>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="MyMusic">
<meta name="author" content="Victoria Brandt">
<title>Users - MyMusic</title>
</head>
<body>
<nav>
<ul>
<li>User</li>
<li>Music</li>
<li>Playlist</li>
</ul>
</nav>
<div>
<div>
<div>
<strong><h2>Create new user</h2></strong>
</div>
</br>
<div>
<div>
<form th:object="${user}" th:action="#{/users/saveUser}"
method="POST">
<div>
<fieldset>
<div>
<div th:if="${#fields.hasAnyErrors()}">
<div th:each="detailedError : ${#fields.detailedErrors()}">
<span th:text="${detailedError.message}"></span>
</div>
</div>
</div>
<div>
<div>
<input type="text" id="id" th:field="*{id}" readOnly="readonly" />
</div>
</div>
<div>
<div th:classappend="${#fields.hasErrors('name')}? 'has-error'">
<label>Name</label> <input type="text" th:field="*{name}"
autofocus="autofocus" />
</div>
</div>
<div>
<div
th:classappend="${#fields.hasErrors('location')}? 'has-error'">
<label>Location</label> <input type="text"
th:field="*{location}" />
</div>
</div>
</fieldset>
</div>
<div >
<button type="submit" >Save</button>
<a th:href="#{/users}">Cancel</a>
</div>
</form>
</div>
</div>
</div>
</div>
</body>
</html>

Page in my project doesn't work (Tomcat Error)

My page doesn’t open. On this page I can create a user and give him a username and password + role (admin or user). Why does page doesn t work. You can look at the code, it seems to have written correctly. Maybe somewhere I made a mistake in the code.Tomcat gives error 404. I just need him to open this page, you can see the controller
Admin Controller
#Controller
#RequestMapping("/admin")
public class AdminController {
#Autowired
private StudentService studentService;
#GetMapping("/allStudentsAdmin")
public ModelAndView allStudentsForUser() {
ModelAndView mv = new ModelAndView();
List<Student> studentList = studentService.getAllStudents();
mv.addObject("studentList", studentList);
mv.setViewName("allStudentsAdmin");
return mv;
}
#GetMapping(value = "/deleteStudent/{id}")
public ModelAndView deleteUserById(#PathVariable Long id) {
studentService.deleteStudentById(id);
ModelAndView mv = new ModelAndView("redirect:/admin/allStudentsAdmin");
return mv;
}
#GetMapping(value = "/editStudent/{id}")
public ModelAndView displayEditUserForm(#PathVariable Long id) {
ModelAndView mv = new ModelAndView("adminEditStudent");
Student student = studentService.getStudentById(id);
mv.addObject("headerMessage", "Редактирование студента");
mv.addObject("student", student);
return mv;
}
#PostMapping(value = "/editStudent")
public String saveEditedUser(
#RequestParam("id") Long id,
#RequestParam("name") String name,
#RequestParam("surname") String surname,
#RequestParam("avatar") MultipartFile file) {
try {
studentService.updateStudent(name, surname, file, studentService.getStudentById(id));
} catch (FileSystemException ex) {
ex.printStackTrace();
} catch (IOException e) {
return "redirect:/errors";
}
return "redirect:/admin/allStudentsAdmin";
}
#GetMapping(value = "/addStudentAdmin")
public ModelAndView displayNewUserForm() {
ModelAndView mv = new ModelAndView("addStudentAdmin");
mv.addObject("headerMessage", "Add Student Details");
mv.addObject("student", new Student());
return mv;
}
#PostMapping(value = "/addStudentAdmin")
public String saveNewStudent(#RequestParam("name") #NonNull String name,
#RequestParam("surname") #NonNull String surname,
#RequestParam("avatar") MultipartFile file)
throws IOException {
Student student = new Student();
student.setSurname(surname);
student.setName(name);
if (file != null && !file.isEmpty()) {
student.setAvatar(studentService.saveAvatarImage(file).getName());
}
studentService.saveStudent(student);
return "redirect:/admin/allStudentsAdmin";
}
#GetMapping(value = "/addUser")
public ModelAndView displayAddUserForm() {
ModelAndView mv = new ModelAndView("addStudentAdmin");
mv.addObject("headerMessage", "Add Student Details");
mv.addObject("student", new Student());
return mv;
}
#PostMapping(value = "/addUser")
public String saveNewUser(#RequestParam("name") #NonNull String name,
#RequestParam("surname") #NonNull String surname,
#RequestParam("role") #NonNull String role)
throws IOException {
Student student = new Student();
student.setSurname(surname);
student.setName(name);
studentService.saveStudent(student);
return "redirect:/admin/allStudentsAdmin";
}
}
Admin Decorator
<body>
<div id="container">
<div id="header">
</div>
<div id="nav">
<ul>
<li><span>Главная</span></li>
<li class="dropdown"><span>Студенты</span>
<ul>
<li><span>Список студентов</span></li>
<sec:authorize access="hasRole('ADMIN') || hasRole('USER')">
<li><span>Добавить студента</span></li>
</sec:authorize>
<sec:authorize access="hasRole('ADMIN')">
<li><span>Добавить юзера</span></li>
</sec:authorize>
</ul>
</li>
<li><a><span>О нас </span></a></li>
<sec:authorize access="!isAuthenticated()">
<li><span>Выйти</span></li>
</sec:authorize>
</ul>
</div>
</div>
<sitemesh:write property='body'/>
<jsp:include page="/WEB-INF/template/admintemplate.jsp"/>
</body>
</html>
AddUser.JSP
<%#page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<%# taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.0/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.0/js/bootstrap.min.js"></script>
<title>Home</title>
</head>
<body>
<div class="add">
<br>
<br>
<br>
<br>
<center>
<h1>${headerMessage}</h1>
<form:form method="POST" action="${pageContext.request.contextPath}/admin/addUser" enctype="multipart/form-data">
<table>
<tr>
<td><label path="Name">Name</label></td>
<td><input type="text" name="name"/></td>
</tr>
<tr>
<td><label path="Surname">Surname</label></td>
<td><input type="text" name="surname"/></td>
</tr>
<tr>
<td><select name="select" size="3" multiple>
<option selected value="s1">Admin</option>
<option value="s2">User</option>
</select></td>
<td>
<input type="text" name="Role"/>
</td>
</tr>
<tr>
<td><input class="btn btn-primary" type="submit" value="Добавить"></td>
</tr>
</table>
</form:form>
</center>
</div>
</body>
</html>
Change URL to admin/addUser
because in your controller you have added a root level admin
#RequestMapping("/admin")
ex .http://localhost:8080/SchoolMaven/admin/addUser

spring form controller with html checkbox

I am trying to bind the input type checkbox using spring form controller,But i failed .
Here i am posting Controller,bean and jsp example,One more thing is i can't use
.
Below is the code:
Controller:
package com.test.web;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.SimpleFormController;
import com.vaannila.domain.User;
import com.vaannila.service.UserService;
#SuppressWarnings("deprecation")
public class UserController extends SimpleFormController {
private UserService userService;
public UserController() {
setCommandClass(User.class);
setCommandName("user");
}
public void setUserService(UserService userService) {
this.userService = userService;
}
#Override
protected ModelAndView onSubmit(Object command) throws Exception {
User user = (User) command;
user.setCommunity(user.getCommunity());
userService.add(user);
return new ModelAndView("userForm","user",user);
}
}
jsp:
<%# page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%# taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Registration Page</title>
<script>
function submitForm(){
document.testForm.submit();
}
</script>
</head>
<body>
<form:form method="POST" commandName="user" name="testForm" action="./userRegistration.htm">
<table>
<tr>
<td>User Name :</td>
<td><form:input path="name" /></td>
</tr>
<tr>
<td>Password :</td>
<td><form:password path="password" /></td>
</tr>
<tr>
<td>Gender :</td>
<td><form:radiobutton path="gender" value="M" label="M" />
<form:radiobutton path="gender" value="F" label="F" /></td>
</tr>
<tr>
<td>Country :</td>
<td><form:select path="country">
<form:option value="0" label="Select" />
<form:option value="1" label="India" />
<form:option value="2" label="USA" />
<form:option value="3" label="UK" />
</form:select></td>
</tr>
<tr>
<td>About you :</td>
<td><form:textarea path="aboutYou" /></td>
</tr>
<tr>
<td>Community :</td>
<td><input type="checkbox" name="community" value="Hibernate"/>Hibernate</br>
<input type="checkbox" name="community" value="test"/>test</br>
<input type="checkbox" name="community" value="test1"/>test1</br>
</td>
</tr>
<tr>
<td></td>
<td><form:checkbox path="mailingList"
label="Would you like to join our mailinglist?" /></td>
</tr>
<tr>
<td colspan="2"><input type="submit" onclick="submitForm();"></td>
</tr>
</table>
</form:form>
</body>
</html>
Java beans:
package com.test.domain;
public class User {
private String name;
private String password;
private String gender;
private String country;
private String aboutYou;
private String[] community;
private Boolean mailingList;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public String getAboutYou() {
return aboutYou;
}
public void setAboutYou(String aboutYou) {
this.aboutYou = aboutYou;
}
public String[] getCommunity() {
return community;
}
public void setCommunity(String[] community) {
this.community = community;
}
public Boolean getMailingList() {
return mailingList;
}
public void setMailingList(Boolean mailingList) {
this.mailingList = mailingList;
}
}
I tried different ways,but no luck.Any hints please.
the browser will not send the field in the request if the checkbox isn't checked. the value will either be "true" or not sent. you will never get a "false" value.
add a hidden field with _name for every checkbox
EX:
<input type="checkbox" name="community" value="Hibernate"/>
<input type="hidden" name="_community" value="on"/>
Then, spring will take care of it.
If you do not use the form tag it will not automaticly bind your checkboxes. If you use plain html you have to bind the your self.
You can solve this by adding a list of community objects and then use form:checkboxes.
For example:
<form:checkboxes path="communityList" items="${communityList}" itemValue="key" itemLabel="value" />
I would also recomend you to use a HashMap when using ModelAndView like this:
Map<String, Object> model = new HashMap<String, Object>();
model.put("user", user);
model.put("communityList", communityList);
return new ModelAndView("userFormat", model);
Manually bind using 'ServletRequestUtils'... http://static.springsource.org/spring/docs/2.5.x/api/org/springframework/web/bind/ServletRequestUtils.html
Example
public ModelAndView test(HttpServletRequest request, HttpServletResponse response) throws ServletRequestBindingException {
Long subscriptionOwnerId = ServletRequestUtils.getLongParameter(request, "id");
return new ModelAndView('test'); }`

Resources