How to use checkboxes for batch operation? - asp.net-mvc-5

Say I have a view like this:
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#assign">
Assign
</button>
<table class="table table-hover table-bordered table-sm">
<thead class="thead-light">
<tr>
<th></th>
<th>Name</th>
</tr>
</thead>
#foreach (var employee in ViewBag.employees)
{
<tr>
<td><input type="checkbox" name="#employee.Id" /></td>
<td>#employee.Name</td>
</tr>
}
</table>
<!-- Modal -->
<div class="modal fade" id="assign" tabindex="-1" role="dialog">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="modalTitle">Assign employee to group</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<!-- a dropdown of groups -->
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary">Assign</button>
</div>
</div>
</div>
</div>
And I have a controller to call:
public ActionResult Assign(List<int> employeeIds, int groupId)
How can I get the list of Ids that are checked and the groupId from the dropdown in modal, so that I can call the controller?
For example if the data look like this
0. Alice
1. Bob
2. Charlie
3. Dan
And I checked Bob and Charlie and want to assign them to group 1, so the employeeIds will be 1 and 2, and groupId will be 1.

You need to use for loop instead of foreach. Besides, I suggest to use ViewModel instead of ViewBag.
#using (Html.BeginForm("Index", "Home", FormMethod.Post, null))
{
for (int i = 0; i < Model.Employees.Count; i++)
{
<tr>
<td>#Html.CheckBoxFor(x => Model.Employees[i].Checked)</td>
<td>#Model.Employees[i].Name</td>
</tr>
}
<input type="submit" value="Submit" />
}
Model
You want to rename ViewModel something meaningful.
public class ViewModel
{
public List<Employee> Employees { get; set; }
public int GroupId { get; set; }
}
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public bool Checked { get; set; }
}
Controller
public class HomeController : Controller
{
public ActionResult Index()
{
ViewModel vm = new ViewModel
{
Employees = new List<Employee>
{
new Employee {Id=1, Name = "John", Checked = true},
new Employee {Id=2, Name = "Eric", Checked = false}
}
};
return View(vm);
}
[HttpPost]
public ActionResult Index(ViewModel vm)
{
return View(vm);
}
}
Here is another way.

Related

MVC Approve or Unapprove button Clarification

I'm new to mvc and am trying to figure out how to create an approve button on an index view. Some clarification is needed.
There is a question here already about creating an Approve/Unapprove button in an mvc view. But after reviewing it, it appears to be missing information. The controller is expecting the SubmitButton and the ID to be passed to it. This I understand. But the view's submit buttons only sends the SubmitButton with no ID. Can someone please clear the fog? Thank you.
Controller
[ActionName("Index")]
[HttpPost]
public ActionResult IndexPost(string SubmitButton, int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
string buttonClicked = SubmitButton;
if(buttonClicked == "Approve")
{
CurrentApplication currentApplication = db.CurrentApplications.Find(id);
currentApplication.AppStatus = "APPROVED";
db.SaveChanges();
}
else if(buttonClicked == "Unapprove")
{
CurrentApplication currentApplication = db.CurrentApplications.Find(id);
currentApplication.AppStatus = "UNAPPROVED";
db.SaveChanges();
}
//Save Record and Redirect
return RedirectToAction("Index");
}
View
<button type="submit" name="SubmitButton" value="Approve" class="btn btn-sm btn-success">Approve</button>
<button type="submit" name="SubmitButton" value="Unapprove" class="btn btn-sm btn-danger">Unapprove</button>
The controller will them update the database with the status change and return the Index to reflect the changes.
According to your newest example, here is the best answer I can think. At first, your code example has a few syntax errors and I needed to create own macaddress -class. I hope its similar than yours.
I used the same example than in my earlier answer. I only modified view and created maccaddress -class. The controller is same as earlier.
Modified View
This is the important part. I think you just have formatted Html form and tables in weird way. Here is a screenshot of how the view is rendered.
Now when user click Approve or Unapprove the form will send maccaddress id and button value to the controller IndexPost -action.
In a view example:
table is made just for example, modify it if needed.
I changed foreach location, now it is before Html.BeginForm. In your example, BeginForm was outside foreach and that caused a few problems when submitting your code. In a way how your form was rendered, it was impossible to know which Id is submitted.
Look #Html.Hidden("id", item.idMACAddress). It should be inside BeginForm.
I have only print id and status into a table for example purposes.
Action buttons are in the third column.
So why it works now is the way Form is rendered. In the example, there is now one html form for every maccaddress row. When a button is clicked, we know exactly what values we want to send into the controller.
#model IEnumerable<MySQL_MACAddress.Models.macaddress>
#{ ViewBag.Title = "Index"; }
<table class="table">
<tr>
<th> MAC Address </th>
<th> Status </th>
<th> Actions </th>
</tr>
#foreach (var item in Model)
{
using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.Hidden("id", item.idMACAddress)
<tr>
<td> #item.idMACAddress </td>
<td> #item.Status </td>
<td>
<button type="submit" name="SubmitButton" value="Approve" class="btn btn-sm btn-success">Approve</button>
<button type="submit" name="SubmitButton" value="Unapprove" class="btn btn-sm btn-danger">Unapprove</button>
</td>
</tr>
}
}
</table>
maccaddress.cs example
Check this example. Don't just copy it because probably it won't work with your code. The view is a more important part here so look it.
using System;
namespace MySQL_MACAddress.Models
{
public class macaddress
{
public macaddress()
{
}
public string Status { get; set; }
public int idMACAddress { get; set; }
}
}
Controller
Check earlier answer.
I suppose you have an HTML form in View made with Html-helpers. In your case, the following example code is probably the simplest one.
View - Home/Index.cshtml
<h2>Welcome to ASP.NET MVC!</h2>
<!--
If needed, check BeginForm parameters to send POST
into right controller and action.
-->
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<!-- second parameter is the value for id field. -->
#Html.Hidden("id", 2001)
<button type="submit" name="SubmitButton" value="Approve" class="btn btn-sm btn-success">Approve</button>
<button type="submit" name="SubmitButton" value="Unapprove" class="btn btn-sm btn-danger">Unapprove</button>
}
Controller - HomeController.cs
using System.Net;
using System.Web.Mvc;
namespace FormTest.Controllers
{
public class HomeController : Controller
{
// GET action for index
public ActionResult Index()
{
// Do something here ...
return View();
}
// POST action for index.
// Will use same path as GET
[HttpPost]
[ActionName("Index")]
public ActionResult IndexPost(string SubmitButton, int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
if (SubmitButton == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
//Redirect to GET Index
return RedirectToAction("Index");
}
}
}
The important part is the Hidden helper that will generate a hidden input field. If you need more modifications in your use case, read more here about parameters and examples.
#T.Nylund , Your thoughts? Thank you.
View
#model Enumerable<MySQL_MACAddress.Models.macaddress>
#{ ViewBag.Title = "Index"; }
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.Status)
</th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#HtmlHidden("id", item.idMACAddress)
</td>
<td>
#Html.DisplayFor(modelitem => item.Status)
</td>
<button type="submit" name="SubmitButton" value="Approve" class="btn btn-sm btn-success">Approve</button>
<button type="submit" name="SubmitButton" value="Unapprove" class="btn btn-sm btn-danger">Unapprove</button>
}
We use different controller methods for the different actions when approving (button to approve, button to reject)
Our view:
if (!Model.IsApproved)
{
<td>
#using (Html.BeginForm("Approve", "BudgetItem", new { id = Model.BudgetItemSid, role = Model.ActiveRole }, FormMethod.Post))
{<button type="submit" id="btnApprove" style="display: none;">Approve</button>}
</td>
}
if (!Model.IsRejected)
{
<td>
#using (Html.BeginForm("Reject", "BudgetItem", new { id = Model.BudgetItemSid, role = Model.ActiveRole }, FormMethod.Post))
{<button type="submit" id="btnReject" style="display: none;">Reject</button>}
</td>
}
Our Controller:
[HttpPost]
public ActionResult Approve(int id, EnumRole role)
{
}
[HttpPost]
public ActionResult Reject(int id, EnumRole role)
{
}

If my button is in the form, it does not send any data back to the view

I am trying to get some more information about Razor Pages with core 2.0 and I am having some issues with the post. Please note, this is not production worthy, its just code to try and getting a better understanding of what you can do in razor pages.
The issue is, if my button is in the form, it does not send any data back to the view. So, computedURL in OnPostConcatURL() is never getting to the view.
If the button is outside the form, the form data does not get to the post controller OnPostEdit23 but I can send back ComputedURL to the view. Clearly I am missing something or a lot. And I cannot find an example to fix this.
#page
#model VisibilityTest
#{ ViewData["Title"] = "Visibility Test Site"; }
<form id="frmVisibility" method="post">
<div class="container">
<div class="row form-group">
<div class="col-md-1"> Select Portal: </div>
<div class="col-md-1"><select id="ddlEnvironment" asp-for="selectedEnvironment" asp-items="Model.Environments"></select></div>
<div class="col-md-2"><select id="ddlPortalName" asp-for="selectedPortalName" asp-items="Model.portalNames"></select></div>
<div class="col-md-3"><input asp-for="#Model.ComputedURL" /></div>
</div>
<div class="row form-group">
<div class="col-md-1"><button id="btnConcatURL" asp-page-handler="ConcatURL" type="submit" class="btn btn-primary">Submit23</button></div>
</div>
</div>
</form>
<form method="post">
<button asp-page-handler="edit23" class="btn btn-default">Edit2</button>
</form>
[BindProperty]
public string ComputedURL { get; set; }
public void OnGet()
{
config = GetConfigFile();
PopulatedEnvironmentSelectList();
PopulatePortalNameSelectList(config);
}
public IActionResult OnPost()
{
ComputedURL = selectedEnvironment + selectedPortalName;
return RedirectToPage("./VisibilityTest");
}
public void OnPostConcatURL()
{
ComputedURL = "this is a test";
}
public void OnPostEdit23()
{
ComputedURL = "this is a test";
}
I'm still figuring out Razorpages as well but I noticed a couple of points about your example:
The Model should be the code behind page class not some other object.
The BindProperty should probably be to an object and not just a string (or you can bind to multiple properties on the code-behind object)
If you want to pass back just a message (string) you can use temp data.'
You definitely want the button inside the form as it will populate the model properties with form values (but as with #2 the binding property should be more than a simple string).
So your example modified example below seems to do what you are looking for (I simplified the dropdowns to text fields for easy testing).
.cshtml page
#page
#model VisibilityTestModel
#{
ViewData["Title"] = "Visibility Test Site";
}
<hr />
<form id="frmVisibility" method="post">
<div class="container">
<div class="row form-group">
<div class="col-md-1"> Select Portal: </div>
<div class="col-md-3"><input asp-for="#Model.Portal.Environment" /></div>
<div class="col-md-3"><input asp-for="#Model.Portal.Name" /></div>
<div class="col-md-3">#Model.Portal.ComputedURL</div>
</div>
<div class="row form-group">
<div class="col-md-1"><button id="btnConcatURL" asp-page-handler="ConcatURL" type="submit" class="btn btn-primary">Submit23</button></div>
</div>
</div>
</form>
<h3>Msg: #Model.Message</h3>
.cshtml.cs page:
public class VisibilityTestModel : PageModel {
[BindProperty]
public PortalInfo Portal { get; set; }
[TempData]
public string Message { get; set; }
public void OnGet() {
Portal = new PortalInfo {
Environment = "www.example.com",
Name = "test"
};
}
public void OnPostConcatURL() {
Portal.ComputedURL = Portal.Environment + " " + Portal.Name;
Message = "URL concat";
}
}
public class PortalInfo {
public string Environment { get; set; }
public string Name { get; set; }
public string ComputedURL { get; set; }
}

Foundation 5 Abide validation not working with MVC5

I am trying to hook up Foundation 5 abide validation to my MVC 5 view.When I leave the required fields and try to submit the form,I see all the required fields highlighted in red,but I want to see the error message I added to the C# view model in the Required(ErrorMessage="username is required"]
These are the things I already added
Added these keys to my root level web.config
<add key="ClientValidationEnabled" value="true" />
<add key="UnobtrusiveJavaScriptEnabled" value="true" />
Added the data_abide attribute to the form element
#using (Html.BeginForm("Register", "Account", FormMethod.Post, new { id = "form-user-register", data_abide = "" }))
{
<div class="row">
<div class="small-3 columns">
#Html.DisplayNameFor(m => m.LoginName)<br />
#Html.TextBoxFor(m => m.LoginName, new { id = "register-loginname"})
</div>
</div>
<div class="row">
<div class="small-3 columns">
#Html.DisplayNameFor(m => m.UserPassword)<br />
#Html.TextBoxFor(m => m.UserPassword, new { id = "register-loginpassword" })
</div>
</div>
<div class="row">
<div class="small-3 columns">
#Html.DisplayNameFor(m => m.Email)<br />
#Html.TextBoxFor(m => m.Email, new { id = "register-loginpassword" })
</div>
</div>
<div class="row">
<div class="small-3 columns">
#Html.DisplayNameFor(m => m.FirstName)<br />
#Html.TextBoxFor(m => m.FirstName, new { id = "register-login-firstname" })
</div>
</div>
<div class="row">
<div class="small-3 columns">
#Html.DisplayNameFor(m => m.LastName)<br />
#Html.TextBoxFor(m => m.FirstName, new { id = "register-login-firstname" })
</div>
</div>
<div class="row">
<div class="small-12 columns">
<div class="left">
<input type="submit" class="button radius small right" value="Register" />
</div>
</div>
</div>
}
I made sure that the jquery.validate.unobtrusive.js and jquery.validate.js is added to the _LayoutView.cshtml
This is the C# Viewmodel
public class RegisterViewModel
{
public int AppUserId { get; set; }
[Display(Name="Username")]
[Required(ErrorMessage="Username is required")]
public string LoginName { get; set; }
[Display(Name = "Email")]
[Required(ErrorMessage = "Email is required")]
public string Email { get; set; }
[Display(Name = "FirstName")]
[Required(ErrorMessage = "FirstName is required")]
public string FirstName { get; set; }
[Display(Name = "LastName")]
[Required(ErrorMessage = "LastName is required")]
public string LastName { get; set; }
[Display(Name = "Password")]
[Required(ErrorMessage = "Password is required")]
public string UserPassword { get; set; }
public AddressViewModel Address { get; set; }
}
Still I only the the above screenshot
I figured out what the issue was.This might help someone like me in the future
I was missing couple of things in my _Layout.cshtml and the Register view
1.Missed thi sfrom _Layout view
<script>
$(document).foundation()
</script>
2.Forgot to add the required HTML5 attribute and the html tag that abide validation uses to display the model error
<div class="small-3 columns">
#Html.DisplayNameFor(m => m.LoginName)<br />
#Html.TextBoxFor(m => m.LoginName, new { id = "register-loginname" ,required=""})
<small class="error">Username is required</small>
</div>
And validation worked like a charm

I can not get dropdownlist value in mvc3

i have a form by dropdownlist element. i want get value of dropdownlist in controller.
i do not know how can do this. i read my username of users with dataview in controller and want to change role of them by dropdownlist options.
#using (Html.BeginForm()) {
<form name="register" action="#">
<div>
<table>
#foreach (MembershipUser user in Model)
{
var userroles = Roles.GetRolesForUser(user.UserName);
<tr>
<td>
#user.UserName
</td>
#foreach (var role in userroles)
{
<td>
#role
</td>
}
<td>
#Html.DropDownList("roleName")
#Html.ValidationMessage("roleName")
</td>
<td>
</td>
</tr>
}
</table>
<input type="submit" class="register" value="Save" />
</div>
</form>
}
and this is my controller:
public ActionResult Index()
{
ViewData["roleName"] = new SelectList(Roles.GetAllRoles(), "roleName");
return View(Membership.GetAllUsers());
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Index()
{
return RedirectToAction("Index", "Home");
}
Did you try:
public ActionResult Index(string roleNameSelectedValue)
{
// roleNameSelectedValue is the "Value" field of selected item in your dropdownlist
return RedirectToAction("Index", "Home");
}
Or using #Html.DropDownListFor(m => Model.roleName, ViewBag.roleName as SelectList)

AllowHtml attribute not working on production

I have a model that required that html be captured. I have added the [AllowHtml] attribute to the model property and it works correctly on my local server when debugging.
Once deployed to production however, it works correctly when executed on the production server (i.e. I remote onto the server and browse it there), but fails with the the usual "potentially dangerous blah blah blah " message when executed from any other machine.
So it seems to me that there is something to do with the location involved in the validation, or am I completely missing the boat.
Just to confirm, I have made no "special" changes to the web.config.
Please can someone explain why I am having this issue.
Model
[AllowHtml]
[Display(Name = "Overview")]
public string Overview { get; set; }
Controller
//
// POST: /Product/
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult EditFeature(BackOffice.Models.ProductFeature model)
{
if (ModelState.IsValid)
{
//insert the new product
}
//invalid model, return with errors
return View(model);
}
View
#model BackOffice.Models.ProductFeature
#using (Html.BeginForm("AddFeature", "Product", null, FormMethod.Post, new { role = "form", #class = "form-horizontal" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
#Html.Hidden("ProductID", #Model.ProductID)
<div class="modal fade" id="FeatureModal" tabindex="-1" role="dialog" aria-labelledby="FeatureModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4 class="modal-title">Add a Feature</h4>
</div>
<div class="modal-body">
<div class='form-group'>
<label class='col-lg-2 control-label'>Title</label>
<div class="col-lg-10">
#Html.TextBoxFor(m => m.Title, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.Title)
</div>
</div>
<div class='form-group'>
<label class='col-lg-2 control-label'>Overview</label>
<div class="col-lg-10">
#Html.TextAreaFor(m => m.Description, 10, 40, new { #class = "ckeditor", id = "overview" })
</div>
</div>
</div>
<div class='clearfix'></div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="submit" class="btn btn-primary">Add</button>
</div>
</div>
<!-- /.modal-content -->
</div>
<!-- /.modal-dialog -->
</div>
<!-- /.modal -->
}
There is a mis-match in the method names here. You have
#using (Html.BeginForm("AddFeature", "Product", null, FormMethod.Post, new { role = "form", #class = "form-horizontal" }))
{
}
But Your action method is called
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult EditFeature(BackOffice.Models.ProductFeature model)
{
}
Where is the AddFeature action method?

Resources