I have a PaartialView declared like this:
#model IEnumerable<mvc1.Models.ProjectDetailModel>
#using (Html.BeginForm())
<form method="get" action="EditProject" enctype="multipart/form-data">
<br />
<fieldset>
<legend>Project Detail</legend>
#foreach (var item in Model)
{
<tr>
<th class="thdetail">
Project Code
</th>
<td class="tddetail">
#Html.DisplayFor(modelItem => item.projectCode)
</td>
<tr>
<th class="thdetail">
Project Name
</th>
<td class="tddetail">
#Html.DisplayFor(modelItem => item.projectName)
</td>
</tr>
<tr>
<th class="thdetail">
Project Type
</th>
<td class="tddetail">
#Html.DisplayFor(modelItem => item.projectType)
</td>
</tr>
<tr>
<th class="thdetail">
Detailed Description
</th>
<td class="tddetail">
<div style="height: 100px; width:700px; overflow: scroll">
#Html.DisplayFor(modelItem => item.projectDescription)
</div>
</td>
</tr>
</table>
</fieldset>
<input type="submit" value="Edit" />
</form>
}
On the submit button, i am calling a controller, but when it goes to the controller the model is not being passed back to the controller. How can i get the model back to the controller, or even just 1 field, ie. Model.projectCode which is the primary key
In the controller i have the fll which takes in the model and gets the primary key and calls a stored procedure to return results to another VIEW()
[HttpGet]
public ActionResult EditProject(ProjectDetailModel model)
{
DBController dbcontroller = new DBController();
string l_user_name = SessionBag.Current.UserName;
Int64 l_project_code = model.projectCode;
if (dbcontroller.DBConnection())
{
MySqlCommand command = new MySqlCommand("edit_projects", dbcontroller.conn);
command.CommandType = System.Data.CommandType.StoredProcedure;
// Input parameters for the insert_projects STORED PROC
command.Parameters.Add(new MySqlParameter("userName", SessionBag.Current.UserName));
command.Parameters["#userName"].Direction = System.Data.ParameterDirection.Input;
// Output parameters for the view_sr_projects_detail STORED PROC
command.Parameters.Add(new MySqlParameter("projectName", MySqlDbType.LongText));
command.Parameters["#projectName"].Direction = System.Data.ParameterDirection.Output;
command.Parameters.Add(new MySqlParameter("projectType", MySqlDbType.LongText));
command.Parameters["#projectType"].Direction = System.Data.ParameterDirection.Output;
command.Parameters.Add(new MySqlParameter("projectDescription", MySqlDbType.LongText));
command.Parameters["#projectDescription"].Direction = System.Data.ParameterDirection.Output;
try
{
MySqlDataReader rdr = command.ExecuteReader();
var model1 = new ProjectDetailModel();
while (rdr.Read())
{
model1.projectCode = (Int64)(rdr["projectCode"]);
model1.projectName = rdr["projectName"].ToString();
model1.projectType = rdr["projectType"].ToString();
model1.projectDescription = rdr["projectDescription"].ToString();
}
dbcontroller.conn.Close();
return View(model1);
}
catch (MySql.Data.MySqlClient.MySqlException ex)
{
dbcontroller.conn.Close();
ViewBag.Message = "Could not view your detail project. Error " + ex.Number + " has ocurred. Please try again or contact the system administrator.";
return View("Error");
}
}
else
{
ViewBag.Message = "Could not connect to the database. Please try again or contact the system administrator";
return View("Error");
}
}
My model looks like this:
public class ProjectDetailModel
{
[Display(Name = "Project Code")]
public Int64 projectCode { get; set; }
[Display(Name = "User Name")]
public string srUserName { get; set; }
[Display(Name = "Project Name")]
public string projectName { get; set; }
[Display(Name = "Project Type")]
public string projectType { get; set; }
[Display(Name = "Project Requirement")]
public string projectDescription { get; set; }
}
public class ProjectDetailModelList : List<ProjectDetailModel>
{
}
thanks
Naren
Here this is what you can do. Below is just partial code, modify it to fit in with your example:
Code in your view:
<button id="btnEdit" type="button">Edit</button>
I then use jQuery to add a click listener to the button (make sure that jQuery is added to the view):
<script>
$(document).ready(function () {
$('#btnEdit').click(function () {
window.location = '#Url.RouteUrl(new { action = "EditProject", projectCode = Model.projectCode })';
});
});
</script>
Your action method:
public ActionResult EditProject(int projectCode)
{
// Retrieve this specific project using this code
// Do what needs to be done to populate the required input fields on view
}
This is the best way that I have found to do it.
When MVC calls your controller.EditProject(ProjectDetailModel) action the binding system tries to create a ProjectDetailModel object from the data posted to the page (or in the query string or route data, etc.). In order for it to create and populate a ProjectDetailModel for you, you must have the following:
A parameterless constructor on ProjectDetailModel
A publically-settable property with a matching value in the form data
So if ProjectDetailModel looks like this:
public class ProjectDetailModel
{
public ProjectDetailModel()
{
}
public int ProjectId
{
get;
set;
}
}
...you can have it populated by MVC by posting this form:
<form method="get" action="EditProject" enctype="multipart/form-data">
<input type="hidden" id="ProjectId" name="ProjectId" value="123" />
</form>
Edit
Looking at the extra code you've added to the question, you have a few things to sort out:
#using (this.Html.BeginForm()) will render an open form tag - as you've manually written one you don't need that line.
Using this.Html.DisplayFor() will just write the property value to the screen; if you want it to be populated into a parameter in your action, you need to provide the values to the binding system. You can do this by using EditorFor() instead of DisplayFor(), which will cause them to be included in the request as it is sent to the action.
Related
I am facing a bit of problem editing and deleting rows from SQL in ASP.NET MVC. The main problem I am facing is the fetch the QueryString value from url in my controller.
I can't attach Request.QueryString["Username"] for some reason. It is giving me Can not apply indexing with [] to an expression of type QueryString error.
Is there something I am doing it wrong or is there any way I can pass the QueryParameter through my controller to my Data access layer, where it will execute the desired query.
I am having the same problem with both Edit and Delete
This is my code looks like so far:
ShowUsers.cshtml (Edit and Delete actions are hooked up here)
#model IEnumerable<SecureMedi.Models.Users>
<section id="users">
<table id="users-table" class="table table-bordered table-responsive">
<thead>
<tr>
<th class="sort">Username</th>
<th class="sort">Role</th>
<th class="sort"><i class="md-icon dp18">mode_edit</i></th>
<th class="sort"><i class="md-icon dp18">delete</i></th>
</tr>
</thead>
<tbody class="list">
#foreach (var item in Model) {
<tr>
<td class="username">#item.Username</td>
<td class="role">#item.Role</td>
<td class="actions">#Html.ActionLink("Edit", "EditUser", "Home", new { Username = #item.Username }, null)</td>
<td class="actions">#Html.ActionLink("Delete", "DeleteUser", "Home", new { Username = #item.Username }, null)</td>
</tr>
}
</tbody>
</table>
</section>
EditUser.cshtml (For Editing) - Note: Here I would also like to display the Username value by default from the QueryString (Username=somename) in the form when the page loads. When going to EditUser action from ShowUsers page the url looks something like /Home/EditUser?Username=somename.
#model SecureMedi.Models.Users
<section id="edit-user">
<h4 class="sub-title space-top space-bottom">Edit user</h4>
<form id="edit-user-form" asp-controller="Home" asp-action="EditUser" method="post">
<div class="form-group">
<label asp-for="Username" class="form-control-label">Username</label>
<input asp-for="Username" type="text" class="form-control" id="username" name="Username" required disabled value="" />
</div><!-- / form-group -->
<div class="form-group">
<label asp-for="Role">Role</label>
<select asp-for="Role" class="form-control selector" id="role" name="Role" required>
<option value="" selected disabled>Select role</option>
<option value="SecureMediUsers">SecureMediUsers</option>
<option value="SecureMediModerators">SecureMediModerators</option>
<option value="SecureMediAdministrators">SecureMediAdministrators</option>
</select>
</div><!-- / form-group -->
<button type="submit" class="btn btn-primary">Save</button>
</form><!-- / form -->
</section>
HomeController.cs
[HttpGet]
public IActionResult EditUser() {
var model = new Users();
return View(model);
}
[HttpPost]
public IActionResult EditUser(Users u) {
if (!ModelState.IsValid) {
return View(u);
}
UsersDAL ud = new UsersDAL();
ud.Edit(u);
return RedirectToAction("ShowUsers");
}
[HttpPost]
public IActionResult DeleteUser(Users u) {
if (!ModelState.IsValid) {
return View(u);
}
UsersDAL ud = new UsersDAL();
ud.Delete(u);
return RedirectToAction("ShowUsers");
}
UsersDAL.cs (Data access layer)
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using SecureMedi.Models;
namespace SecureMedi.DAL {
public class UsersDAL {
public void Edit(Users u) {
string connectionstring = "MY_CONNECTION_STRING";
string sql = String.Format("ALTER ROLE {0} DROP MEMBER {1}", u.Role, u.Username);
string sql2 = String.Format("ALTER ROLE {0} ADD MEMBER {1}", u.Role, u.Username);
SqlConnection conn = new SqlConnection(connectionstring);
SqlCommand cmd = new SqlCommand(sql, conn);
SqlCommand cmd2 = new SqlCommand(sql2, conn);
try {
conn.Open();
using(conn) {
cmd.Transaction = conn.BeginTransaction();
cmd.ExecuteNonQuery();
cmd2.Transaction = cmd.Transaction;
cmd2.ExecuteNonQuery();
cmd2.Transaction.Commit();
}
} finally {
if (conn != null) {
conn.Close();
}
}
}
public void Delete(Users u) {
string connectionstring = "MY_CONNECTION_STRING";
string sql = String.Format("DROP USER {0}", u.Username);
SqlConnection conn = new SqlConnection(connectionstring);
SqlCommand cmd = new SqlCommand(sql, conn);
try {
conn.Open();
using(conn) {
cmd.ExecuteNonQuery();
}
} finally {
if (conn != null) {
conn.Close();
}
}
}
}
}
Users.cs (Model)
using System.ComponentModel.DataAnnotations;
namespace SecureMedi.Models {
public class Users {
[Required]
[StringLength(100)]
public string Username {
get;
set;
}
[Required]
[StringLength(100)]
public string Role {
get;
set;
}
}
}
In summary, the problems I am trying to solve are:
1) Passing the Username query param from EditUser view and Role value from the form to my EditUser controller function.
2) I have hooked up the delete action like
<td class="actions">#Html.ActionLink("Delete", "DeleteUser", "Home", new { Username = #item.Username }, null)</td>
But I'm confused on to how to avoid the [HttpGet] for DeleteUser action. When I click on the anchor tag it is taking me to /Home/DeleteUser?Username=somename, but instead I would just like to execute the DeleteUser action without the view change.
Also, how do I pass Username query param to the controller action.
1) Change [HttpGet] EditUser to take UserName parameter:
and get user by UserName to send this data to model:
[HttpGet]
public IActionResult EditUser(string UserName) {
var model = new Users(UserName);//probably, Or something like GetUser(UserName)
return View(model);
}
2) You can't call HTTPPost method by using actionLink. It will always use HttpGet. So there is two approaches that you can use - 1. Delete user in HttpGet and than redirect to previous page
2. Use JavaScript to call HttpPost, instead of send HttpGet by action link clicking.
You Just Want to Delete the recor form database on just click on the delete link without confirmation user should be navigated to the same page .?
these changes will do this trick for you ! tried
Update your Home Controller Like :
i just remove the [httppost] from delete actionResult and change the parameter name and also write the controller name also in your return redirecttoaction
IMP NOTE : Update The Paramete Users To Username where ever u use it like in DAL Also Suggestion is to use the same one everywhere either it would be null
HomeController.cs
[HttpGet]
public IActionResult EditUser() {
var model = new Users();
return View(model);
}
[HttpPost]
public IActionResult EditUser(Users u) {
if (!ModelState.IsValid) {
return View(u);
}
UsersDAL ud = new UsersDAL();
ud.Edit(u);
return RedirectToAction("ShowUsers", "ControllerName");
}
public IActionResult DeleteUser(Username u) {
if (!ModelState.IsValid) {
return View(u);
}
UsersDAL ud = new UsersDAL();
ud.Delete(u);
return RedirectToAction("ShowUsers", "ControllerName");
}
I am working in an mvc problem and I found my self stuck in a problem trying to bind datetimepicker data to my controller action. I have a view that add a partial to collection of partial views when the user do some action.
My Models looks like
public class CreateOCVM
{
private string errors { get; set; }
public operaciones_confidenciales oc { get; set; }
[Display(Name = "Restringidas")]
public ICollection<PersonaMin> restringidas { get; set; }
public class PersonaMin
{
public int Id { get; set; }
public string motivo { get; set; }
[DataType(DataType.Date)]
public DateTime fecha_acceso;
In my view I have
#using (Html.BeginForm("CreateOC", "OC", FormMethod.Post, new { id = "form" }))
<table id="personasRestringidas" class="table table-striped table-bordered table-hover">
<thead>
<tr>
<th>id</th>
<th>Motivo</th>
<th>Fecha de acceso</th>
<th>Eliminar</th>
</tr>
</thead>
<tbody id="person">
#foreach (PersonaMin item in Model.restringidas)
{
#Html.Partial("~/Views/OC/AddOCRestriccion.cshtml", item)
}
</tbody>
</table>
When user do some ation I add a new item to the ICOllection restringidas. By making an ajax call to a controller method as follows:
function addRestriccion(idPersona) {
if (jQuery.inArray(idPersona, dataset) != 0) {
$.get('/OC/AddOCRestriccion', { id: idPersona}, function (template) {
dataset.push(idPersona);
$("#person").append(template);
$('#datetimepicker' + idPersona).datetimepicker();
$('#datetimepicker' + idPersona).datetimepicker({ format: "DD/MM/YYYY hh:mm" });
$("#datetimepicker" + idPersona).on("change.dp", function (e) {
$('#fechaAcceso' + idPersona).val($("#datetimepicker" + idPersona).datepicker('getFormattedDate'))
});
});
}
This call a controller method an returns a partial view that will be added to the main view.
[HttpGet]
public ActionResult AddOCRestriccion(Int32 id)
{
PersonaMin item = new PersonaMin();
item.Id = id;
persona p = PersonaCollection.getPersonasById(id);
return PartialView("AddOCRestriccion", item);
}
Edit:
This controller returns the partial view to add in the main one.
<tr>
#using (Html.BeginCollectionItem("restringidas"))
{
<td>
#Html.HiddenFor(model => model.Id)
#Html.DisplayFor(model => model.Id, new { #placeholder = "id", #id = "ID" })
</td>
<td>
#Html.TextAreaFor(model => model.motivo, new { #placeholder = "motivo", #id = "motivo", #class = "form-control" })
</td>
<td>
<div class='input-group date' id="#("datetimepicker" + Model.Id)">
<input type='text' class="form-control" />
<span class="input-group-addon">
<span class="glyphicon glyphicon-calendar"></span>
</span>
#Html.HiddenFor(model => model.fecha_acceso, new { #id = "fechaAcceso" + Model.Id })
</div>
</td>
<td class="text-center">
<span class="input-group-btn">
<button type="button" class="btn btn-primary" onclick="deleteAnother($(this).parent().parent())">
<i class="fa fa-minus"></i>
</button>
</span>
</td>
}
When I submit the form it is handled by the controller action
[HttpPost]
public ActionResult CreateOC(CreateOCVM ocvm)
{ ... do something ....
The problem is that when i check the values on my model all the values are ok but not the one for the datetime (fecha_acceso)
In my view the hidden field for my datetime property has value on it.
But the value change when i check it on the controller method.
I tried with diferent aproaches but i always get wrong data on my controller method. Does somebody knows what is happening? Or has somebody a different aproach that can help me to do the same?
Thanks.
By default controller doesn't allow date format as dd-mm-yyyy.
Add below lines in your web.config file to do it
<configuration>
<system.web>
<globalization culture="en-GB" />
</system.web>
</configuration>
Looks like my Html.BeginCollectionItem helper has problems with DateTime so y change de type of my viewmodel from datetime to string. I know it is a workaround and not the correct solution but the lack of time force me to do it.
I am having difficulty passing an IEnumerable as a model. The data is populating a form on one page - and doing so correctly. Upon submission the model returns as null.
I've seen various posts on this and they mostly reference naming-conventions so I have attempted different methods of naming the parameters to try to avoid any confusion in the model binding.
I have also tried various models and helpers to try and pass the data and all have the same result.
Current implementation:
Models:
public class UserProfileListModel
{
public IEnumerable<UserProfileViewModel> UserProfileViewModels { get; set; }
}
public class UserProfileViewModel
{
public UserProfile UserProfile { get; set; }
public Role UserRole { get; set; }
public Team UserTeam { get; set; }
public Scope UserScope { get; set; }
}
View:
#model Project.WebUI.Models.UserPRofileListModel
SNIP
<fieldset>
<legend>Administrate Users:</legend>
<table class="adminTbl">
<thead>
<tr>
<th>UserName:</th>
<th>Role:</th>
<th>Team:</th>
<th>Scope:</th>
<th>Update:</th>
<th>Delete:</th>
</tr>
</thead>
<tbody>
#{foreach (var user in Model.UserProfileViewModels)
{
<tr>
<td>
<p>#user.UserProfile.UserName
#{if (!user.UserProfile.Membership.IsConfirmed)
{
using (Html.BeginForm("Confirm", "Account", FormMethod.Post, null)){
#Html.AntiForgeryToken()
#Html.Hidden("Token", user.UserProfile.Membership.ConfirmationToken)
#Html.Hidden("Name", user.UserProfile.UserName)
}
<input type="submit" value="Confirm" />}
}
</p>
</td>
#{using (Html.BeginForm("SaveUserChanges", "Account", FormMethod.Post, null))
{
#Html.AntiForgeryToken()
#Html.HiddenFor(u => user.UserProfile)
if (user.UserProfile.UserName != User.Identity.Name && user.UserProfile.Membership.IsConfirmed)
{
<td>
#Html.DropDownListFor(u => user.UserRole, Project.WebUI.Controllers.AccountController.RoleList, new { #class = "formdrop" })
</td>
<td>
#Html.DropDownListFor(u => user.UserTeam, Project.WebUI.Controllers.AccountController.TeamList, new { #class = "formdrop" })
</td>
<td>
#Html.DropDownListFor(u => user.UserScope, Project.WebUI.Controllers.AccountController.ScopeList, new { #class = "formdrop" })
</td>
<td>
<input type="submit" value="Save Changes" onclick="return confirm('Are you sure you wish to update this user? ')" />
</td>
}
else
{
/*If user is self or not yet confirmed these are here to buffer the delete button into the last cell*/
<td></td>
<td></td>
<td></td>
<td></td>
}
}
}
<td>
#Html.ActionLink("Delete", "Delete", new { user.UserProfile.UserId }, new
{
onclick = "return confirm('Warning: Action cannot be undone. Are you sure you wish to permanently delete this entry?')"
})
</td>
</tr>
}
}
</tbody>
</table>
</fieldset>
Controller:
Populate View:
public ActionResult AdministrateUsers()
{
populateLists();
var query = repository.UserProfiles.OrderBy(e => e.UserName);
List<UserProfileViewModel> list = new List<UserProfileViewModel>();
foreach(UserProfile up in query)
{
UserProfileViewModel vm = new UserProfileViewModel() { UserProfile = up };
list.Add(vm);
}
UserProfileListModel models = new UserProfileListModel()
{
UserProfileViewModels = list.OrderBy(up => up.UserProfile.UserName)
};
return View(models);
}
Accept Post:
public ActionResult SaveUserChanges(UserProfileListModel model)
{
foreach (UserProfileViewModel upvm in model.UserProfileViewModels)
{
UserProfile up = new UserProfile()
{
UserId = upvm.UserProfile.UserId,
UserEmail = upvm.UserProfile.UserName,
UserName = upvm.UserProfile.UserName
};
if (ModelState.IsValid)
{
repository.SaveUserProfile(up);
}
else
{
return View(model);
}
}
return RedirectToAction("Index", "Admin");
}
The code does still need a lot of work but I can't get past getting the model back to the controller on post. I have also tried returning the UserProfileViewModel instead of the entire list.
Can anyone tell what I am doing wrong?
Thanks!
You have a lot of invalid html including form elements as child elements of tr elements and duplicate id attributes. If you want to post back UserProfileListModel then you need a single form element and use an EditorTemplate or a for loop (not foreach) to render the controls so they are correctly named with indexers.
You are also trying to bind your dropdown lists to complex objects (for example UserProfile, Role etc.). <select> elements (and all form controls) only post back key/value pairs so you need to bind to a value type (for example UserProfile.UserId).
Your SaveUserChanges() post method is also trying access properties of UserProfile but you don't even have controls for properties of UserProfile in the form that post back to this method (for example UserId = upvm.UserProfile.UserId, UserEmail = upvm.UserProfile.UserName, ...) so they will always be null.
You probalby need to bind properties in POST method like here:
public ActionResult Create([Bind(Include = "Id,Subject,Text,IsImportant")] Announcment announcment) {... }
So it will be:
public ActionResult SaveUserChanges([Bind(Include = "UserProfile,Role,UserTeam,UserScope")]UserProfileListModel model)
Have you specified your action method is for HTTP Post? And change your action method to accept UserProfileViewModels instead.
[HttpPost]
public ActionResult SaveUserChanges(UserProfileViewModels model)
{
You are also only posting back one model: UserProfileViewModels.
You have your form in your foreach loop, so each UserProfileViewModels has its own form. If you want to change it to post back your UserProfileListModel, move
#{using (Html.BeginForm("SaveUserChanges", "Account", FormMethod.Post, null))
outside of your foreach.
Hi I have a table with anchor tag in a column. when user clicks on the link, My action method in controller redirect to another method after doing some update logic. Redirect to another action method then after, is not working in my case?
my View :
<fieldset>
<legend>Emended</legend>
<table border="1">
<tr>
<th>
#Html.DisplayNameFor(model => model.CustomerEmneedOrderedProduct.FirstOrDefault().Prd_Qnty)
</th>
</tr>
#foreach (var item in Model.CustomerEmneedOrderedProduct)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Prd_Qnty)
<br />
#Html.ActionLink("Remove", "updateOrderedProdStatuCd", new { orderProductId = item.OrderProductId, OrderedProdStatuCd = 2 })
</td>
</tr>
}
</table>
</fieldset>
My Controller :
public class SellerOrderDetailsController : Controller
{
public ActionResult OrderDetails([Bind(Prefix = "id")] int? orderId)
{
}
public ActionResult updateOrderedProdStatuCd(int orderProductId, int OrderedProdStatuCd)
{
try
{
// Updating few stuffs
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
return RedirectToAction("OrderDetails"); //this is not working
}
}
}
I guess it's because you're not passing orderId to OrderDetails action. The action doesn't match method's signature.
Try something like
return RedirectToAction("OrderDetails ", new { orderId = 123 });
i have two html.actionlinks:
<%= Html.ActionLink("Activate", "ActivateJob", "Management", new { selectedObject = Model.ID }, new { #class = "actions" })%>
|
<%= Html.ActionLink("Deactivate", "DeactivateJob", "Management", new { selectedObject = Model.ID }, new { #class = "actions" })%>
here is part of my table:
foreach (WPM.Logic.Job item in this.Model.Jobs)
{
Model.ID = item.ID;
%>
<tr style="background-color: #FEF0D7">
<td style="border-bottom: solid 1px #f3ad44; width: 80px;" align="center">
<%= i = i + 1 %>
</td>
<td style="border-bottom: solid 1px #f3ad44; width: 120px;">
<input type="checkbox" name="selectedObject" value="<%= Model.ID %>" />
</td>
in page source i have follow results:
<td style="border-bottom: solid 1px #f3ad44; width: 120px;">
<input type="checkbox" name="selectedObject" value="8cdc5c7a-72ba-4883-99b9-272c866c27a9" />
</td>
<td style="border-bottom: solid 1px #f3ad44; width: 120px;">
<input type="checkbox" name="selectedObject" value="fa6b304c-9eee-483f-8208-e2febd077e50" />
</td>
question is: how to get these two checkbox values in HTML.ActionLink selectedObject? I'm getting just a last result in both html.actionlinks but i need value of selected checkbox. i have many of them.
it is a action witch will be called from html.actionlink.
[HttpGet]
public ActionResult ActivateJob(Guid[] selectedObject)
{
foreach (Guid guid in selectedObject)
{
}
return View();
}
[HttpGet]
public ActionResult DeactivateJobs(Guid[] selectedObject)
{
foreach (Guid guid in selectedObject)
{
}
return View();
}
Checkboxes usually go along with HTML forms, not action links. So put them inside a form and use a submit button which will automatically send the checked values to the corresponding controller action. If you want to use links you will need to write javascript code that will subscribe for the click event of the link, extract the values of checkboxes, modify the URL this link is pointing to in order to append those values to the query string which IMHO is too much of a work for something so simple. Of course you can have multiple submit buttons with different names inside a single HTML <form> and in the corresponding controller action you will be able to get the name of the button that was clicked so that you could perform different action.
Also I would strongly recommend you using the HTTP POST or PUT verb for something that is modifying state on the server.
UPDATE:
As requested in the comments section I include an example.
As always you start with a model:
public class JobViewModel
{
public string Guid { get; set; }
public bool Selected { get; set; }
}
public class MyViewModel
{
public IEnumerable<JobViewModel> Jobs { get; set; }
}
then you move on the controller:
public class JobsController: Controller
{
public ActionResult Edit()
{
var model = new MyViewModel
{
// Obviously those will be coming from some data store
// and you could use AutoMapper to map your business entities
// to the corresponding view model
Jobs = new[]
{
new JobViewModel { ID = Guid.NewGuid() },
new JobViewModel { ID = Guid.NewGuid() },
new JobViewModel { ID = Guid.NewGuid() },
}
};
return View(model);
}
[HttpPut]
public ActionResult Update(MyViewModel model, string activate)
{
if (!string.IsNullOrEmpty(activate))
{
// the Activate button was clicked
}
else
{
// the Deactivate button was clicked
}
// TODO: model.Jobs will contain the checked values =>
// do something with them like updating a data store or something
// TODO: return some view or redirect to a success action
return View("Edit", model);
}
}
then you would have a strongly typed view in which you will use editor templates:
<% using (Html.BeginForm("Update", "Jobs")) { %>
<%= Html.HttpMethodOverride(HttpVerbs.Put) %>
<table>
<thead>
<tr>
<th>Foo bar column ...</th>
</tr>
</thead>
<tbody>
<%= Html.EditorFor(x => x.Jobs) %>
</tbody>
</table>
<input type="submit" value="Activate" name="activate" />
<input type="submit" value="Dectivate" name="deactivate" />
<% } %>
and the last part would be the corresponding editor template which will be rendered for each item in the Jobs collection (~/Views/Jobs/EditorTemplates/JobViewModel.ascx):
<%# Control
Language="C#"
Inherits="System.Web.Mvc.ViewUserControl<AppName.Models.JobViewModel>"
%>
<tr>
<%= Html.HiddenFor(x => x.ID) %>
<%= Html.CheckBoxFor(x => x.Selected) %>
</tr>
Maybe I am complete out of scope. But I have solved my problem of "Simulating a checkbox behavior with an ActionLink" in the following (dirty) way using two ASCII-Characters to visualize my two states:
Index.cshtml:
#Html.ActionLink($"{(HomeController.IsExpertMode ? "☑️" : "⬜")}Expert-Mode", "ToggleExpertMode", "Home")
HomeController.cs:
public class HomeController : Controller
{
...
public bool IsExpertMode { get; private set; }
public ActionResult ToggleExpertMode()
{
IsExpertMode = !IsExpertMode;
return RedirectToAction("Index");
}
...
}
Hopefully this can help somebody searching for a simple solution for that problem - which brought me on this page, too...