Getting back null model after post [duplicate] - c#

This question already has answers here:
Post an HTML Table to ADO.NET DataTable
(2 answers)
Closed 4 years ago.
It seems like a lot of people have this issue, but I haven't found a solution to my specific case yet.
I start by sending my tickets to my view
[HttpGet]
public ActionResult AddPopcorn()
{
List<Ticket> tickets = new List<Ticket>();
// Fill the list with tickets
return View("AddPopcorn", tickets);
}
In the view I display the tickets with a form and checkbox. The view displays the tickets correctly.
#model List<Domain.Entities.Ticket>
#{
ViewBag.Title = "AddPopcorn";
}
<body>
// Html stuff
#using (Html.BeginForm("AddPopcorn", "Ticket", FormMethod.Post))
{
<table>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.TicketID)
</td>
<td>
#Html.DisplayFor(modelItem => item.Price)
</td>
<td>
#Html.DisplayFor(modelItem => item.TicketType)
</td>
<td>
#Html.CheckBoxFor(modelItem => item.Popcorn)
</td>
</tr>
}
</table>
<input type="submit" value="Voeg toe!" />
}
</div>
</body>
If people check the checkbox, I want the value 'Popcorn' to be true. So I want the view to return a list of tickets with updated values for popcorn based on the checkbox.
[HttpPost]
public ActionResult AddPopcorn(List<Ticket> model)
{
foreach (var item in model)
{
if (item.Popcorn == true)
{
item.Price = item.Price + 5;
}
}
return RedirectToAction("Payment", "Payment");
}
However, the model returned to AddPopcorn is null. I can't seem to figure out why.

Try changing your foreach loop to a for:
for (int i = 0; i < Model.Count; i++)
{
<tr>
<td>
#Html.DisplayFor(x => Model[i].TicketID)
</td>
<td>
#Html.DisplayFor(x => Model[i].Price)
</td>
// etc
<td>
#Html.CheckBoxFor(x => Model[i].Popcorn)
</td>
</tr>
}
The default model binder uses a specific convention to work out how to bind a list of items, such as Model[0].Popcorn (name). You could check if the HTML for the CheckBox has a name attribute set to that format.
Other than using for, you could also specify a custom EditorTemplate for your Ticket object.

This is because your form isn't actually posting any data back to the controller.
You need input elements to actually be able to retrieve data from the form.
Instead, on the controller, access the model this way:
var model = this.Model
The form will only send data that you've got in input tags, e.g.
<input type="text" name="first_name" />

Related

Bind checkbox and textbox to viewmodel [duplicate]

This question already has answers here:
Post an HTML Table to ADO.NET DataTable
(2 answers)
Closed 5 years ago.
I have to bind checkbox value and textbox value with model.Here I am using model to retrive and to post data as well.
I have tried few options but it didnt work.
Below is my code:
#model IEnumerable<ALDS.Web.Areas.AR.Models.ReportViewModel>
#{
ViewBag.Title = "Standings";
}
<table class="table table-bordered">
<thead>
<tr>
<th>LR Date</th>
<th>LR Line No</th>
<th>Issue</th>
<th>Correction Response</th>
<th>Remarks</th>
<th>Submission</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model)
{
<tr>
<td>
#item.InsertDate
</td>
<td>
#item.LRLineID View
</td>
<td>Margin % incorrect</td>
<td><label for="cbox1">Corrected</label> <input type="checkbox" name="Corrected" value="#item.Status" /> <label for="cbox1">Neglected</label> <input type="checkbox" name="Neglected" value="#item.Status"/></td>
<td><input type="text" value="#item.Comments"/></td>
<td>Submit</td>
</tr>
}
</tbody>
</table>
I have to send the checkbox and textbox value to controller.
Please help.
Render the inputs using the HtmlHelpers provided by MVC. They will make sure that the id and name attributes of the generated <input/> are in a format that can be processed by the MVC ModelBinder.
Also, to post back lists of objects, use a for loop, so that the items get an index understood by the ModelBinder.
Wrap the inputs in a <form> to be posted to the Controller. With the following example, the model will be posted to the "Update" action in "ErrorController" when the user clicks the "Submit" button. myFormClass and myFormId are not neccessary, I just wanted to show how you could add them if needed.
#using(Html.BeginForm("Update", "Error", FormMethod.Post, new { #class = "myFormClass", id = myFormId })) {
for (var i = 0; i < Model.Length; i++) {
#Html.LabelFor(m => m[i].Status)
#Html.CheckBoxFor(m => m[i].Status)
#Html.LabelFor(m => m[i].Comments)
#Html.TextAreaFor(m => m[i].Comments) // multi line
#Html.TextBoxFor(m => m[i].Comments) // single line
}
<button type="submit">Submit</button>
}
LabelFor will try to find [Display(Name="some_resource_key", ResourceType = typeof(Resources))] attributes on the property in the ViewModel to look up the translated text to be used as label in the Resources.resx.
EDIT As Antoine mentioned, you have to provide inputs for ALL ViewModel properties that shall be posted back. You can render <input type="hidden"/> using #Html.HiddenFor(m => m[i].Id).

Failing to pass data from view to the Action by Html.BeginForm()

I am very new at asp.net mvc, so the reason behind my failure might be something basic as well, but I can't seem to find it after nearly a days work now.
What I am trying to do is to get the edited Model from the Index view and pass it to a second action which does not have view and returns return RedirectToAction("Index") in the related controller. In OrdersItemsController my Action is as the following:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult MarkedShipped(IEnumerable<orders_items> orderItems)
{
if (ModelState.IsValid)
{
foreach (var item in orderItems)
{
unitOfWork.GenericRepository<orders_items>().Update(item);
}
}
return RedirectToAction("Index");
}
And in the Index.cshtml which is in OrdersItems folder in the Views, what I did is as following:
#model IEnumerable<Project.DataAccess.orders_items>
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
#using (Html.BeginForm("MarkedShipped", "OrdersItems", new { orderItems = Model }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.quantity)
</th>
<th>
#Html.DisplayNameFor(model => model.itemprice)
</th>
<th>
#Html.DisplayNameFor(model => model.trackingnumber)
</th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.quantity)
</td>
<td>
#Html.DisplayFor(modelItem => item.itemprice)
</td>
<td>
#Html.EditorFor(modelItem => item.trackingnumber)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id = item.itemid })
</td>
</tr>
}
</table>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="MarkShipped" class="btn btn-default" />
</div>
</div>
}
My problem is, I am not able to get the Model from the view with orderItems parameter, I am not sure if this is the right "syntax" for what I am trying to accomplish; but what I get for orderItems when the action is called is a List of orders_items with Count = 0 not a null value.
I have also checked if there is an application level exception, but got no exception from Application_Error in Global.asax
I am stuck for a day now so If anyone can point me a direction on how to pass the Model (or "the edited data") to the action for me to update the db, I would be very grateful. Thanks.
Firstly you cannot add a model which is a collection (or a model which contains a property which is a complex object or collection) to a forms route parameters. Internally the helper calls the .ToString() method and if you inspect the html generated for your form tag you will see something like
<form action=OrdersItems/MarkedShipped?orderItems=System.Collection.Generic.......
and binding will fail since you collection cannot be bound to a string. Even if it did work, it would be rather pointless because it would just post back the original values of the model.
Next, you cannot use a foreach loop to generate form controls. Again if you inspect the html your generating you will see that the EditorFor() method is generating inputs with duplicate id attributes (invalid html) and duplicate name attributes which do not have the necessary indexers to bind to a collection when you post. You need to use either a for loop of a custom EditorTemplate for typeof orders_items
Using a for loop means that your model must implement IList<T> and the view needs to be
#model IList<Hochanda.CraftsHobbiesAndArts.DataAccess.orders_items>
#using (Html.BeginForm()) // only necessary to add the controller and action if they differ from the GET method
{
....
#for(int i = 0; i < Model.Count; i++)
{
#Html.DisplayFor(m => m[i].quantity)
....
#Html.EditorFor(m => m[i].trackingnumber)
}
....
}
Using an EditorTemplate, create a partial in /Views/Shared/EditorTemplates/orders_items.cshtml (note the name of the file must match the name of the class)
#model Hochanda.CraftsHobbiesAndArts.DataAccess.orders_items
<tr>
<td>#Html.DisplayFor(m => m.quantity)</td>
....
<td>#Html.EditorFor(m => m.trackingnumber)</td>
....
</tr>
and then in the main view (you can use IEnumerable<T>)
#model IEnumerable<Hochanda.CraftsHobbiesAndArts.DataAccess.orders_items>
#using (Html.BeginForm())
{
<table class="table">
<thead>
....
<thead>
<tbody>
#Html.EditorFor(m => m)
</tbody>
</table>
....
}
The EditorFor() method accepts IEnumerable<T> and will generate one row for each item in you collection based on the html in the EditorTemplate
In both cases, it you inspect the html you will now see the correct name attributes necessary for you model to bind when you post
<input type="text" name="[0].trackingnumber" ... />
<input type="text" name="[1].trackingnumber" ... />
<input type="text" name="[3].trackingnumber" ... />
Try For instead of foreach here.
#for (int i = 0; i < Model.Count; i++)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.quantity)
</td>
<td>
#Html.DisplayFor(modelItem => item.itemprice)
</td>
<td>
#Html.EditorFor(modelItem => item.trackingnumber)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id = item.itemid })
</td>
</tr>
}
Check this :- http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx/
You can do the following
for (int i = 0; i < #ViewBag.Persons; i++)
{
<li>
<table>
<tr>
<td>
#Html.LabelFor(m => m.Fullname,new { #id = "FullnameLabel" + #i })
</td>
<td>
#Html.TextBoxFor(m => m.Fullname, new { #id = "Fullname" + #i })
</td>
</tr>
In this way each html element gets its own unique id and its possible to retrieve the information back from them individually. You can use for or Foreach. Just make sure that your razor iterator is functioning. You can view the element ids in the source to see if it is working.

MVC viewmodel is null on post (editing multiple rows)

I'm new to ASP.NET MVC and have been searching for a solution to this problem for hours. I'm trying to create a site where admins can go in and approve registration user requests and also assign them to a user group. I'm getting data from multiple tables so I created a viewmodel.
I finally have the GET Edit controller working to display the data, but can't figure out how the POST Edit should work. When I was debugging, I realized that the viewmodel I was trying to return had only null values.
I'm not sure if there are many things wrong with this code or just one. On postback, I need to update some values in the Access table. If you need more information from me, just let me know. Thanks!
ViewModel:
using System.Collections.Generic;
using AccessRequests.Models;
using System.ComponentModel.DataAnnotations;
namespace AccessRequests.ViewModels
{
public class UserAccessData
{
public IEnumerable<User> Users { get; set; }
public IEnumerable<Access> Accesses { get; set; }
public IEnumerable<UserGroup> UserGroups { get; set; }
}
}
Controller:
// GET: Accesses/Edit/5
public ActionResult Edit(string brand, string group)
{
var viewModel = new UserAccessData();
viewModel.Users = db.Users
.Include(i => i.Accesses)
.OrderBy(i => i.UserName);
viewModel.UserGroups = db.UserGroups
.Where(i => i.Groups.Contains(group));
if (brand != null)
{
viewModel.Accesses = db.Accesses
.Include(x => x.User)
.Where(x => x.Brand.ToUpper() == brand);
}
return View(viewModel);
}
// POST: Accesses/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(Access access, UserAccessData editaccess)
{
//code here
}
View:
#model AccessRequests.ViewModels.UserAccessData
#{
ViewBag.Title = "Edit";
}
<h2>Edit Access</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<table class="table">
<thead>
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Email</th>
<th>Company</th>
<th>Role</th>
<th>Region</th>
<th>User Group</th>
<th>Approve</th>
<th>Deny Reason</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model.Accesses)
{
#Html.HiddenFor(modelItem => item.UserName)
<tr>
<td>
#Html.DisplayFor(modelItem => item.User.FirstName)
</td>
<td>
#Html.DisplayFor(modelItem => item.User.LastName)
</td>
<td>
#Html.DisplayFor(modelItem => item.User.Email)
</td>
<td>
#Html.DisplayFor(modelItem => item.User.Company)
</td>
<td>
#Html.DisplayFor(modelItem => item.User.Role)
</td>
<td>
#Html.DisplayFor(modelItem => item.User.Region)
</td>
<td>
#Html.DropDownListFor(m => m.UserGroups, new SelectList(Model.UserGroups, "Groups", "GroupDescription"), "Please select a User Group")
</td>
<td>
#Html.DropDownListFor(modelItem => item.Approved, new SelectList(
new List<Object>{
new { value = 0 , text = "" },
new { value = "YES" , text = "YES" },
new { value = "NO" , text = "NO"}
},"value","text", 2))
</td>
<td>
#Html.EditorFor(modelItem => item.DenyReason, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(modelItem => item.DenyReason, "", new { #class = "text-danger" })
</td>
</tr>
}
</tbody>
</table>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
<script src="~/Scripts/jquery-1.10.2.min.js"></script>
<script src="~/Scripts/jquery.validate.min.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>
In order to post to a collection property, your field names need to be in the form of something like: Accesses[0].Approved, Accesses[1].Approved, etc. In order to achieve that, you need to use a for loop rather than foreach. You'll also need to change your property's type from IEnumerable<Access> to List<Access>.
#for (var i = 0; i < Model.Accesses.Count(); i++)
{
...
#Html.DropDownListFor(m => Model.Accesses[i].Approved, ...)
}
Also, bear in mind that only those properties which have actual HTML fields that participate in the the post will have values in your post action. Everything else will either be null or whatever default value the property may have. If you save an entity with properties that have been nulled out because they weren't posted, you will overwrite those properties in your database. You need to take care to either make sure all the necessary data comes through in the post or that you repopulate said data from the the database before attempting to save anything.

select all tr of a table and send it to the controller using checkbox , in MVC 4

I want to use the Check box for selecting multiple users and sending the result to my controller.
At first I was only sending the number of mobile users to the controller but it became necessary to send more than the number, I need number and name user, my controller and my view are as follows:
Would somehow send the number and name for example?
#using (Html.BeginForm("Enviar", "Home")) {
<table id="myTable">
<thead>
<tr>
<th>
<input type="checkbox" />
</th>
<th>#Html.DisplayName("Nome")</th>
<th>#Html.DisplayName("CANCELADO")</th>
<th>#Html.DisplayName("Numero")</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model) {
<tr>
<td>
<input type="checkbox" name="CELULAR" value="#item.CELULAR" />
</td>
<td name="Nome">#Html.DisplayFor(modelItem => item.NOME)</td>
<td name="Email">#Html.DisplayFor(modelItem => item.CANCELADO)</td>
<td name="Celular" value="#item.CELULAR">#Html.DisplayFor(modelItem => item.CELULAR)</td>
</tr>
}
</tbody>
</table>
<input type="submit" value="Selecionar"/>
}
The controller that receives:
[HttpPost]
public ActionResult Enviar(String[] celular) {
.....
return View();
}
When posting collections, you must index them correctly. Therfore, you must use a for loop instead of a foreach.
Also, why does your HttpPost take a String[] and not your Model? First change that to be your model type.
Assuming your model is: #model List<YourType>
Change your HttpPost to take that:
[HttpPost]
public ActionResult Enviar(List<YourType> model) {
.....
return View();
}
Now we'll rewrite your foreach into a for and use the CheckBoxFor helper. Also, add HiddenFor fields for any properties you want to see on post:
#for (int i = 0; i < Model.Count; i++)
{
<tr>
<td>
#Html.CheckBoxFor(m => m[i].CELULAR)
</td>
<td name="Nome">
#Html.HiddenFor(m => m[i].NOME)
#Html.DisplayFor(m => m[i].NOME)
</td>
<td name="Email">
#Html.HiddenFor(m => m[i].CANCELADO)
#Html.DisplayFor(m => m[i].CANCELADO)
</td>
<td name="Celular" value="#m[i].CELULAR">
#Html.DisplayFor(m => m[i].CELULAR)
</td>
</tr>
}

Access form values in controller, i'm trying to post whole grid back to post method ASP.net MVC

I displayed a list of data in form of a grid [basic html table] and placed text boxes so that we can edit inside the grid and post the values so that I can save it. The list is not big , around 5-10 rows in it.
How to access these form values back in controller? FormCollection doesn't seem to work and I cannot even access the values through Request.Form[]. I want it back in a form of a list so that I can loop through it and get the new values.
.cshtml
<form action="/Parameter/StudentWeights" method="post">
<table>
<tr>
<th>
Category
</th>
<th>
CategoryAlias
</th>
<th>
YearCode
</th>
<th>
ClassKto8
</th>
<th>
Class9to12
</th>
<th></th>
</tr>
#foreach(var item in Model.StudentWeights) {
<tr>
<td>
#Html.HiddenFor(modelItem => item.CategoryId)
</td>
<td>
#Html.DisplayFor(modelItem => item.Category)
</td>
<td>
#Html.DisplayFor(modelItem => item.CategoryAlias)
</td>
<td>
#Html.DisplayFor(modelItem => item.YearCode)
</td>
<td>
#Html.EditorFor(modelItem => item.ClassKto8)
</td>
<td>
#Html.EditorFor(modelItem => item.Class9to12)
</td>
</tr>
}
</table>
<input type="submit" value = "Submit" />
</form>
controller
[HttpPost]
public ActionResult studentWeights(FormCollection collection)
{
try
{
// TODO: Add update logic here
//service.
foreach (var item in collection)
{
int x = item. // i want to loop through it and access the values.
}
}
catch
{
return View();
}
}
please help me how to get these values. I don't want to use JEditable or any third party jQuery tools.
Is there any way to create a custom type and assign values in JavaScript or jQuery upon button click and then send it to my controller action?
Thank you very much, any suggestion would be much helpful.
You need to access the form collection differently. The form collection is a key value pair of the values posted to the controller. formcollection["postvalue"]
[HttpPost]
public ActionResult studentWeights(FormCollection formCollection)
{
foreach (string _formData in formCollection)
{
var x = formCollection[_formData];
}
}
Here are several way to iterate through a form collection
http://stack247.wordpress.com/2011/03/20/iterate-through-system-web-mvc-formcollection/

Categories