Reputation: 23833
Edit: I have read and implemented the method oulined in this article but this does not work.
I have the following table which is populated correctly.
<table class="table">
<tr>
<th>Product</th>
<th>File Name</th>
<th>Release Date</th>
<th>Size</th>
<th></th>
</tr>
@{
if (Model != null)
{
foreach (var item in Model.Uploads)
{
<tr>
<td>@Html.DisplayFor(modelItem => item.Product)</td>
<td>@Html.DisplayFor(modelItem => item.DisplayName)</td>
<td>@Html.DisplayFor(modelItem => item.ReleaseDate)</td>
<td>@Html.DisplayFor(modelItem => item.Size)</td>
<td>@Html.ActionLink("Delete File", "Upload", "Tools", new { id = item.DisplayName }, null)</td>
</tr>
}
}
}
</table>
I also have a controller with the action
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> DeleteUser(
AdministratorViewModel model, string userName)
{
// Do amazing stuff...
return Index();
}
I want to pass to this action the username that was selected for deletion. I thought I could achive this with @Html.ActionLink
as above, but this is not the way to go.
How can I pass the selected username that was selected to my action method?
Thanks for your time.
Edit: changing the Ajax code to the following (using Index
as the called method)
@Ajax.ActionLink(
"Remove",
"Index",
"Tools",
new
{
model = item,
userName = item.UserName
},
new AjaxOptions
{
InsertionMode = InsertionMode.Replace,
HttpMethod = "POST"
})
and changed the name of the DeleteUser(AdministratorViewModel model, string userName)
method to Index(AdministratorViewModel model, string userName)
. This now fires the Index
method in the ToolsController
. But the method called is the NON-POST attributed method(!?) so I now have two questions:
[HttpPost]
attribute?DeleteUser
method using Ben Griffiths' answer below?DeleteUser
method in the Tools controller and pass in the model and the name of the user I want to delete?Thanks for your time.
Upvotes: 1
Views: 11162
Reputation: 1696
The fourth argument to the overload of the ActionLink extension method that you are using when you call
Html.ActionLink("Delete File", "Upload", "Tools", new { id = item.DisplayName }, null)
is for the route values
parameter, which you can use to pass data back to your controller.
Currently, in the above call, the Upload
action method on the Tools
controller will be receiving an id
argument. If you want to pass a display name back to your DeleteUser action method, you could use
Html.ActionLink("Delete User", "DeleteUser", "[ControllerName]", new { userName = item.DisplayName }, null)
However, the DeleteUser
method is decorated with the HttpPost
attribute, meaning that the action will only accept requests that use the post method. This means that you have three options:
(1) Remove the [HttpPost] attribute from the action method - probably NOT a good idea, since I imagine you don't want to be exposing this action to get requests for good reason.
(2) Use a form containing a submit input and a hidden DisplayName input instead of a link.
(3) Use the AjaxHelper.ActionLink extension method to make an asynchronous postback to your controller. e.g.:
Ajax.ActionLink("Delete User", "DeleteUser", "[ControllerName]", new { userName = item.DisplayName }, new AjaxOptions{ HttpMethod = "Post" })
Here's a working (albeit very simple) example of the third option. I'm not 100% sure what the ultimate goal is, so rather than try to provide a realistic example I've tried to create one that is simple but clear. Note that I've omitted the handling of the anti forgery token to aid in this clarity, but I've stuck with an async
action method so as not to deviate too far from real life.
Controller:
public class HomeController : Controller {
private async Task<string> Delete(string displayName) {
Thread.Sleep(1000);
return string.Format("{0} has been deleted", displayName);
}
[HttpPost]
[AllowAnonymous]
public async Task<JsonResult> DeleteItem(string displayName, int product) {
Task<string> deleteTask = Delete(displayName);
return new JsonResult() {
Data = new {
product = product,
result = await deleteTask }
};
}
public ActionResult Index() {
AdministratorViewModel model = new AdministratorViewModel() {
Uploads = new List<ItemModel>() {
new ItemModel() {
DisplayName = "First one",
Product = 1,
ReleaseDate = DateTime.Now,
Size = 11
},
new ItemModel() {
DisplayName = "Second one",
Product = 2,
ReleaseDate = DateTime.Now.AddDays(1),
Size = 12
}
}
};
return View(model);
}
}
Model:
public class AdministratorViewModel {
public IEnumerable<ItemModel> Uploads { get; set; }
}
Layout:
<!DOCTYPE html>
<html>
<head>
<title>Demo</title>
</head>
<body>
@RenderBody()
<script src="~/Scripts/jquery-1.10.2.js"></script>
<script src="~/Scripts/jquery.unobtrusive-ajax.js"></script>
@RenderSection("scripts", false)
</body>
</html>
Home/Index view:
@model AdministratorViewModel
<table class="table">
<tr>
<th>Product</th>
<th>File Name</th>
<th>Release Date</th>
<th>Size</th>
<th></th>
</tr>
@{
if (Model != null) {
foreach (var item in Model.Uploads) {
<tr>
<td>@Html.DisplayFor(modelItem => item.Product)</td>
<td>@Html.DisplayFor(modelItem => item.DisplayName)</td>
<td>@Html.DisplayFor(modelItem => item.ReleaseDate)</td>
<td>@Html.DisplayFor(modelItem => item.Size)</td>
<td>@Ajax.ActionLink("Remove", "DeleteItem", "Home", new { displayName = item.DisplayName, product = item.Product }, new AjaxOptions { HttpMethod = "POST", OnComplete = "itemDeleted" }, new { id = item.Product })</td>
</tr>
}
}
}
</table>
@section scripts {
<script>
var itemDeleted = function (data) {
var $link = $('#' + data.responseJSON.product);
$link.parents('tr')
.children()
.css('text-decoration', 'line-through');
$link.remove();
alert(data.responseJSON.result);
};
</script>
}
Upvotes: 5
Reputation: 469
I think first you have to handle the url by adding routing like
routes.MapRoute(
name: "Default",
url: "{User}/{DeleteUser}/{username}",
defaults: new { controller = "Home", action = "Index", username = UrlParameter.Optional }
);
Upvotes: 1