Spring boot and thymeleaf pagination and sorting - spring

I am studying spring boot and making pet project and now I need do pagination and sorting. I am using thymeleaf in html page and I have some problem with Pageable and Sort field.
I have controller
#GetMapping
public String showAllClassrooms(Model model, Pageable page) {
Page<Classroom> classroomPage = classroomService.getAndSort(page);
model.addAttribute("classroomPage", classroomPage);
return "classrooms-page";
}
and Service method
#Transactional
public Page<Classroom> getAndSort(Pageable paging) {
long totalElement = count();
List<Classroom> classrooms = new ArrayList<>();
if (paging.getSort().toString().contains("Address")) {
classrooms = getAndSortByAddress(paging.getPageSize(), (int) paging.getOffset());
} else {
classrooms = getAndSortByCapacity(paging.getPageSize(), (int) paging.getOffset());
}
return new PageImpl<Classroom>(classrooms, paging, totalElement);
}
and naviagion in page
<nav aria-label="..." th:object="${classroomPage}">
<ul class="pagination justify-content-center">
<li class="page-item" th:classappend="*{(getNumber == 0 ? 'disabled' : '' )}">
<a class="page-link" th:href="#{/classrooms/(page=*{getNumber-1}, size = *{size}, sort=*{sort})}">«</a></li>
<li class="page-item" th:classappend="*{(getNumber == getNumber ? 'active' : '' )}">
<a class="page-link" th:text="*{getNumber} + 1" th:href="#{/classrooms/(page=*{getNumber}, sort=*{sort})}" ></a></li>
<li class="page-item" th:classappend="(*{getNumber} == *{getTotalPages}-1 ? 'disabled' : '' )">
<a class="page-link" th:href="#{/classrooms/(page=*{getNumber+1} , size = *{size} , sort=*{sort})}">»</a>
</li>
<li class="page-item">
<form action="#" method="get" th:action="#{/classrooms/}"
>
<div class="input-group">
<input class="w-25 p-1" type="text" name="page" th:value="${classroomPage.getNumber+1}">
<input type="hidden" name="size" th:value="${classroomPage.size}">
<input type="hidden" name="sort" th:value="${classroomPage.getSort}">
<span class="input-group-text" th:utext="${classroomPage.getNumber + 1}" ></span>
</div>
</form>
</li>
</ul>
</nav>
everything is working alright, but I have double direction in address field of browser, like this
http://localhost:8080/classrooms/?page=2&size=5&sort=Capacity:%20ASC:%20ASC:%20ASC:%20ASC:%20ASC:%20ASC:%20ASC:%20ASC
Thank you for you time, and sorry for my bad english :)

Related

sort and filter product with query builder spatie

I have filter product that have sort and filter
sorting by price, created at , available product
But when I filter the product, I can no longer sort the same product filter
for example
http://127.0.0.1:8000/product?&filter[brand]=1
and I want sort product in brand based on created at but
When I click on the latest, the link does not continue the filter and only does the sort
http://127.0.0.1:8000/product?sort=created_at
I know there is a problem with my JavaScript, but I do not know how to solve it
<form method="GET" id="jobfilter-form" enctype="multipart/form-data" content="{{ csrf_token() }}">
<div class="col-sm-12 d-flex justify-content-center">
<div class="product-short">
<ul class="products-archive-tabs watch-page">
<p>order by ::</p>
<li>
<a id="createdAt" href="{{ URL::current()."?sort=created_at" }}">
The oldest
</a>
</li>
<li>
<a id="createdAt" href="{{ URL::current()."?sort=-created_at" }}">
the newest
</a>
</li>
</ul>
</div>
</div>
<div class="shop-box-area">
<div class="sidebar-categores-box shop-sidebar mb-10">
<div class="expander">
<div class="inner-bit">
<ul class="barnd-item">
#foreach ($brand as $brands)
<li>
<label class="m-checkbox">
<input
name="brand" type="checkbox" value="{{ $brands->id }}"
#if (in_array($brands->id, explode(',', request()-
>input('filter.brand'))))
checked
#endif
>
{{ $brands->name }}
<span class="count-brand">({{count($brands->product)}})
number
</span>
<img src="/photo/brandsmall/{{$brands->smallbarnd}}">
</label>
</li>
#endforeach
</ul>
</div>
</div>
</div>
</div>
java script
<script>
function getIds(checkboxName) {
let checkBoxes = document.getElementsByName(checkboxName);
let ids = Array.prototype.slice.call(checkBoxes)
.filter(ch => ch.checked == true).map(ch => ch.value);
return ids;
}
function filterResults() {
let brandIds = getIds("brand");
let createdAt = getIds("createdAt");
let href = 'product?';
if (createdAt.length) {
href += '&sort[created_at]=' + createdAt;
}
if (brandIds.length) {
href += '&filter[brand]=' + brandIds;
}
document.location.href = href;
}
$("#jobfilter-form #filter").on("click", e => {
filterResults();
});
</script>
$products = QueryBuilder::for(Product::class)
->allowedFilters([
AllowedFilter::exact('brand', 'brand_id'),
])
->allowedSorts('created_at')
->defaultSort('available_id')
->with('category')->paginate('5')
->appends(request()->query());
How can I keep the latest value in the link?
At First, you should not have duplicated ID value in your elements.
id="createdAt" is duplicated.
Second, you are using href for the sort function, whenever you click on it, the page will redirect to the URL of the href. so other things on the page will reset.
You should use a checkbox for sort items without the <a> tag, then when filter results are called, your sort items will be added to the URL without any page redirection.

MVC display UserName on layout page

In my app, users can login via a barcode scan.
The scan triggers a controller action like so:
function scanner(value) {
var badge = (value.startsWith('B-') || value.startsWith('B-')); //b- of B- = badge scan
if (badge)
{
$.ajax({
type: 'GET',
url: '/Login/Login',
data: {'badge': value},
succes: function () {
console.log("logged in");
}
});
}
else return;
}
Controller action:
public ActionResult Login(string badge)
{
string userName = string.Empty;
if (badge != null)
{
int oncid;
if (Int32.TryParse(badge.Substring(2), out oncid))
{
Core.Employee employee = Data.Oracle.GetEmployee(oncid.ToString());
if (employee != null)
{
userName = employee.FirstName + ' ' + employee.Name;
}
}
else
userName = "invalid badge";
}
ViewBag.UserName = userName;
return PartialView("_Login");
}
in _Layout.cshtml:
<nav class="navbar fixed-top navbar-expand-sm navbar-dark bg-dark">
<div class="container">
<a class="navbar-brand" href="#"><img src="~/Content/Images/logo.png" height="36" width="36" class="d-inline-block align-top"> App Name</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ml-auto mt-2 mt-lg-0">
<li class="nav-item active">
#*<a class="nav-link" href="#Url.Action("Index", "Home")">Home <span class="sr-only">(current)</span></a>*#
<a class="nav-link" href="#Url.Action("Index", "Home")"><span class="fas fa-home fa-2x" aria-hidden="true"></span></a>
</li>
<li class="nav-item">
<a class="nav-link" id="smrButton" href="#">SMR</a>
</li>
</ul>
</div>
#Html.Partial("_Login")
</div>
</nav>
_Login.cshtml just has #ViewBag.UserName for now.
If i set a breakpoint here, i can see that ViewBag.UserName holds the correct value, however, nothing is displayed in my page layout. What am i missing here?
Try using Session instead of ViewBag. For example:
Session["username"] = userName;
And call it in the view, simple as that

Laravel submit form from a tab and return to same tab in view

I have a view with multiple tabs. Each having different forms. When I submit a form from one tab, it returns to same page but primary tab. How could I get to return to same tab from which I was working.
Controller
public function recieve(Request $request)
{
$item = Item::find($request->input('item'));
$item->recieved_at = now();
$item->recieved_status = "1";
$item -> save();
return redirect('/files/'.$item->file)->with('success','Item Recieved Confirmed');
}
view - tabs
<ul class="nav nav-tabs" id="fileTab" role="tablist">
<li class="nav-item">
<a class="nav-link active" id="profile-tab" data-toggle="tab" href="#profile" role="tab"
aria-controls="profile" aria-selected="true">Profile</a>
</li>
<li class="nav-item">
<a class="nav-link" id="job-tab" data-toggle="tab" href="#job" role="tab" aria-controls="job"
aria-selected="false">Job</a>
</li>
<li class="nav-item">
<a class="nav-link" id="items-tab" data-toggle="tab" href="#items" role="tab" aria-controls="items"
aria-selected="false">Items</a>
</li>
<li class="nav-item"><a class="nav-link" id="mechanic-tab" data-toggle="tab" href="#mechanic" role="tab"
aria-controls="mechanic" aria-selected="false">Labour Hours</a>
</li>
<li class="nav-item">
<a class="nav-link" id="accounts-tab" data-toggle="tab" href="#accounts" role="tab"
aria-controls="accounts" aria-selected="false">Accounts</a>
</li>
<li class="nav-item">
<a class="nav-link" id="accounts-tab" data-toggle="tab" href="#preview" role="tab"
aria-controls="accounts" aria-selected="false">Print Preview</a>
</li>
</ul>
Form
#if($file->job_status == "Closed")#else
{!! Form::open(['action' => 'FilesController#item','method' => 'POST']) !!}
<div class="row pt-3 pb-1">
<div class="form-group col-xs-12 col-sm-12 col-md-2">
{{Form::text('part_number','',['class'=>'form-control','placeholder'=>'Part Number'])}}
</div>
<div class="form-group col-xs-12 col-sm-12 col-md-6">
{{Form::text('description','',['class'=>'form-control','placeholder'=>'Part Name'])}}
</div>
<div class="form-group col-xs-12 col-sm-12 col-md-2">
{{Form::text('qty','',['class'=>'form-control','placeholder'=>'Qty'])}}
</div>
<div class="form-group col-xs-12 col-sm-12 col-md-2">
{{Form::select('recieved_by',$users,null,['class'=>'form-control'])}}
</div>
</div>
{{Form::hidden('file',$file->id)}}
{{Form::submit('Release Item',['class'=>'form-control btn-danger btn btn-sm'])}}
{!! Form::close() !!}
#endif
I tried using hashtag tab name in return redirect statement,
return redirect('/files/'.$item->file."#items-tab")->with('success','Item Recieved Confirmed');
it would simply highlight the name but would not select. How could I achieve it?
your tab navigation,
<ul class="nav nav-tabs" id="tabMenu" role="tablist">
<li><i class="fa fa-camera" aria-hidden="true"></i> Galleries</li>
</ul>
hidden input field for tab
<input type="hidden" name="tab" value="Galleries">
RedirectRoute
$tab = $request->get('tab');
return back()->withInput(['tab'=>$tab]);
Javascript,
<script>
//redirect to specific tab
$(document).ready(function () {
$('#tabMenu a[href="#{{ old('tab') }}"]').tab('show')
});
</script>
If you want to do it in laravel than all you can do is,
-Give an id to each tab and an write an active class.
-Put a hidden input with value of the parent tab id.
-So, laravel will receive the tab id in the request object.
-Now, you can do whatever and return the tab id with to the view.
-Give a if condition to each tab item and check if matches the id from laravel response.
The match the id set the active class to it.
example
<ul>
<li id='tab1 {{ session()->get('tabId') === 'tab1' ? 'active' : '' }}'></li>
<li id='tab2 {{ session()->get('tabId') === 'tab2' ? 'active' : '' }}'></li>
</ul>
// tab1 body
<form>
<input type="hidden" name="tabId" value="tab1">
...
</form>
// tab2 body
<form>
<input type="hidden" name="tabId" value="tab2">
...
</form>
// controller
public funciton(Request $request) {
//tabId
$tabId = $request->input('tabId')
....
....
return redirect()->back()->with('tabId' => $tabId);
}
You can do this:
Controller:
public function tab() {
$active_tab = "tab2";
return view('tabs.index', compact('active_tab'));
}
View:
<ul class="nav nav-tabs">
<li class="#if($active_tab=="tab1") active #endif"><a data-toggle="tab" href="#home">Home</a></li>
<li class="#if($active_tab=="tab2") active #endif"><a data-toggle="tab" href="#menu1">Menu 1</a></li>
<li class="#if($active_tab=="tab3") active #endif"><a data-toggle="tab" href="#menu2">Menu 2</a></li>
</ul>
<div class="tab-content">
<div id="home" class="tab-pane fade #if($active_tab=="tab1") in active #endif">
<h3>HOME</h3>
<p>Some content.</p>
</div>
<div id="menu1" class="tab-pane fade #if($active_tab=="tab2") in active #endif">
<h3>Menu 1</h3>
<p>Some content in menu 1.</p>
</div>
<div id="menu2" class="tab-pane fade #if($active_tab=="tab3") in active #endif">
<h3>Menu 2</h3>
<p>Some content in menu 2.</p>
</div>
</div>
You can show a specific tab with this code:
function showTab(tab){
$('.nav-tabs a[href="#' + tab + '"]').tab('show');
};
// Verify location.hash
if (window.location.hash !== ''){
showTab(window.location.hash);
}
But I would pass a query string in the return, just to make sure there will be no collision with other eventual hashes. Something like:
return redirect('/files/'.$item->file."?openTab=your-tab-id")->with('success','Item Recieved Confirmed');
Of course, you would change the "location.hash" verification to a query string verification. Example: https://davidwalsh.name/query-string-javascript

pagination in spring boot whit a form parameter

I'm using spring boot and thymeleaf.
I have a page with a form that search by a specific param then submits the data as POST request.
The page shows the result in a table and works fine. The table shows the correct pagination options and result,
Problem: When I click the button to show results on page 2 o laters, then the POST data from the previous search is lost.
Question: How can I retain the POSTed data during pagination?
CONTROLLER:
#RequestMapping(value = "/list",method = {RequestMethod.POST, RequestMethod.GET})
public String list(#RequestParam(name = "page", defaultValue = "0") int page,
#RequestParam(name = "name", defaultValue = "") String name,
#RequestParam(name = "lastname", defaultValue = "") String lastname,
Pageable pageable,
Model model) {
Pageable pageRequest = new PageRequest(page, 4);
Cliente cliente = new Cliente();
Page<Cliente> clientes;
if (name.equals("") && lastname.equals("")) {
clientes = clienteService.findAll(pageRequest);
}else {
clientes = clienteService.findMatch(name, lastname, pageRequest);
}
PageRender<Cliente> pageRender = new PageRender<Cliente>("/listar", clientes);
model.addAttribute("titulo", "Listado de clientes");
model.addAttribute("clientes", clientes);
model.addAttribute("cliente", cliente);
model.addAttribute("page", pageRender);
return "listar";
}
HTML:
<form th:action="#{/listar}" th:object="${cliente}" method="post">
<div class="form-group row">
<label for="nombre" class="col-sm-2 col-form-label">Nombre</label>
<div class="col-sm-6">
<input type="text" th:field="*{nombre}" class="form-control"
th:errorclass="'form-control alert-danger'" /> <small
class="form-text text-danger"
th:if="${#fields.hasErrors('nombre')}" th:errors="*{nombre}"></small>
</div>
</div>
<div class="form-group row">
<label for="nombre" class="col-sm-2 col-form-label">Apellido</label>
<div class="col-sm-6">
<input type="text" th:field="*{apellido}" class="form-control"
th:errorclass="'form-control alert-danger'" /> <small
class="form-text text-danger"
th:if="${#fields.hasErrors('apellido')}" th:errors="*{apellido}"></small>
</div>
</div>
<div class="form-group row">
<div class="col-sm-6">
<input type="submit" th:value="'Buscar'"
class="btn btn-secondary" />
</div>
</div>
</form>
Pagination:
<nav th:fragment="paginator">
<ul class="pagination">
<li class="page-item"
th:class="${page.first? 'page-item disabled': 'page-item'}"><span
class="page-link" th:if="${page.first}">Primera</span> <a
class="page-link" th:if="${not page.first}"
th:href="#{${page.url}(page=0)}">Primera</a></li>
<li class="page-item"
th:class="${not page.hasPrevious? 'page-item disabled': 'page-item'}">
<span class="page-link" th:if="${not page.hasPrevious}">«</span>
<a class="page-link" th:if="${page.hasPrevious}"
th:href="#{${page.url}(page=${page.paginaActual-2})}">«</a>
</li>
<li class="page-item" th:each="item : ${page.paginas}"
th:class="${item.actual? 'page-item active': 'page-item'}"><span
class="page-link" th:if="${item.actual}" th:text="${item.numero}"></span>
<a class="page-link" th:if="${not item.actual}"
th:href="#{${page.url}(page=${item.numero-1})}"
th:text="${item.numero}"></a></li>
<li class="page-item"
th:class="${not page.hasNext? 'page-item disabled': 'page-item'}">
<span class="page-link" th:if="${not page.hasNext}">»</span> <a
class="page-link" th:if="${page.hasNext}"
th:href="#{${page.url}(page=${page.paginaActual})}">»</a>
</li>
<li class="page-item"
th:class="${page.last? 'page-item disabled': 'page-item'}"><span
class="page-link" th:if="${page.last}">Última</span> <a
class="page-link" th:if="${not page.last}"
th:href="#{${page.url}(page=${page.totalPaginas-1})}">Última</a>
</li>
</ul>
</nav>
I had a similar situation where I used the request parameter. So the trick is to capture the request parameter based on what we perform the search. Then afterwards you can get that parameter (from URL) and use it in the links you are building for pagination.
For example, say, your post/get URL looks like this:
http://localhost:8080/listByName?name=earth
Then get the vale of "name" parameter and use it while building your pagination url (links) in your html table/list like this:
th:href="#{/listByName/(name=${#request.getParameter('name')},pageSize=${selectedPageSize}, page=${page})}"
for me it working perfectly. This way you wont have to go for additional code using flashAttributes or mucking with JavaScript.

Html in Razor MVC 3

How can I write this in MVC 3 Razor correctly?
<li <%if(ViewContext.RouteData.Values["Controller"].ToString() == "Home"){%> class="active"<%} %>>Home</li>
Blessings
EDIT
this is my code and does not work :(
<div class="navbar">
<div class="navbar-inner">
<div class="container">
<a class="brand" href="#">CYSAC 2.0</a>
<ul class="nav">
<li #(ViewContext.RouteData.Values["Controller"].ToString() == "Home" ? "class=\"active\"" : string.Empty)>Inicio</li>
<li #(ViewContext.RouteData.Values["Controller"].ToString() == "University" ? "class=\"active\"" : string.Empty)>University</li>
<li #(ViewContext.RouteData.Values["Controller"].ToString() == "Estudent" ? "class=\"active\"" : string.Empty)>Estudent</li>
</ul>
</div>
</div><!-- /navbar-inner -->
</div><!-- /navbar -->
please help
I would write this as:
<li class="#ViewContext.RouteData.Values["Controller"].ToString() == "Home" ? "active" : """>
Home
</li>
This has a drawback of having an class="" if it's not the current controller, but in the newest version of Razor, it will recognize this and strip it out.
You can use the #(expression) - syntax:
<li class="#((ViewContext.RouteData.Values["Controller"].ToString() == "Home" ?
"active" : string.Empty)">
Home
</li>
Today I found myself in the same situation ( 5 months later ).
1) Define variable controllerName and set with controller name .ToLower()
2) Compare with Equals("home")
#{
var controllerName = ViewContext.RouteData.Values["Controller"].ToString().ToLower();
}
<div class="navbar">
<div class="navbar-inner">
<div class="container">
<a class="brand" href="#">CYSAC 2.0</a>
<ul class="nav">
<li class="#(controllerName.Equals("home") ? "active" : "")">Inicio</li>
<li class="#(controllerName.Equals("university") ? "active" : "")">University</li>
<li class="#(controllerName.Equals("estudent") ? "active" : "")">Estudent</li>
</ul>
</div>
</div><!-- /navbar-inner -->
</div><!-- /navbar -->
Works for me!

Resources