How to have pagination in spring boot JPA starting with 1 not from 0 - spring-boot

I'm trying to have pagination starting from page 1 and not from 0. I have tried having one-indexed-parameters but with that number in response still comes as 0 even though in request I can pass page=1 and get page 0 data. How can I achieve that?
one-indexed-parameters: true

This is the frontend code in thymeleaf
<div th:if="${totalPages > 1}">
<div class="row col-sm-10">
<div class="col-sm-2">
Total Rows: [[${totalItems}]]
</div>
<div class="col-sm-1">
<span th:each="i: ${#numbers.sequence(1, totalPages)}">
<a th:if="${currentPage != i}"
th:href="#{'/home/page/' + ${i}}">[[${i}]]</a>
<span th:unless="${currentPage != i}">[[${i}]]</span>
</span>
</div>
<div class="col-sm-1">
<a th:if="${currentPage < totalPages}"
th:href="#{'/home/page/' + ${currentPage + 1}+ }">Next</a>
<span th:unless="${currentPage < totalPages}">Next</span>
</div>
<div class="col-sm-1">
<a th:if="${currentPage < totalPages}"
th:href="#{'/home/page/' + ${totalPages}}">Last</a>
<span th:unless="${currentPage < totalPages}">Last</span>
</div>
</div>
</div>
This is the controller code
#Controller
#RequestMapping("/home")
public class HomeController {
#GetMapping("/")
public String showAllBlogs(Model theModel){
return findPaginated(1,theModel);
}
#GetMapping("/page/{pageNo}")
public String findPaginated(#PathVariable(value = "pageNo") int pageNo,
Model model) {
int pageSize = 3;
Page<Post> page = blogService.findPaginated(pageNo, pageSize);
List<Post> listPosts = page.getContent();
model.addAttribute("currentPage", pageNo);
model.addAttribute("totalPages", page.getTotalPages());
model.addAttribute("totalItems", page.getTotalElements());
}
return "blogs";
}
This is the code for service
#Override
public Page<Post> findPaginated(int pageNo, int pageSize) {
Pageable pageable = PageRequest.of(pageNo - 1, pageSize);
return blogRepository.findAll(pageable);
}
I wish this code will help you to resolve the issue.

Related

Splice doesn't delete any item from array - Angular12

I am trying create a function for deleting specific items from a shopping cart.
It doesn't give any errors, but when I click the button nothing happens. Any idea what might be the problem?
See the codes below about my issue.
service.ts
removeCartItem(product: Product){
this.cartItemList.map((a:any, index:any)=>{
if(product.id=== a.id){
this.cartItemList.splice(index,1);
}
})
cartitem.component.html
<div class="cartitem">
<div class="container">
<div class="row">
<div class="col">
{{ cartItem.name }}
</div>
<div class="col">
<img [src]="cartItem.imageUrl" class="card-img-top" alt="..." />
</div>
<div class="col-5">
{{ cartItem.description }}
</div>
<div class="col">{{ cartItem.price | currency: "EUR" }}</div>
<div class="col">{{ cartItem.qty }}</div>
<div>
<button (click)="removeItem(item)" class="btn btn-primary">
Remove from cart
</button>
</div>
</div>
</div>
</div>
cartitem.component.ts
constructor(public service: MessengerService) { }
ngOnInit(): void {
}
removeItem(item: Product){
this.service.removeCartItem(item);
}
cart.component.ts
cartItems: Product[] = [];
cartTotal = 0;
product: any;
constructor(private msg: MessengerService, public dialog: MatDialog) {}
ngOnInit() {
this.msg.getMsg().subscribe((product: Product) => {
this.addProductToCart(product);
});
}
addProductToCart(product: Product) {
let productExists = false;
for (let i in this.cartItems) {
if (this.cartItems[i].id === product.id) {
this.cartItems[i].qty++;
productExists = true;
break;
}
}
if (!productExists) {
this.cartItems.push({
id: product.id,
name: product.name,
description: product.description,
qty: 1,
price: product.price,
imageUrl: product.imageUrl,
purchased:product.purchased
});
}
this.cartItems.forEach((item) => {
this.cartTotal += item.qty * item.price;
});
}
cart.component.html
<ul *ngIf="cartItems.length > 0" class="list-group">
<li class="list-group-item">
<h3>My Cart</h3>
</li>
<li class="list-group-item" *ngFor="let item of cartItems">
<app-cartitem [cartItem]="item"></app-cartitem>
</li>
<li class="list-group-item">
<span>Total: {{ cartTotal | currency: "EUR" }} </span>
</li>
<li class="list-group-item">
<button
id="btnFinalize"
class="btn btn-primary"
(click)="purchaseDisabled(product)"
>
Finalize purchase
</button>
</li>
</ul>
The problem is that you are trying to splice inside map. Changing to the following will work:
removeCartItem(product: Product){
let indexToRemove: number = -1;
this.cartItemList.map((a:any, index:any)=>{
if(product.id === a.id){
indexToRemove = index;
}
return a;
});
if(indexToRemove !== -1){
this.cartItemList.splice(indexToRemove,1);
}
}
But notice there is no need to use map here. It's a waste of time and memory to copy the entire array again. Just a loop through the array to find the index to remove would be enough:
removeCartItem(product: Product){
let indexToRemove: number = -1;
let index: number = 0;
for(const cardItem of this.cartItemList){
if(product.id === cardItem.id){
indexToRemove = index;
}
index++;
}
if(indexToRemove !== -1){
this.cartItemList.splice(indexToRemove,1);
}
}

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.

Passing form data from View Component to Controller in .NET Core MVC

I have a Component View and I try to update data from a form in it, I call the controller but in the controller I receive null :
public class CustomerPropertyViewComponent: ViewComponent
{
private MyDBContext context;
public CustomerPropertyViewComponent(MyDBContext _contex)
{
context = _contex;
}
public async Task<IViewComponentResult> InvokeAsync(int id)
{
CustomerPropertyModelView c = new CustomerPropertyModelView();
TblCustomerProperty t = new TblCustomerProperty(context);
c = t.getAllInfo(id);
if (c.model == null)
{
c.model = new TblCustomerProperty();
c.model.CustomerId = id;
}
return View(c);
}
}
and in the view I have
#model SunSystemDotNetCoreVersion.Models.helpers.CustomerPropertyModelView
<form asp-action="Update" asp-controller="CustomerProperties"
data-ajax="true"
data-ajax-method="POST"
method="post">
<div class="row w-100">
<div class="col-6">
<div class="row align-items-center h-100">
<div class="col-5 text-right">
Number of Bedrooms
</div>
<div class="col-7 p-1 p-1">
#Html.TextBoxFor(model => model.model.Bedrooms, new { #class = "form-control", Type = "number" })
</div>
<div class="col-5 text-right">
Current Heating System
</div>
<div class="col-7 p-1">
<select asp-for="model.HeatingSystemTypeId" class="form-control"
asp-items="#(new SelectList(Model.HeatingsList,"HeatingSystemTypeId","Title"))">
<option value="0">-Plaese Select-</option>
</select>
</div>
.....
<div class="col-12">
<button type="submit" >Save</button>
</div>
</div>
</form>
I have reduced most of the view code but it contains all data that the model needs. and this is my controller:
public class CustomerPropertiesController : Controller
{
private readonly MyDBContext_context;
public CustomerPropertiesController(MyDBContextcontext)
{
_context = context;
}
public IActionResult Update(TblCustomerProperty modelView) {
//here modelView is null
return View();
}
it should be work I don't know why it keeps sending null to my controller.
You could F12 to check the html elements in browser,and you will find the name of these elements are like:model.Bedrooms.Because your main model is CustomerPropertyModelView but your input belongs to TblCustomerProperty which named model in CustomerPropertyModelView.If your backend code recieve CustomerPropertyModelView as parameter,it will not be null.But if you recieve TblCustomerProperty as parameter,you need specify the suffix.
Change like below:
public IActionResult Update([Bind(Prefix ="model")]TblCustomerProperty modelView)
{
return View();
}

Load dynamic image from external url in thymleaf img tag[SringBoot]

I have a Dynamic external url that i am setting in model at controller side and trying to access in html using thymleaf tag
Here is the code:
Controller:
#RequestMapping(value = "/{id:.+}", method = RequestMethod.GET)
public String searchUser(#PathVariable("id") String data, Model model) {
/*
* if (!data.startsWith("#")) { data = "#" + data; }
*/
List<User> list = userService.searchUserUsingText(data);
if (list.isEmpty()) {
System.out.println("error");
return "404";
} else {
User user = list.get(0);
if (null != user.getName())
model.addAttribute("name", user.getName());
if (null != user.getProfession())
model.addAttribute("profession", user.getProfession());
if (null != user.getPhotoUrl()) {
System.out.println("inside image" + user.getPhotoUrl());
model.addAttribute("image" + user.getPhotoUrl());
}
return "profile";
}
}
PhotoUrl will be a external url like:
https://firebasestorage.googleapis.com/v0/b/mysocialhandle-ecfc0.appspot.com/o/images%2FFtX4VVciacM1jKdrP2NfInSyWMf1%2FFtX4VVciacM1jKdrP2NfInSyWMf1.jpg?alt=media&token=10e06c9b-044e-4e15-ab74-a55429bcb22b
Thymleaf/Html side:
<div class="container">
<div class="owner">
<div class="avatar">
<img th:src="#{${image}}" alt="Circle Image"
class="img-circle img-no-padding img-responsive">
</div>
<div class="name">
<h4 class="title" th:text="${name}">
<br />
</h4>
<h6 class="description" th:text="${profession}"></h6>
</div>
</div>
name and profession are resolved perfectly but at img Tag i am getting null.
Guys please help in this...
You have
model.addAttribute("image" + user.getPhotoUrl());
I think you meant to have
model.addAttribute("image", user.getPhotoUrl());

Spring + Thymeleaf can't pass the parameter correctly

I can't pass the parameter correctly. I know the error was in the HTML.
The error was in the code ${vo.news.likeCount}, ${vo.news.link}, and so on.
But I don't know how to use it in the correct way. Thank you.
This is a thymeleaf template:
<div class="posts">
<div th:each="vo : ${vos}">
<div class="post">
<div class="votebar">
<button class="click-like up" aria-pressed="false" title="赞同"><i class="vote-arrow"></i>
<span class="count"><span th:text="${vo.news.likeCount}"></span></span>
</button>
<button class="click-dislike down" aria-pressed="true" title="反对"><i class="vote-arrow"></i>
</button>
</div>
<div class="content" data-url="${vo.news.link}">
<div>
<img class="content-img" src="${vo.news.image}" alt="">
</div>
</div>
</div>
</div>
</div>
This is class ViewObject:
public class ViewObject {
private Map<String, Object> obj = new HashMap<String, Object>();
public void set(String key, Object value) {
obj.put(key, value);
}
public Object get(String key) {
return obj.get(key);
}
}
This is the controller method:
#RequestMapping(path = {"/", "/index"}, method = {RequestMethod.GET, RequestMethod.POST})
public String index(Model model) {
List<News> newsList = newsService.getLatesNews(0, 0, 10);
List<ViewObject> vos = new ArrayList<>();
for (News news : newsList) {
ViewObject vo = new ViewObject();
vo.set("news", news);
vo.set("user", userService.getUser(news.getUserId()));
vos.add(vo);
}
model.addAttribute("vos", vos);
return "home.html";
}
The expression vo.news.likeCount means vo.getNews().getLikeCount(). ViewObject doesn't have method getNews(). You need to structure your expressions to look the same way they would in java.
<span th:text="${vo.get('news').likeCount}"></span>
<img class="content-img" src="${vo.get('news').image}" alt="">
<div class="content" data-url="${vo.get('news').link}">
etc...

Resources