I've struggled with this for quite some time. Today I finally wrote the following code.
The ViewModel contains an int property which later tells the view how many pages the data has been split into.
The controller splits the data by taking a specified amount of rows and, in the event of paging, splits by pageNumber * recordsPerPage
Take a look:
The ViewModel
public class ThreadPostsViewModel
{
public Thread Thread { get; set; }
public List<Post> Posts { get; set; }
public int Pages { get; set; }
}
The Controller
private int PostsPerPage = 10;
public ActionResult Thread(int id, int page = 1)
{
using (OrtundEntities Db = new OrtundEntities())
{
// get the thread and its parent data (parent for breadcrumbs)
var Thread = Db.Threads.Include(t => t.Title).FirstOrDefault(x => x.Id == id);
// create a list for the Posts
List<Post> Posts = new List<Post>();
// select based on paging
if (page == 1)
// no paging has happened, get the first set of records
Posts = Db.Posts.Include(x => x.User).Where(x => x.ThreadId == id).OrderByDescending(x => x.Date).Take(PostsPerPage).ToList();
else
// we're on a new page. Skip however many rows we've already seen
Posts = Db.Posts.Include(x => x.User).Where( x=> x.ThreadId == id).OrderByDescending(x => x.Date).Take(PostsPerPage).Skip(PostsPerPage * page).ToList();
// create and return the view model
ThreadPostsViewModel Model = new ThreadPostsViewModel
{
Thread = Thread,
Posts = Posts,
Pages = Posts.Count / PostsPerPage
};
return View(Model);
}
}
The View
#model Ortund.Models.ThreadPostsViewModel
<div class="paging">
#for (int i = 1; i < Model.Pages; i++)
{
string Url = String.Format("/View/Thread/{0}?page={1}", Model.Thread.Id, i);
#i
}
</div>
<div class="posts-list">
#foreach (var Post in Model.Posts)
{
<div class="post" id="#Post.Id">
</div>
}
</div>
In this code, assuming 300 posts are selected from the database and 10 posts are specified per page, then there should be 30 pages.
Even that's a hefty amount of links to fit into your page design so how can I minimize these paging links and display, say, 10 paging links only where, when you get to say, page 8, the links will change to show you 3-13, for example?
Even having the paging links display as follows would be preferable:
1 2 3 4 5 ... 90 91 92 93 94
In controller put value of current page:
ViewBag.currentPage = page;
In view you can do something like this (not tested):
<div class="paging">
#if (Model.Pages > 11 && ViewBag.currentPage > 6)
{
for (int i = ViewBag.currentPage - 6; i < ViewBag.currentPage -1; i++)
{
string Url = String.Format("/View/Thread/{0}?page={1}", Model.Thread.Id, i);
#i
}
for (int i = ViewBag.currentPage + 1; i < ViewBag.currentPage + 6; i++)
{
string Url = String.Format("/View/Thread/{0}?page={1}", Model.Thread.Id, i);
#i
}
}
else
{
for (int i = 1; i < Model.Pages; i++)
{
string Url = String.Format("/View/Thread/{0}?page={1}", Model.Thread.Id, i);
#i
}
}
</div>
Related
I have created a loop in an MVC Razor Page to create the number of buttons based on a category, but have not figured out how to get the value, which is a count number and the label of the button chosen as of yet.
How do you get the value of the button and Label of the button chosen?
[Code]
//MVC Razor View Page:
//Loop to Get Dictionary List Buttons with Distinct Section Values.
for (int i = 0; i < Model.Section.Count; i++)
{
// var Count = i;
<input type="submit" name="#Html.NameFor(model => model.Section)" value=#Model.Section[i].Value id="ddlSectionButtons" , new { onclick="document.forms[0].submit();" class="ddlSectionButtons" onClick="focusMe(this);" } />
}
//Controller:
Section = (from c in entities.LocationDatas
where !string.IsNullOrEmpty(c.Section) && c.Section != null && country == c.PlantLocation
select new SelectListItem { Text = c.Section, Value = c.Section }).Distinct().ToList(),
//Model:
public List<SelectListItem> SectionList { get; set; }
public List<SelectListItem> Section { get; set; }
//J-Query: (It only gets the first button label, but not the chosen?)
//Capture the current ddlSection.
$(".ddlSectionButtons").each(function () {
$("body").on("click", ".ddlSectionButtons", function () {
var ddlSectionButtons = $('#ddlSectionButtons').val();
if ($('.ddlSectionButtons').val() != null) {
alert("Section: " + ddlSectionButtons);
};
});
});
[/Code]
J-Query only gets the first button and does not return the correct value, but only the first value, and need it to also return the count number generated through the loop.
Try to use only onclick="focusMe(this)",so that you can get the current input in the function.Here is a working demo:
for (int i = 0; i < Model.Section.Count; i++)
{
// var Count = i;
<input type="submit" name="#Html.NameFor(model => model.Section)" value=#Model.Section[i].Value id=#i class="ddlSectionButtons" onclick="focusMe(this)"/>
}
js:
function focusMe(t) {
var count=$(t).attr("id");
var ddlSectionButtons = $(t).val();
if (ddlSectionButtons != null) {
alert("Section: " + ddlSectionButtons);
}
}
After trying to fill a html table with data from a MVC model, we ran into a problem. This code below generates a table, which is supposed to represent a schedule. The first row displays the days of the week, and the first column displays the number representation of the school hours.
<table class="table2">
<tr>
<th></th>
#{ string[] days = { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" };}
#for (int i = 0; i < days.Length; i++)
{
<th>#(days[i])</th>
}
</tr>
#{
TimeSpan timeSpan = new TimeSpan(8, 30, 0); //08:30:00
TimeSpan AddTime = new TimeSpan(0, 50, 0); //00:50:00
TimeSpan timeSpan2 = new TimeSpan();
}
#{ for (int i = 1; i < 16; i++)
{
<tr>
<td>
<h3>#(i)</h3>
<a>
#{timeSpan.ToString();}
#(string.Format("{0:00}:{1:00}", timeSpan.Hours, timeSpan.Minutes))
</a>
#{timeSpan2 = timeSpan.Add(AddTime);}
<div>
<a>
#(string.Format("{0:00}:{1:00}", timeSpan2.Hours, timeSpan2.Minutes))
#{timeSpan = timeSpan2;}
</a>
</div>
</td>
#foreach (var item in Model)
{
if (item.Hours.Contains(i))
{
for (int x = 0; x < days.Length; x++)
{
if (item.Day != days[x])
{
<td>
</td>
}
else
{
<td>
#(item.Class)
#(item.Classroom)
</td>
}
}
}
}
</tr>
}
}
</table>
Our model looks like this;
public class ScheduleModel
{
[Display(Name = "Lesson Code")]
public string LessonCode { get; set; }
[Display(Name = "Day")]
public string Day { get; set; }
[Display(Name = "Classroom")]
public string Classroom { get; set; }
[Display(Name = "Hours")]
public int[] Hours { get; set; }
[Display(Name = "Class")]
public string Class { get; set; }
}
Ill try to explain what we're trying to do. Our view gets a list of models, filled with the data specified above. We want to display this data in the html table we tried to create above. The "Day" variable corresponds to the days in the top row of the table, and the Hours int[] corresponds to the hours in the first column of the table. These values should be compared with eachother to find the correct spot in the table. We almost got it to work, with the code displayed above, but we ran into a problem with duplicate hours and empty cells.
Lets say we get 2 instances of the model, one looks like this;
Day : Monday
Hours : [1,2,3]
and the second looks like this :
Day : Tuesday
Hours: [1,2,3]
The problem with this is that the foreach loop goes through the first model completly, and fills all cells in the first row with empty cells, then when the foreach loops through the second model, it cannot fill the already created empty cells, so it just sticks them on at the end; screenshot to visualize the problem ;
So it all boils down to this; how do we generate the rows, but in such a way that we can still add new data into the empty fields.
This works perfectly!
#for (int x = 0; x < 7; x++)
{
<td>
#foreach(var item in Model)
{
if (item.Hours.Contains(i))
{
if(item.Day == days[x]){
#(item.Class)
#(item.Classroom)
}
else
{
#("")
}
}
}
</td>
}
While coding an assignment I've come to need to transfer data from the code-behind to the view so that I can parse that data with Javascript and build some HTML with it, and I've decided to use asp:HiddenField to that end.
However, it seems something goes wrong, since I get the error "The name "HiddenFieldData" does not exist in the current context".
I assume that I'm somehow not linking the view to the model correctly.
Perhaps it's because I'm using a model that is not the appropriate cshtml.cs, but one that is "given" to the view via the controller.
Truth be told, this is my first time with ASP.NET so it's very likely the problem is somewhere here.
The code in question, I've marked the trouble spots with '>>>>':
Controller -
public class saveController : Controller
{
// GET: Save
public ActionResult SaveRoute()
{
saveModel model = new saveModel();
Model given >>>> return View(model);
}
}
Model -
public class saveModel
{
private DataMiner miner;
public saveModel(string ip = "127.0.0.1", int port = 5400, int duration = 10, int interval = 1000)
{
// Initialize miner
miner = new DataMiner(ip, port, duration, interval);
}
public void SaveRoute()
{
// Mine and retrieve data
miner.Mine();
double[][] data = miner.GetData();
int lines = data.GetLength(0);
int cols = data.GetLength(1);
string[] str_data = new string[lines];
for (int i = 0; i < lines; ++i)
{
// Turn double data into strings to write
str_data[i] = data[i].ToString();
}
// Write to file
System.IO.File.WriteAllLines(#"file1.txt", str_data);
// Write values to HiddenField
string values = String.Join(" ", str_data);
Error here >>>> HiddenFieldData.Value = values;
// Call JS function to load at
ScriptManager.RegisterStartupScript(this, GetType(), "showDataMined", "showDataMined();", true);
}
}
View -
#model RESTful_Flight_Simulator.Models.saveModel
#{
ViewBag.Title = "SaveRoute";
}
<html>
<head>
<asp:ScriptManager ID="ScriptManager1" runat="server" EnablePageMethods="true" />
<script type="text/javascript" language="javascript">
function showDataMined()
{
var body = document.body
var tbl = document.createElement('table');
tbl.style.width = '100px';
tbl.style.border = '1px solid black';
for (var i = 0; i < 3; i++)
{
var tr = tbl.insertRow();
for (var j = 0; j < 2; j++)
{
if (i == 2 && j == 1) { break; }
else
{
var td = tr.insertCell();
td.appendChild(document.createTextNode('Cell'));
td.style.border = '1px solid black';
if (i == 1 && j == 1) {
td.setAttribute('rowSpan', '2');
}
}
}
}
// Build title for table
var title = document.createElement('h3');
title.innerHTML = "Data mined:";
// Finally, append title and table to body
body.appendChild(document.createElement('hr'));
body.appendChild(title);
body.appendChild(tbl);
}
</script>
</head>
<body>
HiddenField >>>> <asp:HiddenField id="HiddenFieldData" runat="server" value="" />
<h2>Saving route...</h2>
</body>
</html>
Thanks ahead for any help!
I have a view with 1700 records. I want to paginate them using ajax to make page load lighter. I am able to do paging and bring set of new records everytime based on the page selected.
The Problem
I am showing only 10 indexes in page-bottom, the page selected as well as 4 to the left and 5 to the right. Now I need CurrentPage value which I send everytime from jQuery/ajax to controller which I get as a ajax data parameter. The problem is in getting back Current page value persistent to view when the next page index I select. I always get the old value and not the last selected page value. I have even used ViewBag instead of tempData but no success.
View Code:
#model IEnumerable<UrCompedDAL.DBModels.SlotMachineModel>
<div class="my-properties">
<table id="tbl_slots" class="table no-margin" data-search="true" data-pagination="false">
<tbody class="orgTbody">
#foreach (var item in Model)
{
<tr>
//Code for Slot list
</tr>
}
</tbody>
</table>
<ul class="paging">
#{
int i = 1;
int pg = Convert.ToInt32(TempData["Current"]);
if (i > 0 || i == ViewBag.PageSize)
{
<li>
<<
</li>
}
if (pg < 6)
{
for (i = 1; i < 11; i++)
{
<li>
#i
</li>
}
}
else
{
for (i = pg - 4; i < pg; i++)
{
<li>
#i
</li>
}
for (i = pg; i < pg + 6; i++)
{
<li>
#i
</li>
}
}
if (i > 1 || i < ViewBag.PageSize)
{
<li>
>>
</li>
}
}
</ul>
</div>
<script>
$(document).ready(function () {
$('.lipaging').click(function () {
$("#loadingDiv").show();
$(this).addClass('active');
var pageThis = $(this).text();
var current = #TempData["Current"];
if (pageThis == '>>') {
pageThis = current +1;
}
if (pageThis == '<<') {
pageThis = current -1;
}
$.ajax({
type: 'get',
url: '#Url.Action("Index", "Game_SlotMachine")',
data: {
CurrentPage: pageThis
}
}).done(function (data) {
var startIndex = data.indexOf("<tbody");
var endIndex = data.indexOf("</tbody>");
var html = data.substring(startIndex, endIndex + 8);
$('#tbl_slots').html('');
$('#tbl_slots').html(html);
setTimeout(function () {
filter();
}, 300);
$("#loadingDiv").hide();
});
});
Controller Code:
public ActionResult Index(int id = 0, int CurrentPage = 1)
{
List<SlotMachineModel> slotmodel = new List<SlotMachineModel>();
slotmodel = UrCompedDAL.DataAccessor.Instance.GameAccessor.GetAllSlotMachines().ToList();
ViewBag.PageSize = slotmodel.Count / 10;
TempData["Current"] = CurrentPage;
slotmodel = slotmodel.Skip((CurrentPage - 1) * 10).Take(10).ToList();
return View(slotmodel);
}
Please help.
Pack your model IEnumerable<UrCompedDAL.DBModels.SlotMachineModel> into other model and set your model as a property of new model. Pass this new model as a model for your view. You will be able to pass as many data from controller as you like.
The issue is that you're re-creating the whole view, but only updating the table from the result
var html = data.substring(startIndex, endIndex + 8);
$('#tbl_slots').html('');
$('#tbl_slots').html(html);
(you should also reconsider how you extract the table from the view and use a partial instead).
None of the ajax/2nd+ rendering of the paging ul is used and is "thrown away" each time.
You can either overwrite your ul in the same way as the table or update the paging element via javascript (probably the former).
Reusing the whole view (rather than a partialview), you'd get something like:
}).done(function (data) {
var html = $("<div/>").html(data);
$('#tbl_slots').html(html.find("#tbl_slots").html());
$('ul.paging').html(html.find("ul.paging").html());
I'm trying to implement this algorith in a View page using Razor, but, it does not display the expected result and I don't get any compilation errors. Any suggestion please ?
Edit : I apologize I was not very clear, I confess. My problem is that I do not understand why ViewBag.NbrePages is equal to 0. However, the database had been filled.
Action();
[HttpGet]
public ActionResult Rechercher(string rech, string type, int num = 1)
{
int nbLignesDepassees = 10 * (num - 1);
ViewBag.Recherche = Server.HtmlEncode(rech);
ViewBag.Type = Server.HtmlEncode(type);
ViewBag.NumPgeCourrante = num;
if (type == "nomAppMetier")
{
var appsMetiers = _db.AppsMetiers
.Where(x => SqlFunctions.PatIndex("%" + rech + "%", x.nomApplication) > 0)
.OrderBy(x => x.nomApplication)
.Skip(nbLignesDepassees)
.Take(10);
ViewBag.NbrePages = (int)(appsMetiers.Count() / 10) ;
return View("RechercheAppsMetiers",appsMetiers);
}
if (type == "nomPoste")
{
var postes = _db.Postes
.Where(x => SqlFunctions.PatIndex("%" + rech + "%", x.nomPoste) > 0)
.OrderBy(x => x.nomPoste)
.Skip(nbLignesDepassees)
.Take(10);
ViewBag.NbrePages = (int)(postes.Count() / 10);
return View("RecherchePostes", postes);
}
return HttpNotFound();
}
View();
<ul>
#{
for (int i = 0; i < ViewBag.NbrePages; i++)
{
if(i==1 || i==2 || i==3){
<li class="disabled">&maquo;</li>
}else{
<li>«</li>
}
if (i == ViewBag.NumPgeCourrante)
{
<li class="active">#i <span class="sr-only">(current)</span></li>
}
else
{
<li>#i </li>
}
if(i==ViewBag.NbrePages || i==ViewBag.NbrePages-1 || i==ViewBag.NbrePages-2){
<li class="disabled">»</li>
}else{
<li>»</li>
}
}
}
</ul>
Thanks a lot !
Rather than having so much logic in the view, consider the following:
A model
public class PagesModel
{
public int NumberOfPages { get; set; }
public int CurrentPage { get; set; }
}
A helper method in a class
public static class Helpers
{
public static bool GetClassNames(int page, int totalPages, int currentPage)
{
var classNames = new List<string>();
var isWithinFirstOrLastThree = page <= 2 || page >= (totalPages - 2);
if (isWithinFirstOrLastThree)
{
classNames.Add("disabled");
}
if (page == currentPage)
{
classNames.Add("active");
}
return string.Join(" ", classNames.ToArray());
}
}
And then your view could be as simple as
#model PagesModel
#for (int i = 0; i < Model.NumberOfPages; i++)
{
<li class="#Helpers.GetClassNames(i, Model.NumberOfPages, Model.CurrentPage)">
&maquo;
#i
</li>
}
This doesn't exactly match what you are trying to achieve, but I hope that it is helpful nonetheless.
NbrePages will be either 0 or 1 (if you have more that 10 records) due to Take(10) and using integer division:
ViewBag.NbrePages = (int)(appsMetiers.Count() / 10) ;
So most likely you get less that 10 items in appsMetiers.
Suggestion to improve source based on original misspelling of the variable in CSHTM:
Using good names or strongly typed model would help to avoid spelling like NbrePges in for condition in original post:
for (int i = 0; i < ViewBag.NbrePages; i++)
CSHTML files are not compiled till run-time access, so no compile errors. Since ViewBag allows any property to be used you are not getting any intellisense warning either.
Instead of ViewBag consider some strongly typed model or at least put strongly typed object for paging into ViewBag:
class PagingState
{
public int NumberOfPages { get;set;}
public int CurrentPage { get;set;}
}
and in view:
var pageingState = (PagingState)(ViewBag.Paging);
for(int I = 0; i < pageingState.NumberOfPages; i++)...