I have Thymeleaf pagination at departments page, which work good, but i have one problem.
When i try to update department name, this renamed department gone from page and shown at last page as last row.
How can i fix this? I want to update departments name and have it on the same place, not at the end.
This is my code.
Service:
public Page<Department> findPaginatedDepartments(final Pageable pageable) {
List<Department> departments = departmentRepository.findAll();
int pageSize = pageable.getPageSize();
int currentPage = pageable.getPageNumber();
int startItem = currentPage * pageSize;
final List<Department> list;
if (departments.size() < startItem) {
list = Collections.emptyList();
} else {
int toIndex = Math.min(startItem + pageSize, departments.size());
list = departments.subList(startItem, toIndex);
}
return new PageImpl<>(list, PageRequest.of(currentPage, pageSize), departments.size());
}
Controller:
#GetMapping()
public String getAllDepartments(
Model model,
#RequestParam("page") Optional<Integer> page,
#RequestParam("size") Optional<Integer> size) {
int currentPage = page.orElse(1);
int pageSize = size.orElse(10);
Page<Department> departmentPage = departmentService.findPaginatedDepartments(PageRequest.of(currentPage - 1, pageSize));
model.addAttribute("departmentPage", departmentPage);
int totalPages = departmentPage.getTotalPages();
if (totalPages > 0) {
List<Integer> pageNumbers = IntStream.rangeClosed(1, totalPages)
.boxed()
.collect(Collectors.toList());
model.addAttribute("pageNumbers", pageNumbers);
}
return "/department/departments";
}
View:
<div class="departments_wrapper main_menu_wrapper">
<div id="dep-grid" class="departments_table highlight">
<div class="head_departments">
<span class="head-left-grid" th:onclick="'javascript:sortDepartments()'">Name<i id="arrow-sort" class="tiny material-icons arrow-sort-button">expand_more</i></span>
<span class="head-right-grid">Edit</span>
</div>
<ul>
<div class="dep-body" th:each="department : ${departmentPage.content}">
<li id="dep-li" class="left-column" th:text="${department.name}"></li>
<li class="right-column">
<div class="dep_edit">
<a id="dep-modal-pic" class="edit_dep modal-trigger" href="#modal3"
th:onclick="'javascript:showFunctionModal(\'' + ${department.id} +'\' , \'' + ${department.name} +'\');'"><i
class="material-icons">more_horiz</i></a>
</div>
</li>
</div>
</ul>
</div>
<div class="pagination pagination-dep">
<ul>
<li class="disabled"><i class="material-icons">chevron_left</i></li>
<li><a th:if="${departmentPage.totalPages > 0}" th:each="pageNumber : ${pageNumbers}"
th:href="#{/departments(size=${departmentPage.size}, page=${pageNumber})}" th:text="${pageNumber}"
th:class="${pageNumber==departmentPage.number + 1} ? active"></a></li>
<li class="disabled"><i class="material-icons">chevron_right</i></li>
</ul>
</div>
</div>
Update Department name
#Transactional
public void updateDepartment(final Long id, final Department department) {
final Department departmentFound = departmentRepository.getOne(id);
departmentFound.setName(department.getName());
departmentRepository.saveAndFlush(departmentFound);
}
Solved!
After many time of search i find, that it is the postgres "feature".
So, in my DepartmentService i just wrote comparator, which will sort Departments by id.
And this solved my problem.
final Comparator<Department> cmp = Comparator.comparing(Department::getId);
Related
I am working on MVC 5. Using WEB API, fetch the data now it is time to apply the HTML PAGE design and CSS.
#foreach (var item in Model)
{
if (Model.First() == item)
{
///APPLY FIRST RECORD CSS:- Works FINE
}
else {
<div class="row">
<div class="col-sm-4">
</div>
</div>
}
}
In the else portion, every time it generates the new ROW for a SINGLE record. But I am interested to display record 2 3 4 in SECOND ROW. 5 6 7 Record in the THIRD ROW and so on.
If it is the first item, open a div and then put the items in it. Close the div when the number of columns is 3 or close the div if the item is at the end of the list.
The following algorithm does this for you
#{
int i = 0;
int columnCounter = 1;
bool newRow = false;
}
#foreach (var item in Model)
{
//add and open div row
if (i == 0 || newRow)
{
newRow = false;
#:<div class="row" style="border:2px solid red;">
}
<div class="col-md-4" style="padding:0;">
<div style="height:40px;background:#f6f6f6;width:100%;text-align:center;">
<span>Column #i</span>
</div>
</div>
//close div row if column count == 3 or reach of end list
if (columnCounter == 3 || i == Model.Count - 1)
{
newRow = true;
columnCounter = 1;
#:</div>
}
else
{
columnCounter = columnCounter + 1;
}
}
result:
Model
public partial class Official
{
public short OfficialNo { get; set; }
public string Surname { get; set; }
public string Firstname { get; set; }
}
I have 2 partial answers but neither gives me what I want:
In my controller on Edit:
ViewBag.OfficialNo = new SelectList(_context.Official, "OfficialNo", "OfficialNo", "", "Surname");
This gives me
Smith
111
Brown
102
Gray
103
The top line is bold and comes from "Surname" which is supposed to be the Data Group Field. The bottom line is what is stored in the database and displays a value if there is one (which is correct).
Alternatively
ViewBag.OfficialNo = new SelectList(_context.Official.Select(e => e.OfficialNo + " - " + e.Firstname + " " + e.Surname));
This gives me
101 – John Smith
102 – Bob Brown
103 – Garry Gray
which is the info I want but does not allow a selection to be stored (naturally).
I want
John Smith
111
Bob Brown
102
Garry Gray
103
so that the first line is info and the second line (if selected) will be what is stored on update.
How do I combine the 2 ViewBag statements above ?
For further info my Edit View statement is:
<div class="form-group">
<label asp-for="RefereeNo" class="control-label"></label>
<select asp-for="RefereeNo" class="form-control" asp-items="ViewBag.OfficialNo"><option> </option></select>
</div>
One of the solution is to add Group property of SelectListItem :
Controller :
var officials = _context.Official.GroupBy(x => x.Firstname+" "+x.Surname);
List<SelectListItem> selectListItems = new List<SelectListItem>();
foreach (var official in officials)
{
var optionGroup = new SelectListGroup() { Name = official.Key };
foreach (var item in official)
{
selectListItems.Add(new SelectListItem() { Value = item.OfficialNo.ToString(), Text = item.OfficialNo.ToString(), Group = optionGroup });
}
}
ViewBag.OfficialNo = selectListItems;
Then in view show/bind the dropdownlist :
<div class="form-group">
<label asp-for="#item.RefereeNo" class="control-label"></label>
<select asp-for="#item.RefereeNo" class="form-control" asp-items="ViewBag.OfficialNo"><option> </option></select>
</div>
As per Nan Yu's answer above:
Controller :
var officials = _context.Official.GroupBy(x => x.Firstname+" "+x.Surname);
List<SelectListItem> selectListItems = new List<SelectListItem>();
foreach (var official in officials)
{
var optionGroup = new SelectListGroup() { Name = official.Key };
foreach (var item in official)
{
selectListItems.Add(new SelectListItem() { Value = item.OfficialNo.ToString(), Text = item.OfficialNo.ToString(), Group = optionGroup });
}
}
ViewBag.OfficialNo = selectListItems;
I store 2 dates in my products table start_discounted and end_discounted this 2 columns are responsible of my discount price duration. Now when my product discount duration expires except it return back to normal price it stays on discounted price and my timer goes to minus.
Codes
my model scope
public function scopeValidDiscount($query){
return $query->where('start_discounted', '<=', Carbon::now()->toDateTimeString())
->where('end_discounted', '>=', Carbon::now()->toDateTimeString());
}
controller
public function product($slug){
$product = Product::where('slug', $slug)->firstOrFail();
$validdiscount = $product->ValidDiscount()->get();
//...
}
blade
#if($validdiscount)
show timer
#endif
PS: I need to avoid showing timer in 2 conditions, 1 discount dates are expired. 2 Product discount dates are null.
Any idea where is my mistake?
UPDATE
My timer in blade:
#if($validdiscount)
<div class="deals_timer row align-items-center">
<div class="col-md-6 deals_timer_title_container">
<div class="deals_timer_title">{{__('store.hurryup')}}</div>
<div class="deals_timer_subtitle">{{__('store.endsin')}}</div>
</div>
<div class="col-md-6 deals_timer_content ml-auto">
<div class="deals_timer_box clearfix" data-target-time="{{$end}}">
<div class="text-center deals_timer_unit">
<div id="deals_timer3_hr" class="deals_timer_hr"></div>
<span>{{__('store.hours')}}</span>
</div>
<div class="text-center deals_timer_unit">
<div id="deals_timer3_min" class="deals_timer_min"></div>
<span>{{__('store.mins')}}</span>
</div>
<div class="text-center deals_timer_unit">
<div id="deals_timer3_sec" class="deals_timer_sec"></div>
<span>{{__('store.secs')}}</span>
</div>
</div>
</div>
</div>
#endif
Please pay attention to data-target-time="{{$end}}" i think the issue might be here...
my timer script:
<script>
$(document).ready(function(){
if($('.deals_timer_box').length)
{
var timers = $('.deals_timer_box');
timers.each(function()
{
var timer = $(this);
var targetTime;
var target_date;
// Add a date to data-target-time of the .deals_timer_box
// Format: "Feb 17, 2018"
if(timer.data('target-time') !== "")
{
targetTime = timer.data('target-time');
target_date = new Date(targetTime).getTime();
}
else
{
var date = new Date();
date.setDate(date.getDate() + 2);
target_date = date.getTime();
}
// variables for time units
var days, hours, minutes, seconds;
var h = timer.find('.deals_timer_hr');
var m = timer.find('.deals_timer_min');
var s = timer.find('.deals_timer_sec');
setInterval(function ()
{
// find the amount of "seconds" between now and target
var current_date = new Date().getTime();
var seconds_left = (target_date - current_date) / 1000;
console.log(seconds_left);
// do some time calculations
days = parseInt(seconds_left / 86400);
seconds_left = seconds_left % 86400;
hours = parseInt(seconds_left / 3600);
hours = hours + days * 24;
seconds_left = seconds_left % 3600;
minutes = parseInt(seconds_left / 60);
seconds = parseInt(seconds_left % 60);
if(hours.toString().length < 2)
{
hours = "0" + hours;
}
if(minutes.toString().length < 2)
{
minutes = "0" + minutes;
}
if(seconds.toString().length < 2)
{
seconds = "0" + seconds;
}
// display results
h.text(hours);
m.text(minutes);
s.text(seconds);
}, 1000);
});
}
});
</script>
{{$end}}
This is what I have in my controller in order to get my expired date and use if in my JavaScript above:
$mytime = Carbon::now();
if(!empty($product->start_discounted && $product->end_discounted)){
$start = $product->start_discounted->format('M d, Y');
$end = $product->end_discounted->format('M d, Y');
}
then I also have my scope code:
$validdiscount = $product->ValidDiscount()->get();
now what I think is that my issue cause by: I get my expire date from
product directly $product->end_discounted while I try to avoid it while I'm using scope which
it does the same carbon thing in my model, not sure! (maybe i should get my $end by scope as well?
You're not using scopes correctly. Scopes are used on query builder, not on an already retrieved model.
For doing this, you just need a regular method:
public function validDiscount() {
return $this->start_discounted <= now()->toDateTimeString()
&& $this->end_discounted >= now()->toDateTimeString();
}
And then in your view, check it like this:
#if($product->validDiscount())
show timer
#endif
And remove the unneeded $validdiscount = $product->ValidDiscount()->get(); (where you're using a query scope on an instance of a model).
You can use javascript/jquery in front end to do it.
When you populate that page, call that js function to check that value like this $(document).ready(function(){ check_timer() })
Then trigger that method again when you timer has expired to update your view(hide promotional price, hide timer & showing actual price)
check_timer function can be like this
function check_timer(){
if(!{{ $validdiscount }}){
//hide your required div
}
}
I want to use MvcPaging in a PartialView. I have a search page and I need to paginate the results. So far, only the first page from the results appears - when I am trying to go to page 2 I get in the console a 500 error and nothing happens.
Here are the two actions from controller:
public PartialViewResult SearchResults(string lat, string lng, double? dist)
{
if (Request.IsAjaxRequest())
{
string address = Request["address"];
string latitude = lat;
string longitude = lng;
GeoCoordinate coord = new GeoCoordinate(Double.Parse(latitude, CultureInfo.InvariantCulture), Double.Parse(longitude, CultureInfo.InvariantCulture));
IQueryable<Restaurants> near = (from r in _db.Restaurants select r);
results = new List<Restaurants>();
foreach (Restaurants restaurant in near)
{
double latBD = (double)restaurant.Latitude;
double lngDB = (double)restaurant.Longitude;
if (new GeoCoordinate(latBD, lngDB).GetDistanceTo(coord) <= dist * 1000)
{
results.Add(restaurant);
}
}
return PartialView("_SearchResult", results.ToPagedList(0, 2));
}
return PartialView("Search");
}
public ActionResult PaginationAjax(int? page)
{
int currentPageIndex = page.HasValue ? page.Value - 1 : 0;
return PartialView("_SearchResult", results.ToPagedList(currentPageIndex, 2));
}
And the partial view:
#model IPagedList<Restaurants>
#using MvcPaging
foreach (var item in Model)
{
<blockquote>
<h3>#item.Name</h3>
</blockquote>
}
<div class="pager">
#Html.Pager(Model.PageSize, Model.PageNumber, Model.TotalItemCount, new AjaxOptions { UpdateTargetId = "searchResults" }).Options(o => o.Action("PaginationAjax"))
</div>
Does anyone has any idea what I am doing wrong? Thanks!
EDIT:
Error
GET http://localhost/TakeASeat/Restaurants/PaginationAjax?page=2&X-Requested-With=XMLHttpRequest&_=1431698681795 500 (Internal Server Error)
jQuery.ajaxTransport.send # jquery-2.1.3.js:8625
jQuery.extend.ajax # jquery-2.1.3.js:8161
asyncRequest # jquery.unobtrusive-ajax.js:128
(anonymous function) # jquery.unobtrusive-ajax.js:138
jQuery.event.dispatch # jquery-2.1.3.js:4430
jQuery.event.add.elemData.handle # jquery-2.1.3.js:4116
i have the following code:
HtmlAgilityPack.HtmlNodeCollection nodeCollection = bodyNode.SelectNodes("//ul[#class='myClass']//li");
which grabs about 250 LI's
the UL format is a bit weird and it goes like this:
<ul>
<li>
<h5>Parent</h5>
Some more tags here...
</li>
<li>
<h4>Child of the prev li</h4>
</li>
<li>
<h4>Child of the prev li</h4>
</li>
<!-- and so on -->
<!-- Then again -->
<li>
<h5>Parent</h5>
Some more tags here...
</li>
<li>
<h4>Child of the prev li</h4>
</li>
<li>
<h4>Child of the prev li</h4>
</li>
<!-- child li's are not constant, this is only for demo -->
</ul>
i need to separate the LI's into groups where each group contains the parent LI and all the children LI's
anyone can help with this?
If I understood correctly this is want you want
HtmlNodeCollection liList = doc.DocumentNode.SelectNodes("//ul//li");
List<List<HtmlNode>> liGroups = new List<List<HtmlNode>>();
List<HtmlNode> liGroup = null;
foreach (HtmlNode li in liList)
{
if (li.InnerText.Contains("Parent"))
{
if (liGroup != null)
liGroups.Add(liGroup);
liGroup = new List<HtmlNode>();
liGroup.Add(li);
}
else
{
liGroup.Add(li);
}
}
liGroups.Add(liGroup);
What you will have at the end is a list liGroups that will have other list liGroup. For your above html it will show that liGroups have 2 liGroup because in your above html you have 2 parents and both two liGroup will have 3 li (1 parent + 2 childs) because both parents have same amount of children.
After that you do with them whatever you want for example:
MessageBox.Show(liGroups[0][2].InnerText); //Show from the first group the 3rd's li InnerText
var tree = new Dictionary<HtmlNode, List<HtmlNode>>();
foreach (var node in nodeCollection)
if (node.SelectSingleNode("h5[text()='Parent']") != null)
tree.Add(node, new List<HtmlNode>());
else
tree.Last().Value.Add(node);
or
var groups = nodeCollection.Group();
static class Extensions
{
public static ILookup<HtmlNode, HtmlNode> Group(this HtmlNodeCollection collection)
{
return collection.Where(n => !n.IsParent()).ToLookup(n => n.GetParent());
}
public static bool IsParent(this HtmlNode node, string header = "Parent")
{
var h = node.Element("h5");
return h != null && h.InnerText == header;
}
public static HtmlNode GetParent(this HtmlNode node)
{
while (!node.IsParent())
node = node.PreviousSibling;
return node;
}
}