In my ASP.Net project I'm trying to redirect a page to other action after select a row table and click on a button. So I have this code:
JQuery:
function editItem(tableId, url) {
if ($("#" + tableId + " .selected").exists()) {
var thisRowId = $("#" + tableId + " .selected").attr("id");
window.location.replace(url, { operation: "edit", carId: thisRowId });
}
};
//more code
View (ManagementCar):
#model myProject.Model.Car.Car[]
<table class="table" id="tableCars">
<thead>
#*some code to header*#
</thead>
<tbody>
foreach (var item in Model)
{
<tr id="#(item.Id)" onclick="selectRow('tableCars', '#(item.Id)')">
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.OwnerName)
</td>
<td>
#(item.IsSold == true ? "Yes" : "No")
</td>
</tr>
}
</tbody>
</table>
<br />
<button id="btEdit" type="button" class="disable" onclick="editItem('tableCars','CarOperation')">Edit</button>
Controller:
[HttpGet]
public ActionResult CarOperation(string operation, int carId)
{
//some code...
return RedirectToAction("ManagementCar", "Backoffice");
}
But I have a Server Error after redirect saying carId parameter is null. I debug my jquery and that parameter isn't null. I tried also doing
$.get(url, { operation: "edit", carId: thisRowId });
instead
window.location.replace(url, { operation: "edit", carId: thisRowId });
but it don't redirect.
How can I solve this?
set it by giving it a new value like this.
window.location = window.location.replace(url, "shouldBeAstringNotAJsonObject");
The problem with using window.location is that the referrer is not passed on the request as this behaviour simply mimics a new request as if you had typed the URL into the address bar. If you intend to use website analytics, a reliable referrer will be quite important. I use jQuery to generate a dynamic link upon which I call click().
var url = '/my/url';
$('')[0].click();
Notice I click() the underlying element not the jQuery selected object, the reason being that a jQuery click only raises the event and the href is not navigated to whereas indexing to the element and clicking that will cause the same behaviour you would expect as if you had actually clicked a link on the page.
I have put together a jQuery.navigate plugin that neatly wraps this up and abstracts your site map away from your UI logic, which you might find useful. For example, using my plugin would mean you could remove your editItem function altogether and simply change your markup to something like this?
<tr id="#(item.Id)" onclick="$.navigate('to', 'edit', { id : '#(item.Id)' })">
Ok, I finally solved the problem with a new url routing config and the following code:
My RouteConfig:
routes.MapRoute(
name: "ManipulatingCar",
url: "{controller}/{action}/{operation}/{carId}"
);
My JQuery:
editItem = function (tableId, url) {
if ($("#" + tableId + " .selected").exists()) {
var thisRowId = $("#" + tableId + " .selected").attr("id");
var fullUrl = url + "/edit/" + thisRowId;
window.location = fullUrl;
}
};
Basically, controller action parameters must match with the configurations specified in RouteConfig.cs
Related
I am using x.PagedList to use pagination in my ASP.NET MVC page. The only problem I have with the plugin is , it used a page refresh when I navigate between pages.
To avoid that I am using jQuery calls to replace page contents as explained in this article.
My View and javascript looks like this.
<div id="circuitsContent">
<table class="table">
<tr>
--Header
</tr>
#foreach (var item in Model)
{
<tr>
--Loop through and create content
<td>
#Html.ActionLink("Edit", "Edit", new { id = item.ID }) |
#Html.ActionLink("Details", "Details", new { id = item.ID }) |
#Html.ActionLink("Delete", "Delete", new { id = item.ID })
</td>
</tr>
}
</table>
</div>
<div id="circuitContentPager">
#Html.PagedListPager((IPagedList)Model, page => Url.Action("Circuits", new { page }))
</div>
#section scripts
{
<script language="javascript" type="text/javascript">
$(document).ready(function () {
$(document).on("click", "#circuitContentPager a[href]", function () {
$.ajax({
url: $(this).attr("href"),
type: 'GET',
cache: false,
success: function (result) {
$('#circuitsContent').html(result);
}
});
return false;
});
});
</script>
And this is my controller code:
public ActionResult Circuits(int? page)
{
var pageNumber = page ?? 1;
var circuits = _repo.GetAllCircuits().OrderBy(circ=>circ.ID).ToList();
var pagedCircuits = circuits.ToPagedList(pageNumber, 25);
return View(pagedCircuits);
}
What am I missing here?
Your ajax call returns the html from Circuits() method which is the same view you have used to render the page initially, which includes all the initial html, but you only replacing part of of the existing page, so elements such as the paging buttons generated by the #Html.PagedListPager() method are going to be repeated. Your also generating invalid html because of duplicate id attributes (you will have multiple <div id="circuitsContent"> elements
There are 2 ways you could solve this.
Create a separate controller method that returns a partial view of just the <table> and call that method, however you would need to extract the value of the page number for the href attribute of you pager buttons to pass that as well.
Using your current Circuits() method, test if the request is ajax, and if so, return a partial view of just the <table>.
public ActionResult Circuits(int? page)
{
var pageNumber = page ?? 1;
var circuits = _repo.GetAllCircuits().OrderBy(circ=>circ.ID);
var pagedCircuits = circuits.ToPagedList(pageNumber, 25);
if (Request.IsAjaxRequest)
{
return PartialView("_Circuits", pagedCircuits);
}
return View(pagedCircuits);
}
Note: Do not use .ToList() in your query. That is defeating the whole purpose of using server side paging because .ToList() immediately downloads all the records fro the database.
Where _Circuits.cshtml would be
#model IEnumerable<yourModel>
<table class="table">
<thead>
<tr>
// <th> elements
</tr>
</thead>
<tbody>
#foreach (var item in Model)
{
<tr>
.... // Loop through and create content
</tr>
}
</tbody>
</table>
Note that your header elements should be in a <thead> element and the records in a <tbody> element.
I have question about requests - their count.
Im using Ajax.BeginForm and onSuccess option.
But when I click that form my JS handler for OnSuccess option fires up many times.
I looked up for my request and its looks like this:
Image with number of request
So my question is: why if I click on AjaxForm it makes many request?
Thanks
View with Ajax action link:
#foreach (var item in Model)
{
if (item.Accepted == false)
{
<text>
<tr>
<td>
#Html.DisplayFor(modelItem => item.Accepted)
</td>
<td>
#Html.DisplayFor(modelItem => item.IsOrganizer)
</td>
<td>
#Html.DisplayFor(modelItem => item.PlayerRating)
</td>
<td>
#Html.DisplayFor(modelItem => item.FirstName)
</td>
<td>
#Ajax.ActionLink("Akceptuj", // <-- Text to display
"AcceptPlayer", // <-- Action Method Name
new { id = item.PlayerId },
new AjaxOptions
{
HttpMethod = "POST",
})
</td>
</tr>
</text>
}
}
Controller action:
[Authorize]
public ActionResult AcceptPlayer(long id)
{
using (var Players = new DbMigrationExample2Entities())
{
Player playerToAccept = Players.Player.Find(id);
if (playerToAccept == null)
{
return HttpNotFound();
}
playerToAccept.Accepted = true;
Players.SaveChanges(); return View();
}
If you have included jquery.unobtrusive-ajax.min.js twice once in the layout once in the partial. So your browser executes the js inside twice which will subscribe twice on the form click event that is why doing two POST instead of one.
So you need to remove the jquery.unobtrusive-ajax.min.js from the partial.
Note: If your are using a partial with a layout you don't need to duplicate the js included in the partial because it's already done by the layout. There are some good articles about layouts and partials.
Second think if problem persist then the solution here :
First ajax request is sent multiple times
If nigher this all solution is not working in your case then definitely you need to change your Ajax.ActionLink into Html.ActionLink Like :
#Html.ActionLink(article.Title, new { controller = "Akceptuj", action = "AcceptPlayer", id = item.PlayerId }, new AjaxOptions { HttpMethod = "POST" })
Cheers !!
On one of my pages I have a for loop to iterate through a list of "Projects" (which is the main model for my website) and display some of their data. The following code is nested in a table and the middle cells removed for redundancy.
foreach (var item in Model.Projects)
{
<tr>
<td>#Html.DisplayFor(modelItem => item.SubmissionNumber)</td>
<td>#Html.DisplayFor(modelItem => item.Status)</td>
<!-- and so on -->
<td>#Html.ActionLink("Detail", "DisplayDetails", new { id = item.ProjectID })</td>
</tr>
}
The "Detail" link in the last cell will ideally make a box pop up (I'm thinking of using a Modal via Bootstrap) containing all of the data for the project. The "DisplayDetails" controller action returns a partial view that presents this information, but since I'm not using JavaScript or anything to render the partial view on the current page it renders it as it's own unformatted page. This is the controller action:
[HttpGet]
public ActionResult DisplayDetails(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Project project = db.Projects.Find(id);
if (project == null)
{
return HttpNotFound();
}
return PartialView("~/Views/Projects/_Detail.cshtml", project);
}
Ideally I would pass the ID to the controller using AJAX like I did below (which is code from another page of my website, again edited to remove redudancy):
$("#show").on("click", function () {
$.ajax({
url: '/Projects/SearchTable',
type: "GET",
data: {
Line1: $('#' + '#Html.IdFor(m => m.Project.ProjectAddress.Line1)').val(),
// and so on
County: $('#' + '#Html.IdFor(m => m.Project.ProjectAddress.County)').val(),
}
}).done(function(partialViewResult) {
$(".wrapper").html(partialViewResult);
$(".wrapper").css('display', 'block');
});
});
And by doing this I can embed the partial view onto the current page instead of it opening as a new page. I'm just not sure how to pass the project ID for a specific row in the table as data to the controller. Is this even possible? If not is there another way to achieve the same result?
You can replace your ActionLink with this:
<td>Details</td>
Then,
$(".details").on("click", function (e) {
e.preventDefault();
var projectId = $(this).data('id');
// Make the AJAX call here...
});
I am trying to fix this problem I got an error 404 on the partial view path url: localhost:49259/Panier/TableContent. This TableContent is under the Panier folder.
I can't figure out what is wrong with the URL.
Does the TableContent should be under this folder ViewModels instead since it is using this model
#model Tp1WebStore3.ViewModels.ShoppingCartViewModel?
Thanks
TableContent.cshtml (partial view) from Panier
#model Tp1WebStore3.ViewModels.ShoppingCartViewModel
#{
ViewBag.Title = "Table Content";
}
<a href="#" class="TableContent">
<table>
<tr>
<th>
Produit
</th>
<th>
Prix (unitaire)
</th>
<th>
Quantite
</th>
<th></th>
</tr>
#foreach (var item in Model.CartItems)
{
<tr id="row-#item.ProduitId">
<td>
#Html.ActionLink(item.Produit.Description, "Details", "Produit", new { id =
item.ProduitId }, null)
</td>
<td>
#item.Produit.Prix
</td>
<td id="item-count-#item.PanierId">
#item.Quantite
</td>
<td>
<a href="#" class="RemoveLink" data-id="#item.PanierId"> Enlever du panier
</a>
</td>
</tr>
}
<tr>
<td>
Total
</td>
<td></td>
<td></td>
<td id="cart-total">
#Model.CartTotal
</td>
</tr>
</table>
</a>
Index.cshtml from Panier
#model Tp1WebStore3.ViewModels.ShoppingCartViewModel
#{
ViewBag.Title = "Shopping Cart";
}
<script src="/Scripts/jquery-1.8.2.min.js" type="text/javascript"></script>
<script type="text/javascript">
$(function () {
$('.RemoveLink').click(function () {
$.ajax({
url: '/Panier/RemoveFromCart',
data: { id: $(this).data('id') },
type: 'POST',
cache: false,
success: function (result) {
$('#row-' + result.DeleteId).remove();
$('#row-' + result.DeleteId).fadeOut('slow');
$('#cart-status').text('Cart (' + result.CartCount + ')');
$('#update-message').text(result.Message);
$('#cart-total').text(result.CartTotal);
$.get("/Panier/TableContent").done(function (data) { <==error 404
$("#TableContent").html(data); });
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
alert("Status: " + textStatus); alert("Error: " + errorThrown);
}
});
return false;
});
});
</script>
<h3>
<em>Details</em> du panier:
</h3>
<p class="button">
#Html.ActionLink("Checkout >>", "AddressAndPayment", "Checkout")
</p>
<div id="update-message">
</div>
<div id="table-content">
#Html.Partial("TableContent") <=== partial view call
</div>
PanierController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Tp1WebStore3.Models;
using Tp1WebStore3.ViewModels;
namespace Tp1WebStore3.Controllers
{
public class PanierController : Controller
{
//
// GET: /Panier/
Tp1WebStoreDBEntities dbProduit = new Tp1WebStoreDBEntities();
//
// GET: /ShoppingCart/
public ActionResult Index()
{
var cart = ShoppingCart.GetCart(this.HttpContext);
// Set up our ViewModel
var viewModel = new ShoppingCartViewModel
{
CartItems = cart.GetCartItems(),
CartTotal = cart.GetTotal()
};
// Return the view
return View(viewModel);
}
//
// GET: /Store/AddToCart/5
public ActionResult AddToCart(int id)
{
// Retrieve the album from the database
var addedProduit = dbProduit.Produits
.Single(produit => produit.ProduitId == id);
// Add it to the shopping cart
var cart = ShoppingCart.GetCart(this.HttpContext);
cart.AddToCart(addedProduit);
// Go back to the main store page for more shopping
return RedirectToAction("Index");
}
//
// AJAX: /ShoppingCart/RemoveFromCart/5
[HttpPost]
public ActionResult RemoveFromCart(int id)
{
// Remove the item from the cart
var cart = ShoppingCart.GetCart(this.HttpContext);
// Get the name of the album to display confirmation
string produitDescription = dbProduit.Paniers
.Single(item => item.PanierId == id).Produit.Description;
// Remove from cart
int itemCount = cart.RemoveFromCart(id);
// Display the confirmation message
var results = new ShoppingCartRemoveViewModel
{
Message = Server.HtmlEncode(produitDescription) +
" has been removed from your shopping cart.",
CartTotal = cart.GetTotal(),
CartCount = cart.GetCount(),
ItemCount = itemCount,
DeleteId = id
};
return Json(results);
/* return View("CartSummary"); */
}
//
// GET: /ShoppingCart/CartSummary
[ChildActionOnly]
public ActionResult CartSummary()
{
var cart = ShoppingCart.GetCart(this.HttpContext);
ViewData["CartCount"] = cart.GetCount();
return PartialView("CartSummary");
}
}
}
You'll want to keep your partial view inside the view folder (not view models)... the specific subdirectory is up to you but I believe it will default to looking in shared.
I'd suggest specifying more of the URL when you call the partial view and see if that fixes your issue...
For Example:
Html.Partial("~/Views/Shared/TableContent.cshtml")
More Info Here:
Render partial from different folder (not shared)
Ok I realize I misunderstood the question and the 404 was being generated by the script call not the #Html.Partial at the bottom of the file.
I'm going by this snippet from the comments...
$.get("~/Views/Shared/TableContent").done(function (data) { $("#TableContent").html(data); });
I see two issues here... First is that you are going to want to resolve your path differently as the "~" operator isn't going to help in javascript. You can use #url.Action("TableContent") in this case to get the actual url on the server and pass it in to your get statement.
The second issue is that I believe TableContent is just a view without a corresponding action. This is fine for rendering inline with a Html.Partial, however the server isn't going to be able to render it outside of that context. You'll want to add a corresponding action and call that instead from your ajax.
Your controller, Panier, doesn't seem to have a method called TableContent which should return the partial view TableContent.cshtml.
Also, when you refer the urls, try to use the Url.Action in the ajax / get calls:
url: '/Panier/RemoveFromCart',
Should be:
url: '#Url.Action("RemoveFromCart","Panier")',
Another way of solving this is to create an action TableContent in the Panier Controller which would call the partial view TableContent. The TableContent.cshtml should be in the same directory as Index (ie /Views/Panier)
public PartialViewResult TableContent()
{
return PartialView("TableContent");
}
You'll then need to replace a line in the ajax call
$.get("/Panier/TableContent")
becomes
$.get('#Url.Action("TableContent", "Panier")')
I'm developing an ASP.NET MVC4 application and have started using jQuery actionlinks.
However when I run the following Razor code (and click the view ticket actionlink) I get a generic jQuery error (twice) saying that an empty string was passed to getElementById().
I have no idea where this error is happening since firefox merely links to the jQuery code.
This is my Razor code: (I know the js functions show and hideticket are empty but that is to simplify the code):
<script>
function ShowTicket(id) {
$("#viewTicketButton" + id).hide();
$("#hideTicketButton" + id).show();
$("#viewTicket").show();
}
function HideTicket(id) {
$("#viewTicketButton" + id).show();
$("#hideTicketButton" + id).hide();
$("#viewTicket").hide();
}
</script>
<h3>Your tickets</h3>
<table border="1">
<tr>
<td>Title:</td>
<td>Urgency:</td>
<td>Status:</td>
</tr>
#foreach (SupportTicketViewData t in Model.supportTicketViewDataList)
{
<tr>
<td>#t.title</td>
<td>#t.text</td>
<td>#t.status</td>
<td>#Ajax.ActionLink("View Ticket", "ViewTicket", new { id = t.id },
new AjaxOptions
{
HttpMethod = "GET",
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "viewTicket",
OnComplete = "ShowTicket(" + t.id +");"
}, new { id = "viewTicket" + t.id })</td>
<td><button id="#Html.Raw("HideTicket" + t.id)" onclick="HideTicket(#t.id);">Hide Ticket</button></td>
</tr>
}
</table>
<div id="viewTicket">
</div>
Also I get a result from the GET request just fine since it get's inserted into the div element however I get 2 errors when debugging in firefox.
Also when I click the viewTicketButton the button doesn't hide as it should.
Warnings 'Empty string passed to getElementById()' occurs when sending form created via Ajax.BeginForm or Ajax.ActionLink with unobtrusive validation turned on.
In my case adding handlers to all events supported by Ajax.BeginForm fixed issue with warnings:
#using (Ajax.BeginForm(“SomeAction”, null, new AjaxOptions() {
OnBegin = “someFunction”,
OnComplete = “ShowTicket”,
OnFailure = “someFunction”,
OnSuccess = “someFunction”
}
....
I believe that this should fix your issue.
More details about issue on my blog post.
I believe you cannot simply do
OnComplete = "ShowTicket(" + t.id +");"
The argument must be a javascript function. If what you want to call is parameterless, you can do
OnComplete = "ShowTicket"
where show ticket is the function object, so this is fine.
In your case however, you've got to pass the ID to ShowTicket. Try the following:
OnComplete = "function() { ShowTicket(" + t.id +"); }"
You will likely have to add the slashes to compensate for the double quotes that you need in the id tag
eg:
Html.Raw("id=\"SomeIdString\"")