MVC Approve or Unapprove button Clarification - asp.net-mvc-5

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)
{
}

Related

How to use checkboxes for batch operation?

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.

Creating a product gallery with EF 6 and MVC 5

I'm working on a website for someone and am having a small issue. I'll admit I'm a little new to MVC and EF so I've got this issue. My Index view shows my products, but it shows them in a straight up and down list, like the table I created it supposed to do.
What I'm trying to do is create a gallery, where they're side by side for say 4 in a row, then move on to the next row and so forth (God I know this is making sense). Here's my view
#model IEnumerable<AccessorizeForLess.ViewModels.DisplayProductsViewModel>
#{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Index</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table class="table">
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Name)<br />
<img src="#item.Image.ImagePath"/>
</td>
<td>
#Html.DisplayFor(modelItem => item.Price)
</td>
<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>
And of course it's doing what it's supposed to do. Can someone help me out with this?
In case anyone has anything close to this issue this is what I came up with and it works just the way I was looking for:
#model IEnumerable<AccessorizeForLess.ViewModels.DisplayProductsViewModel>
#{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Products</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
#foreach (var item in Model)
{
<div style="float:left; width:25%">
#Html.DisplayFor(modelItem => item.Name)<br />
<input id="Hidden1" type="hidden" value="#item.Id" />
<div>$#Html.DisplayFor(modelItem => item.Price)</div>
<div><img src="#item.Image.ImagePath" /></div>
<div> </div>
<div>Quantity: <input type="text" id="quantity" style="width:50px;" /></div>
<div>#Html.ActionLink("Details", "Details", new { id = item.Id })</div>
</div>
}
I do, however have one question, in my controller how can I format the price as currency, I tried this:
IEnumerable<DisplayProductsViewModel> model = products.Select(p => new DisplayProductsViewModel()
{
Id = p.ProductId,
Name = p.ProductName,
Image = p.ProductImage,
Price = string.Format("{0:C}",p.ProductPrice)
}).ToList();
And get the error:
LINQ to Entities does not recognize the method 'System.String
Format(System.String, System.Object)' method, and this method cannot
be translated into a store expression

Pass Id to ActionMethod

I have a Delete button inside my table and I m trying to delete the selected Row.
The problem is that I always get in the post method a null ID
<div>
<table class="table">
<thead>
<tr>
<th>Role Name</th>
<th>Action</th>
</tr>
</thead>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#using (Html.BeginForm("Delete","Role", new { id = item.Id },FormMethod.Post))
{
<div class="form-actions no-color">
<input type="submit" value="Delete" class="btn btn-default" /> |
</div>
}
</td>
</tr>
}
</table>
In My Controller
// POST: Jobs/Delete/5
[HttpPost, ActionName("Delete")]
public ActionResult Delete(int id)
{
IdentityRole role = _context.Roles.Find(id);
_context.Roles.Remove(role);
_context.SaveChanges();
return RedirectToAction("Index");
}
Any time I click on the button the id is null
From your comments, the html generated in <form> tags is
action="/Role/Delete/b1bc13ca-a855-48b0-90e2-9e5fc081ac86"
meaning that the Id property of your model is typeof Guid. Change the POST method signature to
public ActionResult Delete(Guid id)

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)

ASP.Net MVC: How to generate html table using editorfor and editor template

still now i generate html table like below code example but interested to know how could i generate html table using editorfor using editor template
see my full razor and view model code and guide me how to achieve my goal.
#model MVCCRUDPageList.Models.StudentListViewModel
#{
ViewBag.Title = "Index";
}
<h2>CREATE TABULAR UI WITH HTML TABLE</h2>
#using (Html.BeginForm("Index", "HtmlTable", FormMethod.Post))
{
<div class="form-group">
<div class="col-md-12 table-responsive">
<table class="table table-bordered table-hover">
<tr>
<th>
Row No
</th>
<th>
ID
</th>
<th>
Name
</th>
<th>
Country
</th>
<th>
Hobbies
</th>
<th>
Sex
</th>
</tr>
}
#for (int x=0; x<=Model.Students.Count-1;x++)
{
<tr>
<td>
<label>#(x+1)</label>
</td>
<td>
#Html.TextBoxFor(m => m.Students[x].ID)
</td>
<td>
#Html.TextBoxFor(m => m.Students[x].Name)
</td>
<td>
#Html.DropDownListFor(m => m.Students[x].CountryID,
new SelectList(Model.Country, "ID", "Name", Model.Students[x].CountryID),
"-- Select Countries--", new { id = "cboCountry", #class = "edit-mode" })
</td>
<td>
#for (var i = 0; i < Model.Students.FirstOrDefault().Hobbies.Count; i++)
{
<div class="checkbox">
#Html.HiddenFor(m => m.Students[x].Hobbies[i].ID)
#Html.HiddenFor(m => m.Students[x].Hobbies[i].Name)
#Html.CheckBoxFor(m => m.Students[x].Hobbies[i].Checked)
#Html.LabelFor(m => m.Students[x].Hobbies[i].Name, Model.Students[x].Hobbies[i].Name)
</div>
}
</td>
<td>
#for (var i = 0; i < Model.Sex.Count; i++)
{
<div class="checkbox">
#Html.HiddenFor(m => Model.Sex[i].ID)
#Html.HiddenFor(m => Model.Sex[i].SexName)
#Html.RadioButtonFor(m => m.Students[x].SexID, Model.Sex[i].ID)
#Html.LabelFor(m => m.Students[x].SexID, Model.Sex[i].SexName)
</div>
}
</td>
</tr>
}
</table>
</div>
<input type="submit" value="Submit" />
</div>
}
View Model Code
public class StudentListViewModel
{
public IList<Student> Students { get; set; }
public List<Country> Country { get; set; }
public List<Sex> Sex { get; set; }
public StudentListViewModel()
{
Students = new List<Student>
{
new Student
{
ID=1,Name="Keith",CountryID=0,SexID="F",
Hobbies= new List<Hobby>
{
new Hobby{ID=1,Name="Football",Checked=false},
new Hobby{ID=2,Name="Hocky",Checked=false},
new Hobby{ID=3,Name="Cricket",Checked=false}
}
},
new Student
{
ID=2,Name="Paul",CountryID=2,
Hobbies= new List<Hobby>
{
new Hobby{ID=1,Name="Football",Checked=false},
new Hobby{ID=2,Name="Hocky",Checked=false},
new Hobby{ID=3,Name="Cricket",Checked=false}
}
},
new Student
{
ID=3,Name="Sam",CountryID=3,
Hobbies= new List<Hobby>
{
new Hobby{ID=1,Name="Football",Checked=false},
new Hobby{ID=2,Name="Hocky",Checked=false},
new Hobby{ID=3,Name="Cricket",Checked=false}
}
}
};
Country = new List<Country>
{
new Country{ID=1,Name="India"},
new Country{ID=2,Name="UK"},
new Country{ID=3,Name="USA"}
};
Sex = new List<Sex>
{
new Sex{ID="M",SexName="Male"},
new Sex{ID="F",SexName="Female"}
};
}
}
please guide me how to restructure my razor code to use editorfor and editor template for clean code. what will be name of editor template ?
thanks
To use an EditorTemplate using #Html.EditorFor(m => m.SomeProperty) (where SomeProperty is a complex object, or collection of complex objects), then the template must be located in the /Views/Shared/EditorTemplates or /Views/YourControllerName/EditorTempates folder, and be named the same as the class name for SomeProperty.
In your case, if you want an EditorTemplate for typeof Student, then the template (a partial view) will be named Student.cshtml.
#model yourAssembly.Student
<tr>
<td>#Html.TextBoxFor(m => m.ID)</td>
<td>#Html.TextBoxFor(m => m.Name)<td>
....
<tr>
and in the main view, your would use
<table>
<thead> .... </thead>
<tbody>
#Html.EditorFor(m => m.Students)
</tbody>
</table>
which will generate the correct html for each item in your collection.
However, since you also have a dropdownlist for CountryID, then you need to pass the SelectList to the template (the template has no knowledge of the parent model) using additionalViewData, so the code needs to be modified to
#Html.EditorFor(m => m.Students, new { Countries = Model.Countries})
where the Countries property in StudentListViewModel should be
public IEnumerable<SelectListItem> Countries { get; set; }
and delete you current List<Country> Country property. Then in the view generate the dropdownlist using
<td>#Html.DropDownListFor(m => m.CountryID, (IEnumerable<SelectListItem>)ViewData["Countries "], "-- Select Country--", new { ... })<td>
Note you should not be adding new { id = "cboCountry" } because its creating invalid html (duplicate id attribute).
Now you can extend this further by creating an EditorTemplate for Hobby (which will be named Hobby.cshtml
#model yourAssembly.Hobby
<div class="checkbox">
#Html.HiddenFor(m => m.ID)
#Html.HiddenFor(m => m.Name)
#Html.CheckBoxFor(m => m.Checked)
#Html.LabelFor(m => m.Checked, Model.Name)
</div>
Note the LabelFor() should be for the Checked property, not the Name property as you currently have it.
Then in your Student.cshtml template, use
<td>#Html.EditorFor(m => m.Hobbies)<td>
to generate the correct html for each Hobby.

Resources