Paging data in MVC3 AJAX - c#

For the site I'm currently working on, I have a list of products which I need to display in a paged list. The list needs to be used on several different pages, each of which has their own rules for how to retrieve their list of products. The list pages need to refresh with AJAX. I'm using LINQ-2-SQL to talk to the database, and MVC3/Razor as the view engine.
So far so good.
What I need help with is figuring out how to implement this. I'll explain what I've done so far, and what isn't working, and I hope someone can give me some direction of the best way to get this working, whether it be bug fixes, missing options, or a redesign. Note that the setup described above is immutable, but everything else can be altered.
For the first set of data, I have Index.cshtml. This will be a list of all products. There will be other sets (such as a list of all products in a category, but I can do the selection for that just fine), but this is the primary test case.
Currently, I have an object to represent the state of the grid: PagingData. The details of it aren't really important, but it takes an IEnumerable when first instantiated, it stores itself in HttpContext.Current.Session between requests, and it has a function that returns an IEnumerable of the products that are supposed to be on the current page. I tried it as an IQueryable<>, but that didn't work.
Currently, I am getting an IQueryable.ToList() and setting it as the data for a DataPager that's used as the Model of a Partial view called _ProductList.cshtml. _ProductList primarily consists of a pager control (another partial) and a foreach loop across the Model to display a partial for each Product.
_ProductList.cshtml:
#model PagingData
<script type="text/javascript">
$(function() {
$('#productList a.pagerControl').live('click', function() {
$('#productList').load(this.href);
return false;
});
</script>
<div id="productList">
#Html.Partial("_Pager", Model)
#foreach (var item in Model.ProductsOnPage)
{
#Html.Partial("_ProductListGridDetail", item);
}
</div>
_Pager uses: #Html.ActionLink(page.ToString(), "_ProductListSetPage", new { newPage = page }, new { #class = "pagerControl" }) to provide the links to change pages (the page variable is the number of the page to draw, from a loop).
This solution works, kindof. The problem I'm having with it is that the only way to update the PagingData with the new page is via a Controller, and each method of modifying the pager (page, # of products per page, format, sort) will need its own controller because I can't overload them. This also means _Pager produces URLs like http://localhost:52119/Product/_ProductListSetPage?newPage=3 instead of http://localhost:52119/Product.
I've tried Ajax.ActionLink(), and wrapping the whole thing in an Ajax.BeginForm(), but neither seemed to work at all. I do have the jquery.unobtrusive-ajax.js library included.
Is this approach feasible? Should I replace the PagingData object with something else entirely? I do not want the paging data in the URL if it's at all possible to avoid it.

If you don't want the page in the url you could use a <form> instead of link, like this:
#using (Html.BeginForm("Index", "Product")
{
#Html.Hidden("newPage", page)
<input type="submit" value="#page" />
}
Which should generate a form for each page with a hidden field containing the actual page number, for example:
<form action="/Product" method="post">
<input type="newPage" value="3" />
<input type="submit" value="3" />
</form>
Now all that's left is to AJAXify this form:
$(function() {
$('#productList form').live('submit', function() {
$.post(this.action, $(this).serialize(), function(result) {
$('#productList').html(result);
});
return false;
});
});
Which would invoke the Index action on ProductController:
public ActionResult Index(int? newPage)
{
var model = ... fetch the products corresponding to the given page number
return PartialView(model);
}

Related

How to Check if Page is Being Displayed as a Result of Form Submission or by Other Means

I have a listing page (Index) displaying a list of items in a grid. The page consists of a small form at the top and a grid (list) at the bottom. The form serves as a way to filter the items shown in the list.
I need to check if the page is being displayed as a result of submitting the form (clicking any of the 2 buttons) or by clicking a link from another page or by entering the URL directly in the browser's address bar.
The View:
#model MyNameSpace.ViewModels.FooFilterViewModel
#{
ViewBag.Title = "Foo Listing";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>#ViewBag.Title</h2>
#using (Html.BeginForm("Index", "Home", FormMethod.Get))
{
#Html.ValidationSummary(true)
<!-- Field controls used for filtering go here -->
<input id="ClearFilter" type="button" value="Clear Filter" />
<input id="ApplyFilter" type="submit" value="Apply Filter" />
}
<!-- Grid displaying the list of foos goes here -->
The Controller Action:
public ActionResult Index(FooFilterViewModel fooFilterViewModel)
{
// Retrieve all records
IQueryable<Foo> foos = _DBContext.Foos;
if (ModelState.IsValid)
{
if (/* check if coming from form submission */){
// Do something
}
else
{
// Do something else
}
// Code to filter records go here
foos = foss.Where(f => f...........)
}
fooFilterViewModel.Foos = foos;
return View(fooFilterViewModel);
}
Since you're sending your form as GET, as well, all of these methods (submitting the form, following a link, typing the URL in the address bar) are functionally equivalent from the server's point of view. They're all just GET requests for the same URL.
If you just need to differentiate the form submit, you can either add a hidden field or simply name your submit button. Either way, you can then inspect the Request object for this name, and if it exists, you can assume the form was submitted.
<button type="submit" name="FormSubmitted">Submit</button>
Then:
if (Request["FormSubmitted"] != null)
{
// form was submitted
}
However, this can be easily faked. For example, someone could simply type in the URL, http://foo.com/?FormSubmitted, and there'd be no way for you to know. I'm not sure how much a concern malicious users may be in this scenario, but you could somewhat mitigate that by making what you look for more obscure, so it's not as obvious as "FormSubmitted". Or, you could use JavaScript to set something in the form, and then bury that in a minified external file perhaps. Still, security through obscurity is still not security.
Short of that, though, there's no way to tell. Again, all of these methods look exactly the same to a server. In order to differentiate the method, the actual request needs to be different in some way, such as altering the query string, send as POST rather than GET, etc. Otherwise, if it's exactly the same request, the server doesn't know or care how it came about.

Pass data from one view to another view -MVC

What would be the best method to transfer data from one view to another view? or is this bad practice?
The reason is that i want to display #model.count() from one view to the homepage, basically a summary of all the views on the homepage. There is tempdata, html.helper etc but id like to know the best and most reliable method. Thus avoiding problems further on down the line
here is the code in question
product.cshtml
<p>Total amount of Products = #Model.Count()</p>
i basically want that same value to display on the homepage
You'd have to refresh the homepage for the value to actually 'update.' At that point you may as well pass the value back in as a parameter, but I think that's not what you're hoping for.
It probably isn't the cleanest solution, but I would think you'd want your text on the main page to be settable via some javascript/jquery.
Main Page has this snippet:
<form id="PassBackVals"
<input type="hidden" name="ProductCount" value="0">
</form>
<script>$( ".ProductCount" ).change(function() {
$("$CurrentProductCount").text = $(".ProductCount").val;
});
</script>
and the Area displaying the text itself on the main would have something along the lines of
<p> I'm currently showing <div id="CurrentProductCount">0</div> Products </p>
Then in your product.cshtml, you'd have
<script>$(".ProductCount").val = #Model.Count()
$( ".ProductCount" ).change();
</script>
Ok i looked into other methods and resolved the issue by moving the code behind the viewbag into a public method below
[OutputCache(NoStore = true, Location = OutputCacheLocation.Client, Duration = 10)]
public ActionResult UpdateTotals()
{
JobController j = new JobController();
ViewBag.JobCount = j.JobCount();
ProductController p = new ProductController();
ViewBag.ProductCount = p.ProductCount();
return PartialView("UpdateTotals");
}
then i added the viewbags into a partial view and using setinterval() javascript,
<div id ="UpdateTotals">
#{ Html.RenderAction("UpdateTotals");}
</div>
<script>
setInterval("$('#UpdateTotals').load('/home/UpdateTotals')", 10000);
</script>
i was able to refresh the data constantly to my desire without the mess of pulling data through views via javascript.
hopefully this will help anyone in future :)

Bizzare behavior of MVC Action/RenderAction, ajax post, and Partial Views

So the design ideal is to have one page with a couple different 'widgets' in this MVC app. Each 'widget' should be able to submit information back to itself and reload only itself. Simple in web forms, not so much with MVC it seems
First, we have our main page controller, nothing special
public ActionResult Index()
{
return View();
}
Next, we have our main page View. Note that I have tried both Action and RenderAction and there is no change in behavior
#Html.Action("Index", "Monitor", new { area = "Statistics" });
<div id="messages">
#Html.Action("Index", "Messages", new { area = "Data"});
</div>
#section Script {
<script src="#Url.Content("~/Scripts/jquery-1.9.1.min.js")" type="text/javascript"></script>
<script type="text/javascript">
$('#filter').click(function () {
$.ajax({
method: 'POST',
url: '#Url.Action("Index", "Messages", new { area = "Data"})',
success: function(data){
$('#messages').html(data);
}
});
return false;
});
</script>
The Index ActionResult in the Messages Controller:
public ActionResult Index()
{
var model = GetMessages();
return PartialView(model);
}
For the sake of brevity, going to skip the whole of Monitor Index View, and only give a brief version of Messages Index View
#using (Html.BeginForm("Index", "Messages", FormMethod.Post))
{
//Some fields to limit results are here, with style classes
<button type="submit" id="filter">Filter</button>
}
#foreach(var item in Model)
{
//Display results
}
Upon loading the main page, all looks good. The fields to limit results are displayed, as well as messages. I can enter something into the fields, click Filter, and am returned to the main page but! ...the fields have lost their style classes and the messages are unfiltered.
Strange, but more strange is if I again enter information in the fields and click Filter, this time I am not taken to the main page, but get only the Partial View of the Messages Index displayed and the messages are not filtered.
I can't say that the filtering not working is related to this issue or not, but the non-consistent behavior of clicking Filter is the part that bothers me. Anyone like to point out what I am doing wrong in here?
You probably should be using Ajax.BeginForm rather than Html.BeginForm in your widgets. That will let the widgets manage their own posts:
<div id="messages">
#using (Ajax.BeginForm("Index", "Messages", new AjaxOptions { UpdateTargetId = "messages" }))
{
// fields content
}
// other widget content
</div>
The "bizarre" behavior you're seeing is happening because on page load the submit event for your #filter button is being hijacked using jQuery, but after the first replacement of the widget content the #filter submit event is no longer being hijacked so when you click it the whole page is submitted. If you don't want to use Ajax.BeginForm you'll need to use $.on rather than $.click to sign up events, as it will handle events for matching elements which are created after the event sign up script has run.

How to populate textfields from a dropdown in MVC 4 with razor and c#?

I am working on a "Challenge Engine" web application that allows users to create challenges and compete with each other. I am using MVC 4 C#, razor syntax.
I am trying to make a page where the user can either create a challenge, or edit the details of a challenge he has already created. The way I want this to work is to have a drop-down menu of the challenges a user owns, a submit button, and two text fields below. When a challenge is selected from the drop-down, the two text fields should populate with the challenge name and the challenge description. I am not sure how to accomplish this, although I think it may involve somehow calling additional code that needs to be put into the controller (although maybe not). It may also require JavaScript, jQuery, and/or AJAX, but my understanding of these technologies is still somewhat shaky. Can anyone help, or point me in the right direction? I have Googled this extensively the past couple of days and searched stack overflow, but couldn't find anyone doing quite what I'm trying to do.
Below is the relevant code I have so far.
The Challenge Model:
public class ChallengeModel
{
public int ChallengeId { get; set; }
public String Name { get; set; }
public String Description { get; set; }
}
The Challenge Controller:
public class ChallengeController
public ActionResult Index()
//Note: UserService and Challenge Service are part of the service layer;
//They simply encapsulate the methods in the various DAOs.
{
ViewBag.CurrentUserName = WebSecurity.CurrentUserName;
ViewBag.CurrentUser = UserService.GetUserByName(ViewBag.CurrentUserName);
ViewBag.OwnedChallenges =
ChallengeService.GetActiveChallengesByParticipant(CurrentUser);
return View();
}
The Challenge View (index.cshtml):
<div class="challengeOwner">
<em>Challenges you own:</em>
<select id="selectOwnedChallenges">
<option>Create new challenge...</option>
#foreach (ChallengeModel Challenge in ViewBag.OwnedChallenges)
{
<option id="#Challenge.ChallengeId"
value="#Challenge.ChallengeId"
onclick=" [[[INSERT CODE HERE THAT WILL POPULATE TEXT FIELDS
WITH CHALLENGE NAME AND DESCRIPTION]]] ">#Challenge.Name</option>
}
</select>
<input id="ChallengeName" type="text" name="Name"
value="#selectedChallenge.Name"/><br />
<input id="ChallengeDescription" type="text" name="Description"
value="#selectedChallenge.Description"/>
<input id="updateChallenge" type="button" value="Submit" />
</div>
As you can see, I thought I would use a variable #selectedChallenge to represent the challenge selected in the drop-down. I don't know how to set it, and I'm thinking this is probably not how it should be done, but I left it there for now so you can see my intention. Any help would be greatly appreciated.
Thanks in advance!
You can use data attributes to hold the name and description of each option.
<option value="#Challenge.ChallengeId"
data-name="#Challenge.Name"
data-description="#Challenge.Description">
#Challenge.Name</option>
Then use jQuery to update the input fields when the select changes:
// this runs when the DOM is finished loading http://api.jquery.com/ready/
$(document).ready(function() {
// wire up the change handler http://api.jquery.com/change/
$("#selectOwnedChallenges").change(function() {
// get the selected option's data values http://api.jquery.com/data/
var data = $("#selectOwnedChallenges option:selected").data();
// set the inputs
$("#ChallengeName").val(data.name);
$("#ChallengeDescription").val(data.description);
});
});
You need to use javascript for this. Its fairly easy. Here is a quick function i wrote that would do what you need.
<script type="text/javascript">
function displaySelectedChallenge(name, description)
{
$("#ChallengeName").val(name);
$("ChallengeDescription").val(description);
}
</script>
Then call this function after the selection is made:
displaySelectedChallenge(#Challenge.Name, #Challenge.Description)

Asp.net mvc3 periodic refresh of results without reloading the page

I'm using asp.net MVC3 for a website that displays in a view a query result (managed in the controller) using a foreach.
What I want to do now is to automatically refresh the output of the query every tot time, without refreshing the page.
How can I do that using ajax?
This is the code of the View:
#{
string firstTime = "";
}
#foreach( var database in Model)
{
if (!(firstTime == database.DB))
{
<h3> #database.DB </h3>
}
<div class="logContainer" onclick="location.href='/logs/Details?databaseID=#database.DB&exceptionName=#database.Exception&exceptionsOccurred=#database.Count';">
<div class="counter"><b>#database.Count</b></div>
<div class="exceptionName"> Exceptions of Type: #database.Exception</div>
<div class="date">Siste: #database.LastOccurred</div>
</div>
<hr />
firstTime = database.DB;
}
You could use the window.setInterval javascript method to send AJAX requests to the server at regular intervals and refresh the corresponding part of the DOM. For example if you wanted to refresh the contents every 10 seconds:
window.setInterval(function() {
$.post('#Url.Action("someaction", "somecontroller")', function(result) {
$('#results').html(result);
});
}, 10 * 1000);
This will send an AJAX request to the controller action which in turn could return a partial view containing the updated results:
pubilc ActionResult SomeAction()
{
SomeViewModel model = ...
return PartialView(model);
}
The result of this partial view will then be injected into some DOM element with id="results".
You could either pass the query result using JSON and render the HTML yourself from javascript, or separate the for-each code to a different partial view, and using jQuery's $.ajax method change the query result's div html with the new response
Why not put your data in to an existing grid control such as DataTables, which is lightweight pretty fast and extensible. Then, using a javascript timer, tell the data table to refresh it's contents.
I've used this with MVC3 with great effect.

Categories

Resources