I'm creating a tool for editing/disabling Active Directory user accounts. I will search for a username and it will come up with all matches ("jsm" will come up with a table with "John Smith", "James Smoth", and "Jack Smuth"). Beside the user names are buttons "Edit" and "Disable". When the I click "Edit" or "Disable", a bootstrap modal will appear with textboxes containing the account properties such as SamAccountName and DisplayName inside of form inputs.
Because there could be multiple results, I'm passing in an IEnumerable into the view, then foreach through the #Model to put the data into the table. My next step is trying to pass in just that specific User object into the modal. So if the I click on the Edit button beside John Smith, I want his User object to populate the modal.
My first thought is doing a variable of User I can pass the User object from the foreach loop into then use that object in the modal, but I'm not sure how to go about doing that. Can/should I keep this inside the razor view or should I be passing this data into the controller then back out?
Controller:
namespace ADM.Controllers
{
public class ManagementController : Controller
{
public new IActionResult User()
{
var user = new List<User>();
return View(user);
}
[HttpPost]
public new IActionResult User(string username)
{
var user = new User();
var result = user.Get(username);
return View(result);
}
public IActionResult Group()
{
return View();
}
}
}
User.cshtml:
#model IEnumerable<User>
#{
ViewData["Title"] = "User Management";
var count = 1;
}
<div class="container col-6">
<div class="card card-square">
<div class="card-body">
<form asp-controller="Management" asp-action="User">
<div class="form-row form-inline">
<input type="text" class="form-control col-10" name="username" placeholder="Username" />
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</form>
</div>
</div>
</div>
<br />
#if (Model.Any())
{
<div class="small">
<table class="table table-hover table-sm">
<thead>
<tr>
<th scope="col"></th>
<th scope="col">Display Name</th>
<th scope="col">Description</th>
<th scope="col">SamAccountName</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
#foreach (var user in Model)
{
<tr>
<th scope="row">#(count++)</th>
<td>#user.DisplayName</td>
<td>#user.Description</td>
<td>#user.SamAccountName</td>
<td>
<button id="userEditBtn" class="btn btn-sm btn-info" data-toggle="modal" data-target="#userEditModal">Edit</button>
<button class="btn btn-sm btn-danger" data-toggle="modal" data-target="#userDisableModal">Disable</button>
</td>
</tr>
}
</tbody>
</table>
</div>
}
There's more than one way to do this.
If there isn't a lot of User fields, you can store the properties in the DOM for each User button
#foreach(var user in Model)
{
<button class="btnEdit" data-username="#user.Username">Edit</button>
}
In your JavaScript button handler you extract the data to populate a form submission or make an AJAX request.
$(".btnEdit").on("click", function(e) {
var form = $("#myform");
var username = $(this).data("username");
// populate a form
$("input[name='username']").val(username);
form.submit();
});
Another way is to make a AJAX call for the user you want to edit to obtain more data than what you first loaded with the initial collection. Then use the second call to populate the edit form. One quick way is to just load a html partial.
$(".btnEdit").on("click", function(e) {
var username = $(this).data("username");
var action = $(this).data("action"); // EditDetailsForm
$.ajax({
url: action,
method: "get",
data: { username = username }
})
.then(function(partialView) {
$("#editForm").html(partialView);
});
});
And the action
[HttpGet]
public ActionResult EditDetailsForm(string username)
{
var user = GetUser(username);
return PartialView("_editForm", user);
}
Returns a partial view _editForm.cshtml
#Model User
<form ...>
...
</form>
Which you just drop into your modal.
Related
I have an asp.net mvc application. I want to perform and update operation.I want to show you my code before explain my problem sorry I am not fluent in english thats why I prefer to show my code first.Here I have an index page that shows all items in database with a table
#model IEnumerable<InOut.Models.Item>
#if (Model.Count() > 0)
{
<table class="table table-bordered table-striped" style="width: 100%; text-align: center">
<thead>
<tr>
<th>Item Name</th>
<th>Borrower</th>
<th>Lender</th>
<th >Edit</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model)
{
<tr>
<td style="width: 25%">#item.borrower</td>
<td style="width: 25%">#item.Lender</td>
<td style="width: 25%">#item.ItemName</td>
<td style="width: 13%">
<a asp-controller="Item" asp-action="Delete" asp-route-id="#item.id" onclick="showAlert()"class="btn btn-danger" id="btn1" style="text-decoration: none; color: white">delete</a>
<a asp-controller="Item" asp-action="Update" asp-route-id="#item.id" class="btn btn-primary" id="btn1" style="text-decoration: none; color: white">Update</a>
</td>
</tr>
}
</tbody>
</table>
}
else
{
<p> No Items created </p>
}
Here is the index action for that view
public IActionResult Index()
{
IEnumerable<Item> items = _dbContext.Items;
return View(items);
}
In the foreach loop I have a button to update the object on that row it has an action named "Update" in controller class.here is the code to get the item on that row.
//Get Item
public IActionResult Update(int? id)
{
var item = _dbContext.Items.Single(o => o.id == id);
return View(item);
}
this action sends the selected item properties to the update view.here is the code of view
<form method="post" asp-action="Create">
<input asp-for="id" hidden/>
<div class="form-group">
<label >Item Name </label>
<input type="text" class="form-control" aria-describedby="emailHelp" asp-for="ItemName">
</div>
<div class="form-group">
<label >Borrower</label>
<input type="text" class="form-control" asp-for="borrower">
</div>
<div class="form-group">
<label >Lender</label>
<input type="text" class="form-control" asp-for="Lender">
</div>
<button asp-controller="Item" asp-action="Update" type="submit" class="btn btn-primary" style="margin-top: 10px">Submit</button>
submit button sends properties to the overloaded action update with the parameter of item. after update operation I want it to show me the index page. here is the overloaded update action
// Update Item
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Update(Item item)
{
_dbContext.Update(item);
_dbContext.SaveChanges();
return View("Index");
}
Problem starts here. When I run application after submitting changes I want it to show me the index action update operation working well.but when it try to show me the index page it gives me this error.
ArgumentNullException: Value cannot be null. (Parameter 'source') and it refers to the if statement in the index page it
now if I use redirectToAction return instead of View like this...
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Update(Item item)
{
_dbContext.Update(item);
_dbContext.SaveChanges();
return RedirectToAction("Index");
}
It works as I wanted. I want to know what is the problem with the first approach and is there any difference between those 2 return types?
the difference is here
public IActionResult Index()
{
IEnumerable<Item> items = _dbContext.Items; //maybe you need to add .ToList(); ??
return View(items);
}
when you are using an index action you create the list of items as a model and pass them to the view. But you don't create any model when you call Index view from an update action. If you still want to return view from an update you can do it this way
_dbContext.Update(item);
_dbContext.SaveChanges();
var items = _dbContext.Items.ToList();
return View("Index", items);
or if update and index actions are in the same controller I usually just call an action as a method
_dbContext.Update(item);
_dbContext.SaveChanges();
return Index();
On my view, I have a table with a form that I want to use to delete a particular row. I use a foreach loop to generate an hidden input field with the row value that I want to pass to the controller and asp-for tag for model biding, and a submit button.
The value that is passed to the controller is always the first row. I'm inclined to think that the reason for this behavior is that the generated input fields all have the same name attribute, because the asp-for expression is invariant for every iteration of the foreach loop.
Is there a straight-forward way to implement this using a form and a POST request, or should I just use anchors with route values, i.e., GET requests?
Here's my ViewModel:
public class RolesViewModel
}
public IList<AppUser> UsersInRole {get; set;}
public string SelectedRole {get; set;}
public RemoveUserFromRole RemoveUser {get; set;}
public class RemoveUserFromRole
{
public string UserName {get; set;}
public string RoleName {get; set;}
}
}
My View
<form method="post" asp-action="RemoveUser" id="removeUserForm"></form>
<table id="userTable" class="table table-striped table-sm">
<thead>
<tr>
<th scope="col">User name</th>
<th scope="col" class="text-center">Delete</th>
</tr>
</thead>
<tbody>
#foreach (var user in Model.UsersInRole)
{
<tr>
<td>#user.UserName</td>
<td class="text-center">
<input form="removeUserForm" asp-for="RemoveUser.UserName" type="hidden" value="#user.UserName" />
<input form="removeUserForm" asp-for="RemoveUser.RoleName" type="hidden" value="#Model.SelectedRoleName" />
<button form="removeUserForm" type="submit" class="btn btn-sm btn-link text-danger py-0 my-0">
<i class="fas fa-times"></i>
</button>
</td>
</tr>
}
</tbody>
</table>
And my action method in controller
[HttpPost]
public async Task<IActionResult> RemoveUser(RolesViewModel model)
{
//model.RemoveUser.UserName always have the value from the first row
var user = await _userManager.FindByNameAsync(model.RemoveUser.UserName);
if (user == null)
return RolesError(await GetModel());
var result = await _userManager.RemoveFromRoleAsync(user, model.RemoveUser.RoleName);
if (!result.Succeeded)
return RolesError(await GetModel());
return RedirectToAction("Roles", new { roleName = model.RemoveUser.RoleName });
}
Thanks in advance for your time.
According to your codes, I found you have multiple hidden filed which contains the user.UserName.
If you click the submit button, it will upload all the hidden filed value to the code-behind and it will just bind the first one, this is the reason why your model is always first one.
You could find the formdata in F12 developtool's network.
To solve this issue, we have a easily but not a good solution.
We could set mutiple form tag in your table to avoid post all the all the hidden filed username value to controller:
Like below:
<table id="userTable" class="table table-striped table-sm">
<thead>
<tr>
<th scope="col">User name</th>
<th scope="col" class="text-center">Delete</th>
</tr>
</thead>
<tbody>
#foreach (var user in Model.UsersInRole)
{ int i = 0;
<tr>
<td>#user.UserName</td>
<td class="text-center">
<form method="post" asp-action="RemoveUser" id="#user.UserName">
<input form="#user.UserName" name="RemoveUser.UserName" type="hidden" value="#user.UserName" />
<input form="#user.UserName" name="RemoveUser.RoleName" type="hidden" value="#Model.SelectedRole" />
<button form="#user.UserName" type="submit" class="btn btn-sm btn-link text-danger py-0 my-0">
<i class="fas fa-times">iiiii</i>
</button>
</form>
</td>
</tr>
}
</tbody>
</table>
If you choose this way, you should rebuild all your view's html makeup.
Besides, you could try to use ajax to achieve your requirement, this solution is better than before solution. You could use jquery to get the right form data according to the submit button's id or position and then use jquery ajax to post the form data into controller. Then you could return the redirect url instead of RedirectToAction methods.
More details about how to use ajax to send form data, you could refer to below codes:
#model MVCRelatedIssue.Models.RolesViewModel
#{
ViewData["Title"] = "Index";
}
<h1>Index</h1>
<form method="post" asp-action="RemoveUser" id="removeUserForm">
<table id="userTable" class="table table-striped table-sm">
<thead>
<tr>
<th scope="col">User name</th>
<th scope="col" class="text-center">Delete</th>
</tr>
</thead>
<tbody>
#foreach (var user in Model.UsersInRole)
{
<tr>
<td>#user.UserName</td>
<td class="text-center">
<input form="removeUserForm" name="RemoveUser.UserName" type="hidden" value="#user.UserName" />
<input form="removeUserForm" name="RemoveUser.RoleName" type="hidden" value="#Model.SelectedRole" />
<button form="removeUserForm" type="submit" id="submit" class="btn btn-sm btn-link text-danger py-0 my-0 subbtn">
<i class="fas fa-times">iiiii</i>
</button>
</td>
</tr>
}
</tbody>
</table>
</form>
#section Scripts{
<script>
$(document).ready(function () {
$(".subbtn").bind("click", function (e) {
e.preventDefault();
var formdata = new FormData();
var UserName = $(this).prev().prev().val();
formdata.append("RemoveUser.UserName", UserName);
console.log(UserName);
var roleName = $(this).prev().val();
formdata.append("RemoveUser.RoleName", roleName);
console.log(roleName);
$.ajax({
type: "POST",
url: "/RemoveUser/RemoveUser",
data: formdata,
contentType: false,
processData: false,
success: function (data) {
alert("success");
window.location.href = data;
}
});
});
});
</script>
}
Controller:
[HttpPost]
public async Task<IActionResult> RemoveUser(RolesViewModel model)
{
//model.RemoveUser.UserName always have the value from the first row
//var user = await _userManager.FindByNameAsync(model.RemoveUser.UserName);
//if (user == null)
// return RolesError(await GetModel());
//var result = await _userManager.RemoveFromRoleAsync(user, model.RemoveUser.RoleName);
//if (!result.Succeeded)
// return RolesError(await GetModel());
string redirecturl = "/RemoveUser/Roles?roleName=" + model.RemoveUser.RoleName;
return Ok(redirecturl);
}
Result:
You can use a Delete Link
in View
#foreach (var user in Model.UsersInRole)
{
<a href="#Url.Action("RemoveUser", "YOUR_Controller",new {username = user.UserName})"
onclick="return confirm('Do You want to Delete');"
</a>
}
In Controller
[HttpGet]
public async Task<IActionResult> RemoveUser(String username)
{
// Get the user Object the delete it
}
I was capable of solving this issue with minimal code footprint.
As it turns out, <td> tags can have forms, so, knowing that, it is possible to have a different form on each row, like so:
View:
//Remove inline table form
<table id="userTable" class="table table-striped table-sm">
<thead>
<tr>
<th scope="col">User name</th>
<th scope="col" class="text-center">Delete</th>
</tr>
</thead>
<tbody>
#foreach (var user in Model.UsersInRole)
{
<tr>
<td>#user.UserName</td>
<td class="text-center">
//now each form will have the correct, row-wise formdata
<form method="post" asp-action="RemoveUser">
<input asp-for="RemoveUser.UserName" type="hidden" value="#user.UserName" />
<input asp-for="RemoveUser.RoleName" type="hidden" value="#Model.SelectedRoleName" />
<button type="submit" class="btn btn-sm btn-link text-danger py-0 my-0">
<i class="fas fa-times"></i>
</button>
</form>
</td>
</tr>
}
</tbody>
</table>
I am new to Asp.Net Mvc. I couldn't find a solution that worked for me here, if I am blind just redirect me.
I am trying to make a web-app where i can search through clients, without displaying the entire table of clients. Only after the user presses search, the search result should show as a partial view. I understand that using Ajax is the most popular way of handling something like this.
Any pointers on how to accomplish this?
My first thought was to just make a display: block/none script connected to the submit button but the page updates each time you search rendering this idea useless. That's why i could use some help with how to asynchronously update the web page with the search result.
HomeController:
using testForAutofill.Models;
//Search Functionality
[HttpPost]
public PartialViewResult Index(string searchTerm)
{
test_Db_Context db = test_Db_Context();
List<ViewNewOrderSum> orderSums;
if (string.IsNullOrEmpty(searchTerm))//Fix this.
{
orderSums = db.ViewNewOrderSum.ToList();
}
else
{
orderSums = db.ViewNewOrderSum.Where(x =>
x.ClientName.Equals(searchTerm)).ToList();
}
return PartialView(orderSums);
}
Index View:
#model IEnumerable<testForAutofill.Models.ViewNewOrderSum>
#using (Html.BeginForm())
{
<b>Kundenavn:</b>
#Html.TextBox("searchTerm", null, new { id = "txtSearch" })
<input type="submit" value="🔍 Search" class="btn btn-primary" id="btn-search" />
}
<div id="posts-wrapper"></div>
<div class="client-div" runat="server" style="max-width: 20rem;">
<div class="card-header">Header</div>
<div class="card-body" id="client-Card">
<h4 class="card-title">Client info</h4>
<table id="client-table">
<tr>
<th>
#Html.DisplayNameFor(model => model.ClientName)
</th>
</tr>
#foreach (var item in Model)
{
#Html.Partial("_OrderSum", item)
}
</table>
</div>
</div>
Partial View:
#model testForAutofill.Models.ViewNewOrderSum
<tr>
<td>
#Html.DisplayFor(modelItem => Model.ClientName)
</td>
</tr>
No need of using Ajax. You can submit search text in Form Post. Fetch your data and filter based on your searchTerm retun to View with model. If your model is not null or empty show table else do not display table.
Checkout the below code :
View :
#model List<testForAutofill.Models.ViewNewOrderSum>
#using (Html.BeginForm()) {
<b>Kundenavn:</b>
#Html.TextBox("searchTerm", null, new { id = "txtSearch" })
<input type="submit" value="🔍 Search" class="btn btn-primary" id="btn-search" />
}
#if (Model != null && Model.Count() > 0) {
<div class="client-div" runat="server" style="max-width: 20rem;">
<div class="card-header">Header</div>
<div class="card-body" id="client-Card">
<h4 class="card-title">Client info</h4>
<table id="client-table">
<tr>
<th>
ClientName
</th>
</tr>
#foreach (var item in Model) {
#Html.Partial("_OrderSum", item)
}
</table>
</div>
</div>
}
Controller :
public ActionResult Index()
{
//if you want to load all the clients by default
test_Db_Context db = test_Db_Context();
List<ViewNewOrderSum> orderSums;
orderSums = db.ViewNewOrderSum.ToList();
return View(orderSums);
}
[HttpPost]
public ActionResult Index(string searchTerm) {
test_Db_Context db = test_Db_Context();
List<ViewNewOrderSum> orderSums;
if (!string.IsNullOrEmpty(searchTerm))
{
orderSums = db.ViewNewOrderSum.Where(x =>
x.ClientName.Equals(searchTerm)).ToList();
}
return View(result);
}
My first thought was to just make a display: block/none script
connected to the submit button but the page updates each time you
search rendering this idea useless.
You can prevent the page from updating using something like the following (using jQuery):
<script type="text/javascript">
$('form').submit(function (evt) {
evt.preventDefault();
... your code
});
</script>
Then you can make your ajax POST call, get the data, unhide table headers and append the html results from your partial view.
My application searches for the files that contains a string the user is looking for. So far it does that perfectly. The last thing I need to do is to export it to an excel file so I added a method inside of my controller that is called by the Result page after I press a button.
The results are stored in a List of type Result which is a class with four variables.
The method ExportToExcel currently returns string so that I can test if the list of results is null. Every single time it has come out as "No Data", therefore it is null. It perfectly prints out a table with the information in the Results page but does not have the information when i want to export it.
Why is my model not passing from view to method?
At first I wanted to pass my model so that I can access the information in the List but now I am wondering if it would be better to save the List data in the controller so that I can directly pass it to my method.
Either way would be fine and I am open to any other ways to do this.
Model
namespace Final.Models
{
public class InputModel:Result
{
public List<Result> Results { get; set; }
}
}
Controller
This controller is just showing how I am passing the InputModel between the views and method. Maybe I am doing something wrong here?
public ActionResult Index()
{
var input = new InputModel();
return View(input);
}
[HttpPost]
public ActionResult Index(InputModel input)
{
//Finds files that contain string.
//send model to Result
return View("Result", input);
}
public ActionResult Result(InputModel input)
{
return View(input);
}
[HttpPost]
public string Result(InputModel input,string export)
{
return ExportToExcel(input);
}
public string ExportToExcel(InputModel input)
{
if (input.Results!=null)
{
//Run excel code here
return "Excel Exported";
}
else
{
return "No Data";
}
}
View for Result
This is part of the view, not the whole thing. I didn't think the full view was necessary but I posted it in the bottom just in case.
#foreach(var result in Model.Results)
{
<tr>
//Return graph of information received
</tr>
}
</table>
<form action="Find/Result" method="POST">
<input type="submit" value="Export" name="export" class="btn btn-default">
</form>
Output
Occurs after pressing the "Export" Button
"No Data"
This is my first MVC applications so once again please let me know if there is any other area I can improve in.
Full View For Result
Changed the form to enclose the entire view as suggested by Wubbly but I get the same output.
#model Final.Models.InputModel
#{
ViewBag.Title = "Result";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<br />
<h4>Result</h4>
<hr />
#using (Html.BeginForm("Result", "Find", FormMethod.Post))
{
<p>The <b>#Model.SelectedText</b> files that contain <b>"#Model.Find"</b> are: </p>
<div>
<table class="table table-bordered table-responsive table-hover">
<tr>
//HEADERS
</tr>
#foreach (var result in Model.Results)
{
// int i = 1;
<tr>
<td>#result.SelectedText</td>
<td>#result.FileName</td>
<td>#result.Line</td>
<td>#result.LineCode</td>
</tr>
}
</table>
<div class="form-group">
<div class="col-md-offset-2 ">
<input type="submit" value="Export" name="export" class="btn btn-default">
</div>
</div>
</div>
}
<p>
#Html.ActionLink("Back to List", "Index")
</p>
Use for loop with hidden property which takes your property value to model.
#using (Html.BeginForm("Result", "Find", FormMethod.Post))
{
<p>The <b>#Model.SelectedText</b> files that contain <b>"#Model.Find"</b> are: </p>
<div>
<table class="table table-bordered table-responsive table-hover">
<tr>
//HEADERS
</tr>
#for (int i = 0; i < Model.Results.Count; i++)
{
// int i = 1;
<tr>
<td>
#Html.HiddenFor(r => r.Model.Results[i].SelectedText)
#Html.HiddenFor(r => r.Model.Results[i].FileName)
#Html.HiddenFor(r => r.Model.Results[i].Line)
#Html.HiddenFor(r => r.Model.Results[i].LineCode)
#Html.DisplayFor(r => r.Model.Results[i].SelectedText)
</td>
<td>#Html.DisplayFor(r => r.Model.Results[i].FileName)</td>
<td>#Html.DisplayFor(r => r.Model.Results[i].Line)</td>
<td>#Html.DisplayFor(r => r.Model.Results[i].LineCode)</td>
</tr>
}
</table>
<div class="form-group">
<div class="col-md-offset-2 ">
<input type="submit" value="Export" name="export" class="btn btn-default">
</div>
</div>
</div>
}
<p>
#Html.ActionLink("Back to List", "Index")
</p>
To anyone who might need an answer and is in a similar situation I figured my problem out. Many of you might not think it is the right way to fix it but it is what worked for me. Either way any feedback would be appreciated to improve my abilities.
First, I did change my foreach to a for loop as recommended by StephenMuecke and ShailendraKumar.
The way I transferred the data from my HTTPGet to my HTTPPostis with TempData. I used it to store my model with the user's input inside my HTTPPost Index and called it in my HTTPPost Result.
Here's how I changed my controller.
public ActionResult Index()
{
var input = new InputModel();
input.Type = input.FillType(input.Type);
return View(input);
}
[HttpPost]
public ActionResult Index(InputModel input)
{
input.FileType = input.ValueConvert();
input.FileFind();
TempData["model"] = input
return View("Result", input);
}
public ActionResult Result(InputModel input)
{
return View(input);
}
[HttpPost]
public void Result()
{
InputModel model = new InputModel();
model = (InputModel)TempData["model"];
model.ExportToExcel();
}
I'm going round in circles now so if anyone can identify the issue here I would greatly appreciate it.
I have a partial I'm using to list out items and that works fine.
The post back to the controller works if the items passed back is just set as an ienumerable of the items but I need to pass back a model as it contains more information than just the list.
On doing this the list is empty each time and I cannot see why.
The partial:
#model RequestModel
#section Scripts {
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css" rel="stylesheet">
<style>
.btn span.glyphicon {
opacity: 0;
}
.btn.active span.glyphicon {
opacity: 1;
}
</style>
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.min.js"></script>
}
#for (var i = 0; i < Model.Requests.Count(); i++)
{
<div #(Model.Requests.Count == 3 ? "class=col-md-4" : Model.Requests.Count() == 2 ? "class=col-md-6" : "class=col-md-12")>
#Html.HiddenFor(m => Model.Requests[i].RequestID)
<table class="table table-responsive img-rounded">
<thead>
<tr class="alert-cascade">
<th colspan="2">
<div class="btn-group btn-group-sm pull-right" role="group" data-toggle="buttons">
<button class="btn btn-success" data-toggle="tooltip" title="Accept" id="acceptradio">
#Html.RadioButtonFor(m => Model.Requests[i].AcceptChecked, Model.Requests[i].AcceptChecked, new { #id = Model.Requests[i].RequestID, #style = "display:none" })
<span>Accept </span>
<span class="glyphicon glyphicon-ok" aria-hidden="true"></span>
</button>
<button class="btn btn-warning" data-toggle="tooltip" title="Reject" id="rejectradio">
#Html.RadioButtonFor(m => Model.Requests[i].RejectChecked, Model.Requests[i].RejectChecked, new { #id = Model.Requests[i].RequestID, #style = "display:none" })
<span>Reject </span>
<span class="glyphicon glyphicon-ok" aria-hidden="true"></span>
</button>
</div>
#Model.Requests[i].EmployeeDescription
</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Request Type</strong></td>
<td class="text-right">#Model.Requests[i].RequestType</td>
</tr>
<tr>
<td><strong>Duration</strong></td>
<td class="text-right">#Model.Requests[i].DurationDescription</td>
</tr>
<tr>
<td><strong>Dates</strong></td>
<td class="text-right">#Model.Requests[i].DatesDescription</td>
</tr>
</tbody>
</table>
</div>
}
The view:
#model RequestPageModel
#{
ViewData["Title"] = "Requests";
ViewData["SubTitle"] = "Welcome to Cascade Mobile";
}
#{Layout = "~/Views/Shared/_MainLayout.cshtml";}
#section Scripts {
<script type="text/javascript">
//Submit count setter
$(document).ready(function () {
var accepted = 0;
var rejected = 0;
$("#acceptradio").click(function () {
console.log("ready 2!");
$("#acceptCount").text("4");
});
});
</script>
}
<div class="container">
<h3>#ViewBag.Warning</h3>
#*Existing requests*#
<h4><strong>Requests</strong> <span class="badge alert-cascade">#Model.Pager.TotalRecords</span></h4><br />
#using (Html.BeginForm("Index", "Request", new { #id = "requestsform" }))
{
<div class="row">
#Html.Partial("_MultiSelectPartial", Model.RequestModel)
</div>
<div>
<textarea class="span6" rows="3" placeholder="Comments.." required></textarea>
</div>
<input type="submit" value="submit"/>
}
<button id="submitbtn" class="btn btn-primary pull-right" type="button">
Accept
<span class="badge" id="acceptCount">0</span>
Reject
<span class="badge" id="rejectCount">0</span>
</button>
#Html.Partial("_Pager", Model.Pager)
</div>
The controller action:
[HttpPost]
public IActionResult Index(RequestModel requests)
{
ViewBag.Warning = "We have: ";
foreach (var request in requests.Requests)
{
ViewBag.Warning += request.RequestID + " : ** : ";
}
var requestModel = GetRequestPageModel(3,1);
//requestModel.Requests[0].AcceptChecked = true;
return View("~/Views/User/Requests.cshtml", requestModel);
}
The model:
using System;
using System.Collections.Generic;
namespace Mobile.Models
{
/// <summary>
/// Request data model for the requests page
/// </summary>
public class RequestModel
{
public List<Request> Requests;
public RequestModel(List<Request> requests)
{
Requests = requests;
}
public RequestModel()
{
Requests = new List<Request>();
}
}
}
Again, if the post method takes just a list of request items its fine, but I will need to pass more information and cannot get it to post the list as part of the model. Can anyone see whats wrong here?
The model in your partial is RequestModel and your loop is generating controls with
name="Requests[0].RequestID"
name="Requests[1].RequestID"
etc, but the model in your POST method should be RequestPageModel so the correct name attributes would need to be
name="RequestModel.Requests[0].RequestID"
name="RequestModel.Requests[1].RequestID"
which will post back to
[HttpPost]
public IActionResult Index(RequestPageModel model)
You need to change the model in the partial to #model RequestPageModel (and adjust the HtmlHelper methods accordingly) and in the main view, use #Html.Partial("_MultiSelectPartial", Model).
In addition, change the name of the parameter to (say) RequestModel model so there is no conflict with the equivalent property name (refer this answer for an explanation).
I would however recommend that you use custom EditorTemplate's for your types rather than a partial, so that you controls are correctly named (refer the 2nd part of this answer for an example)
Side notes:
Your radio buttons do not makes sense, since they have different
names so you can select both (and once selected, you cannot
un-select them)
Your have a <textarea> without a name attribute so it will not
submit a value.