Thymeleaf pagination only partially working - spring-boot

I tried to paginate my users CRUD, but it doesn't work. I can open users.html, it only shows 3 records, which is fine and if I use localhost:9001/users/page/1, 2 etc manually, it works fine. But the paginator under the table is an error screen.
My UserController:
#GetMapping("users/page/{pageNo}")
public String findPaginated(#PathVariable (value="pageNo") int pageNo, Model model) {
int pageSize = 3; //only 3 so I can easily test it
Page<User> page = userService.findPaginated(pageNo, pageSize);
List<User> listUsers = page.getContent();
model.addAttribute("currentPage", pageNo);
model.addAttribute("totalPages", page.getTotalPages());
model.addAttribute("totalItems", page.getTotalElements());
model.addAttribute("listUsers", listUsers);
return "/users";
}
**and**
#GetMapping("/users")
public String viewHomePage (Model model) {
//model.addAttribute("listUsers", userService.getAllUsers());
return findPaginated(1, model);
}
My UserService:
Page<User> findPaginated(int pageNo, int pageSize);
UserServiceImpl:
#Override
public Page<User> findPaginated(int PageNo, int PageSize) {
Pageable pageable = PageRequest.of(PageNo -1, PageSize);
return this.userRepository.findAll(pageable);
}
UserResository extends JpaRepository:
public interface UserRepository extends JpaRepository<User, Long> {
Pagintation in users.html:
<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)}">
valami
<a th:if="${currentPage != i}" th:href="#{'/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="#{'/page/' + ${currentPage + 1}}">Következő</a>
<span th:unless="${currentPage < totalPages}">Következő</span>
</div>
<div class="col-sm-1">
<a th:if="${currentPage < totalPages}" th:href="#{'/page/' + ${totalPages}}">Utolsó</a>
<span th:unless="${currentPage < totalPages}">Utolsó</span>
</div>
</div>
Even if I comment out this part of the html, the error is still displayed.
Errors:
An error happened during template parsing (template: "class path resource [templates//users.html]")
Could not parse as expression: "{$totalItems}" (template: "/users" - line 132, col 24)
Summary:
displaying only 3 records - works
using url's like /users/page/2 - manually works
displaying the numbers and pages under the table - does not work

Well, {$totalItems} should be ${totalItems} (which is causing your parser level error). This will continue to cause a parser error regadless of if it is commented out or not unless you use Thymeleaf's parser-level comment blocks which look like this:
<!--/* Total Rows: [[{$totalItems}]] */-->

Related

Umbraco Starter Kit sorting blog posts in wrong order

I tried to find the right answer first but had no luck.
I am working on a website using the Starter Kit of Umbraco in V7
For some reason, it sorts the blog posts using oldest first, while I want to get the newest posts first in the list view using Partial View Macro 'GetLatestBlogPosts'
Here is the code I'm currently using for the Partial View Macro:
#using ContentModels = Umbraco.Web.PublishedContentModels;
#using Umbraco.Web;
#inherits Umbraco.Web.Macros.PartialViewMacroPage
#{
var startNodeId = Model.MacroParameters["startNodeId"] != null
Model.MacroParameters["startNodeId"] : Model.Content.Id;
var numberOfPosts = 3;
if (Model.MacroParameters["numberOfPosts"] != null)
{
int.TryParse((string)Model.MacroParameters["numberOfPosts"], out
numberOfPosts);
}
}
#if (startNodeId != null)
{
#* Get the starting page *#
var startNode = Umbraco.TypedContent(startNodeId);
//Gets all blogposts to calculate pages
var blogposts = startNode.Children.OrderByDescending(x => x.GetPropertyValue("PublicationDate")).ToList();
var pageCount = (int)Math.Ceiling((double)blogposts.Count / (double)numberOfPosts);
var page = 1;
if (!string.IsNullOrEmpty(Request.QueryString["page"]))
{
int.TryParse(Request.QueryString["page"], out page);
if (page <= 0 || page > pageCount)
{
page = 1;
}
}
//Gets the blogposts for the current page
var pagedBlogposts = blogposts.Skip((page - 1) * numberOfPosts).Take(numberOfPosts).ToList();
if (pagedBlogposts.Count > 0)
{
<div class="blogposts">
#foreach (ContentModels.Blogpost post in pagedBlogposts)
{
<a href="#post.Url" class="blogpost">
<div class="blogpost-meta">
<small class="blogpost-date">#post.CreateDate.ToLongDateString()</small>
<small class="blogpost-cat">
#Html.Partial("~/Views/Partials/CategoryLinks.cshtml", post.Categories)
</small>
</div>
#if(post.FeaturedImage != null){
<div class="blogpost-image">
<img class="img-responsive" src="#post.FeaturedImage.Url?width=200" alt="#post.PageTitle" style="width: 100%; height: 100%;"/>
</div>
}
<h3 class="blogpost-title">#post.PageTitle</h3>
<div class="blogpost-excerpt">#post.Excerpt</div>
</a>
}
</div>
}
if (blogposts.Count > numberOfPosts)
{
<div class="pagination">
<nav class="nav-bar nav-bar--center">
#if (page <= 1)
{
<span class="nav-link nav-link--black nav-link--disabled">Prev</span>
}
else
{
<a class="nav-link nav-link--black" href="#(Model.Content.Url + "?page=" + (page - 1))">Prev</a>
}
#for (int i = 1; i <= pageCount; i++)
{
<a class="nav-link nav-link--black #(page == i ? "nav-link--active" : null)" href="#(Model.Content.Url + "?page=" + i)">#i</a>
}
#if (page == pageCount)
{
<span class="nav-link nav-link--black nav-link--disabled">Next</span>
}
else
{
<a class="nav-link nav-link--black" href="#(Model.Content.Url + "?page=" + (page + 1))">Next</a>
}
</nav>
</div>
}
}
I am completely stuck. Tried some things but all I try is going to break the site. Would really appreciate some help here to get this sorted in the desired way.
Thanks a lot for some guidance and assistance
As you figured out yourself, you are trying to sort by a non-existing property on your item.
I would recommend that you add a property on your blog posts called publishDate or something like that. You can then set this property on each of your posts to indicate when they are 'published'.
The reason why you would not want to just use the build in properties (like created date or published date) for something like this, is that if you need to update an old blog post (maybe correcting a typo) the publish date will now be updated when you republish. So simply correcting a typo will now make the old blog post appear as it being the latest blog post.
You should of course implement a fallback so if this property isn't set, the publish date will fallback to using the actual latest publish date from the item.

How to display existing and newly created values in EL in Spring MVC?

Only the last value of the model is displayed as EL, so the existing value is not displayed.
The BoardController code for that.
#RequestMapping("viewPage.do")
public String viewPage(HttpSession session,HttpServletRequest request, HttpServletResponse response,Model model,
#RequestParam(value="board_idx", defaultValue="0") int board_idx) throws IOException,IllegalStateException
{
// View comment list for this post
List<HashMap<String, Object>> commentList= bService.getBoardForComment(boardData.getBoard_idx());
// loop (comment.size)
for(int i = 0; i < commentList.size(); i++)
{
System.out.println("commentList Size : " + commentList.size());
//Saving the board_comm_idx value in the commentList to a variable
int board_comm_idx= (Integer) commentList.get(i).get("board_comm_idx");
//System.out.println("board_comm_idx : " + board_comm_idx);
// View comments in comments (viewed by board_idx, board_comm_idx)
List<HashMap<String, Object>> getCommentOfCommentList =
bService.getBoardCommentOfComment(board_idx, board_comm_idx);
model.addAttribute("cocList", getCommentOfCommentList);
System.out.println("GetCommentOfCommentList : " + getCommentOfCommentList);
System.out.println("cocList (Model) : " + model);
}
model.addAttribute("commentList", commentList); // CommentList Information
return "viewPage";
}
Here is the JSP code.
<c:forEach items="${cocList}" var="cocList">
<div class="commentList" style="margin-left:70px">
<div class="what">
<div class="nickname">
<span>
${cocList.board_c_of_c_writer}
</span>
</div>
<div class="writedatezone" style="margin-left: 715px;">
<span class="era">
<span class="glyphicon glyphicon-time enterReReply"></span>
<span class="commentWriteDate">
<fmt:formatDate value="${cocList.board_c_of_c_writeDate}" pattern="yyy-MM-dd"/>
</span>
</span>
</div>
<c:if test="${cocList.idx == idx}">
<div class="duzone">
<span class="deleteOrUpdateComment">
<a class="updateComment">수정</a>
<a class="deleteComment" onclick="deleteCoc(${cocList.board_idx},${cocList.board_c_of_c_idx})">삭제</a>
<input type="hidden" value="${cocList.board_comm_idx}">
<input type="hidden" value="${cocList.board_c_of_c}">
<input type="hidden" name="board_idx" value="${boardInformation.board_idx}">
</span>
</div>
</c:if>
</div>
<pre>${cocList.board_c_of_c_content}</pre>
</div>
</c:forEach>
First, it is the log of when you first commented on the comment.
GetCommentOfCommentList : [{board_idx=3, board_c_of_c_writer=웹개발자, board_c_of_c_content=Test, board_c_of_c_idx=8, board_comm_idx=5, idx=4, board_c_of_c_writeDate=2017-11-30 10:33:06.0}]
cocList (Model) : {cocList=[{board_idx=3, board_c_of_c_writer=웹개발자, board_c_of_c_content=Test, board_c_of_c_idx=8, board_comm_idx=5, idx=4, board_c_of_c_writeDate=2017-11-30 10:33:06.0}]}
After that, if you make a new comment,
GetCommentOfCommentList : [{board_idx=3, board_c_of_c_writer=웹개발자, board_c_of_c_content=Test, board_c_of_c_idx=8, board_comm_idx=5, idx=4, board_c_of_c_writeDate=2017-11-30 10:33:06.0}]
cocList (Model) : {cocList=[{board_idx=3, board_c_of_c_writer=웹개발자, board_c_of_c_content=Test, board_c_of_c_idx=8, board_comm_idx=5, idx=4, board_c_of_c_writeDate=2017-11-30 10:33:06.0}]}
GetCommentOfCommentList : []
cocList (Model) : {cocList=[]}
As above, when you create a new comment, the comment list will disappear from the existing comment.
The last cocList is an empty string because you have not commented on the comment.
I am convinced that EL is wrong. How do I fix the EL to solve my problem?
Even though you have the
<c:forEach items="${cocList}" var="cocList">
cocList is a reference your list (model attribute), but since you are assigning it to something else var="cocList" you are going to have unintended consequences, you should name your list item to something else.
<c:forEach items="${cocList}" var="item">
You should then be able to reference the item like ${item.blah}
Think of
<c:forEach items="${cocList}" var="cocList">
as
for (Object cocList : cocList) {
...
}
Which obviously is bad, instead you want:
<c:forEach items="${cocList}" var="item">
which is
for (Object item : cocList) {
...
}
I believe that should fix your problem.
For what it is worth as well, most likely, you are not using EL or SpEL, you are using JSTL at the moment. Normally the SpEL tags are going to look like spring:..., c:... normally references JSTL.
I solved this problem.
I added getCommentofCommentList to commentList instead of modifying the query statement.
As follows.
for(int i = 0; i < commentList.size(); i++)
{
System.out.println("commentList Size : " + commentList.size());
// commentList 에 담긴 board_comm_idx 값을 변수에 저장
int board_comm_idx= (Integer) commentList.get(i).get("board_comm_idx");
//System.out.println("board_comm_idx : " + board_comm_idx);
// 댓글에 댓글 조회 (board_idx , board_comm_idx으로 조회)
List<HashMap<String, Object>> getCommentOfCommentList =
bService.getBoardCommentOfComment(board_idx, board_comm_idx);
commentList.get(i).put("cocList", getCommentOfCommentList);
}
I added the following code.
commentList.get(i).put("cocList", getCommentOfCommentList)
as the result, It's very well work.

MVC Pass Paramater from ActionLink to View to return Recordset

I would like to pass a value from a link to a View and display the fields of the single record on the page.
Lets say I have #Html.ActionLink(item.Title, "Article/"+#item.ID, "News") which outputs /News/Article/1, I would need a Controller called NewsController with a /News/Article View.
I already have the following:
NewsController:
namespace WebApplication1.Controllers
{
public class NewsController : Controller
{
private WebApplication1Entities db = new WebApplication1Entities();
public ActionResult Article()
{
var articleModel = (from m in db.News where (m.Id == 1) && (m.Active == true) select m);
return View(articleModel);
}
[ChildActionOnly]
public ActionResult LatestNews()
{
var latestModel = (from m in db.News where (m.Id != 1) && (m.Active == true) select m);
return View(latestModel);
}
}
Not sure if I should use FirstOrDefault() as it can only be one record as the Id is unique, but unsure how to reference item objects inside the View without an IEnumerable list. At present the Id is set to 1 but I would like the recordset to reflect the Id passed.
Not sure what code to put inside the View, Article though, here is what I have so far:
Article.cshtml
#model IEnumerable<WebApplication1.Models.News>
#foreach (var item in Model) {
ViewBag.Title = #item.Title;
<div class="row">
<article class="span9 maxheight">
<section class="block-indent-1 divider-bot-2">
<h2>#item.Title</h2>
#item.Summary
#item.Content
</section>
</article>
<article class="span3 divider-left maxheight">
<section class="block-indent-1">
<h2>Latest News</h2>
#{ Html.RenderAction("LatestNews", "News"); }
</section>
</article>
</div>
}
LatestNews.cshtml
#{ Layout = null; }
#model IEnumerable<Shedtember.Models.News>
<ul class="nav sf-menu clearfix">
#foreach (var item in Model)
{
#Html.MenuLink(item.Title, "Article/"#item.ID, "News")
}
</ul>
This works for Id 1 but this needs to be dynamic.
Any help would be much appreciated :-)
In your RouteConfig class map a route as follows:
routes.MapRoute("Article", "Article/{id}", new {controller = "News", action = "Article"});
then in your Article method you can add and use the id parameter as follows:
public ActionResult Article(int id)
{
var articleModel = (from m in db.News where (m.Id == id) && (m.Active == true) select m);
return View(articleModel);
}

PagedList in asp.net mvc 3 not returning anything to any page after 1

I am using paged list in a search actionresult to page my results. It returns events within a date range or category.The first page returned is fine, but the second never has any results. There are over 7 results that the query returns when I check a breakpoint. Here is my search controller;
public ActionResult Search(DateTime? q, DateTime? e, int? EventCategoryId, Event model)
{
var SearchEvents = db.Events
.OrderByDescending(r => r.start)
.Where(r =>
r.start >= q &&
r.end <= e &&
r.EventCategoryId == EventCategoryId ||
r.start >= q ||
r.EventCategoryId == EventCategoryId);
ViewBag.QValue = model.start;
ViewBag.EValue = model.end;
ViewBag.ListValue = model.EventCategoryId;
ViewBag.EventCategoryId = new SelectList(db.EventsCategories, "Id", "Name", ViewBag.ListValue);
var pageIndex = model.Page ?? 1;
var results = SearchEvents.ToPagedList(pageIndex, 4);
ViewBag.Names = results;
return View(results);
}
}
And here is my view;
#model IPagedList<RealKaac.Models.Event>
#{
ViewBag.Title = "SearchResults";
}
#using PagedList;
#using PagedList.Mvc;
#using System.Linq;
<div id="Content">
<h2>SearchResults</h2>
<div id="EventSearch">
#using (Html.BeginForm("Search", "Event"))
{
<p class="EventPar">Start Date: </p>
<input id ="datepicker" type ="text" name ="q" value="#ViewBag.QValue" />
<p class="EventPar">End Date: </p>
<input id ="datepickerend" type ="text" name ="e" value ="#ViewBag.EValue" />
<p class="EventPar">Category:</p>
#Html.DropDownList("EventCategoryId")
<input id="EventButton" style="padding:1px;" type ="submit" name ="Search" value = "Search" />
}
</div>
<div id="IndexEvents">
#foreach (var item in Model)
{
<div class ="event">
<div class="eventname">
#*<p>#Html.DisplayFor(modelItem => item.Name)</p> *#
</div>
<div class = "etitle">
<p>#Html.DisplayFor(modelItem => item.title)</p>
</div>
<div class="eventsdesc">
<p>#Html.DisplayFor(modelItem => item.EventDescription)</p>
</div>
<ul class ="datelink">
<li style="margin-right:120px;">This event starts on the
#Html.DisplayFor(modelItem => item.start) and ends on the
#Html.DisplayFor(modelItem => item.end)</li>
<li>Click #Html.DisplayFor(modelItem => item.EventWebsite) for more info.</li>
</ul>
</div>
}
</div>
</div>
#Html.PagedListPager((IPagedList)ViewBag.Names, page => Url.Action("Search", new { page }))
I'm fairly new at this so I'm probably missing something small, any advice is greatly appreciated.
Thanks.
The Url.Action has only one parameter to it (the page #), which doesn't match the controller's method. It isn't providing access to any of the other parameters (such as the start/end dates). So, the second "page" query for the next 4 Events likely isn't working. If you created a SearchModel that had the necessary properties on it, you should easily be able to send the values from one page to the next.
public ActionResult Search(SearchModel search) {
//... (your search code)
// ... then, before returning the View,
ViewBag.SearchParameters = search;
return View(results);
where SearchModel has properties like:
public class SearchModel {
public DateTime? StartDate { get; set; }
public DateTime? EndDate { get; set; }
public int Page? { get; set; }
// etc.
}
Then, you could:
#Html.PagedListPager((IPagedList)ViewBag.Names, page => Url.Action("Search", new SearchModel { search = ViewBag.SearchParameters }))
Or you could just use named parameters:
#Html.PagedListPager((IPagedList)ViewBag.Names, page => Url.Action("Search", new { page = page, q = ViewBag.QValue, /* ... etc. */ }))

How to handle paging requirement in ASP.NET MVC Razor View Engine

I have a controller action which passes a list of products to the view. There can be zero, single or any number of products in the list. What I want is that if the products are more than 10, pagination should occur.
I have no idea whatsover as how to do this thing. Currently i am just passing the list to view in the controller like following::
public ActionResult Catalog(string id)
{
Category catalog = pe.Categories.Where(cat => cat.CategoryName == id).Single();
return View(catalog);
}
And then running the foreach in the view ::
#foreach (var cat in Model.Products)
{
.
.
}
I have removed the code between the foreach for brevity.
Please tell me how to handle the pagination for such scenarios. Earlier i used server controls in regular asp.net so didn't required to worry about the HTML and such matters. MVC and Razor are pretty new for me and i am trying to get accustomed.
You may take a look at the following article. Or using MvcContrib Grid. Or even doing it manually.
There is a nuget package called PagedList that makes this dead simple.
http://nuget.org/List/Packages/PagedList
Since https://github.com/TroyGoode/PagedList is no longer maintained I suggest that you try https://github.com/kpi-ua/X.PagedList which is a fork of that same project but with the main difference being that X.PagedList is portable assembly. It means, that you can use it not only in Web projects, but also in Winforms, Window Phone, Silverlight and etc. projects."
To install it using Nuget Package Manager Console run this command
PM> Install-Package X.PagedList.Mvc
IMPORTANT: Running the previous command will try to install JQuery 2.0 and other dependencies build by microsoft
And follow the steps mentioned on
https://github.com/kpi-ua/X.PagedList
/Controllers/ProductController.cs
public class ProductController : Controller
{
public object Index(int? page)
{
var products = MyProductDataSource.FindAllProducts(); //returns IQueryable<Product> representing an unknown number of products. a thousand maybe?
var pageNumber = page ?? 1; // if no page was specified in the querystring, default to the first page (1)
var onePageOfProducts = products.ToPagedList(pageNumber, 25); // will only contain 25 products max because of the pageSize
ViewBag.OnePageOfProducts = onePageOfProducts;
return View();
}
}
/Views/Products/Index.cshtml
#{
ViewBag.Title = "Product Listing"
}
#using PagedList.Mvc; //import this so we get our HTML Helper
#using PagedList; //import this so we can cast our list to IPagedList (only necessary because ViewBag is dynamic)
<!-- import the included stylesheet for some (very basic) default styling -->
<link href="/Content/PagedList.css" rel="stylesheet" type="text/css" />
<!-- loop through each of your products and display it however you want. we're just printing the name here -->
<h2>List of Products</h2>
<ul>
#foreach(var product in ViewBag.OnePageOfProducts){
<li>#product.Name</li>
}
</ul>
<!-- output a paging control that lets the user navigation to the previous page, next page, etc -->
#Html.PagedListPager( (IPagedList)ViewBag.OnePageOfProducts, page => Url.Action("Index", new { page }) )
Other still working links include:
http://www.asp.net/mvc/overview/getting-started/getting-started-with-ef-using-mvc/sorting-filtering-and-paging-with-the-entity-framework-in-an-asp-net-mvc-application
http://blogs.taiga.nl/martijn/2008/08/27/paging-with-aspnet-mvc/
To show the title ---> Topaging + DisplayNameFor
just add bellow code for each column in View
<th>
#Html.DisplayNameFor(model=>model.First().LastName)
If you want to create paging on Razor view, here is the working code for that.
<nav aria-label="Page navigation example">
#{
int currentPage = ViewBag.Page;
int staticRange = 11;
int NoOfPage = 50;
int Range = staticRange;
int Start = 1;
int End = staticRange;
int Reminder = 0;
int Divdent = 0;
if (currentPage >= (Range - 1))
{
Reminder = currentPage % 10;
Divdent = currentPage / 10;
Start = (Divdent * staticRange) - Divdent;
Range = Start + staticRange;
End = Range;
}
else
{
Range = staticRange;
Start = 1;
}
if (Range >= NoOfPage)
{
Range = NoOfPage;
End = Range;
}
if (currentPage == NoOfPage)
{
Start = NoOfPage - staticRange +1;
}
<div>
currentPage: #currentPage<br />
NoOfPage:#NoOfPage <br />
Range: #Range<br />
Start:#Start<br />
End:#End<br />
Reminder:#Reminder<br />
Divdent:#Divdent
</div>
<ul class="pagination">
#{
if (currentPage == 1)
{
<li class="page-item"><a class="page-link disabled" href="javascript:void(0)">Previous</a></li>
}
else
{
<li class="page-item"><a class="page-link" href="#Url.Action("About","Home",new {page=(currentPage>0)?currentPage-1:0 })">Previous</a></li>
}
}
#{
if (Range > staticRange)
{
<li class="page-item"><a class="page-link" href="#Url.Action("About","Home",new {page=Start-1 })">...</a></li>
}
for (int i = Start; i < End; i++)
{
<li class="page-item #(i==currentPage?"active":"")"><a class="page-link" href="#Url.Action("About","Home",new {page=i })">#i</a></li>
if ((i == (Range - 1)) && Range < NoOfPage)
{
<li class="page-item"><a class="page-link" href="#Url.Action("About","Home",new {page=End })">...</a></li>
}
}
<li class="page-item #(NoOfPage==currentPage?"active":"")"><a class="page-link" href="#Url.Action("About","Home",new {page=NoOfPage })">#NoOfPage</a></li>
}
#{
if (currentPage < NoOfPage)
{
<li class="page-item"><a class="page-link" href="#Url.Action("About","Home",new {page=currentPage+1 })">Next</a></li>
}
else
{
<li class="page-item"><a class="page-link disabled" href="javascript:void(0)">Next</a></li>
}
}
</ul>
}
</nav>

Resources