I have a simple project that I want to send information via form and multiple check boxes in asp.net mvc.
This is the view:
And this is the controller method:
[HttpPost]
public ActionResult Index(Customer customer)
{
if (customer != null)
{
return Content(customer.Name.ToString());
}
return Content("empty");
}
I know that I am getting a single variable in the controller method but the point is that if I put an IEnumerable type like list, array or even IEnumerable itself I will receive null value, and if I put a single variable the view page only sends the value of the first checkbox.
Here you can see that I have chosen the B option but the view still returns the A option.
Model binding to a list doesn't play nicely with foreach; you need to use for instead.
ASP.NET Wire Format for Model Binding to Arrays, Lists, Collections, Dictionaries - Scott Hanselman's Blog
Which means you'll need to change your model to be an indexable list.
#model IReadOnlyList<WebApplication2.Models.Customer>
...
#for (int index = 0; index < Model.Count; index++)
{
<tr>
<td>#Html.DisplayFor(m => m[index].Name)</td>
<td>
#Html.ActionLink("Edit", "Edit", new { Model[index].ID })
#Html.ActionLink("Details", "Details", new { Model[index].ID })
#Html.ActionLink("Delete", "Delete", new { Model[index].ID })
#Html.CheckBoxFor(m => m[index].IsSelected)
#Html.HiddenFor(m => m[index].ID)
#Html.HiddenFor(m => m.[index].Name)
</td>
</tr>
}
Related
In my MVC application I have a view where I will display different data from a SQL table. This will generate different amount of drop down lists and text boxes, depending on what is passed in from the Model.
My issue is if I want to then use that data I can't seem to figure out how I can relate control X to object Y in SQL. For example, if I have 2 textboxes that I want to do an update on, then when the Post happens in my application the FormCollection parameter will let me see the Value of the objects, but not their control name or any form of identifying factor.
I could set the Value to a combination of the entered value + a name, then split this, but it seems very much like a lazy workaround.
I've tried to assign an ID to each, for example:
#foreach (DataObject item in Model.AllDataObjects)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.Data)
</td>
<td>
#if (item.Rule.Contains("Yes;No"))
{
#Html.DropDownListFor(model => item.Value, new List<SelectListItem>
{
new SelectListItem {Text="Yes", Value="Yes"},
new SelectListItem {Text="No", Value="No" }
}, new { #id = item.ObjectId });
}
else
{
#Html.TextAreaFor(model => item.Value, new { style = "width: 400px;", #rows = 5, #id = item.ObjectId })
}
</td>
</tr>
}
Edit: The following is my Post ActionResult method in the Controller, albeit it isn't complete as I can't figure out how to get an ID for the control from the FormCollection
[HttpPost]
[ValidateInput(false)]
public ActionResult UpdateData(FormCollection collection, int objectId=0)
{
try
{
int propertyTypeId = 0;
string propertyValue = string.Empty;
// Get all the control values from the collection
string[] allValues = new string[] { };
IValueProvider valueProvider = collection.ToValueProvider();
foreach(string key in collection.Keys)
{
ValueProviderResult result = valueProvider.GetValue(key);
allValues = result.RawValue as string[];
}
ObjectData objectData = _execution.GetObjectDetails(0);
UpdateDataResponse result = _execution.UpdateData(0, objectId,
objectValue, UserName);
return RedirectToAction("Details",
new { ObjectId = objectData.ObjectId, error = result.ErrorMessage });
}
catch (Exception ex)
{
// My exception handling here
}
}
So I can see in the mark-up that the controls are assigned the object ID as their own ID, but how can I get this back? When I check FormCollection I only see the values for each control, but no way of identifying which is which.
Edit: I'm using MVC version 4.
A form only submits the values of its successful controls (as name/value pairs based on the controls name and value attributes) so if you do not generate a control for the ObjectId properties, they will not be submitted.
However, you current use of foreach loop will not allow you to obtain any meaning information from the data which is posted because all your names are identical and there is no way to reliable match up which value belongs to which item in the collection. Instead use a for loop or EditorTemplate in the view and bind to your model, rather than using FormCollection.
The view should be
#for (int i = 0; i < Model.AllDataObjects.Count; i++)
{
<tr>
<td>#Html.DisplayFor(m => m.AllDataObjects[i].Name)</td>
<td>#Html.DisplayFor(m => m.AllDataObjects[i].Data)</td>
<td>
#Html.HiddenFor(m => m.AllDataObjects[i].ObjectId)
#if (Model.AllDataObjects[i].Rule.Contains("Yes;No"))
{
#Html.DropDownListFor(m => m.AllDataObjects[i].Value, new SelectList(new string[]{ "Yes", "No" }));
}
else
{
#Html.TextAreaFor(m => m.AllDataObjects[i].Value, new { style = "width: 400px;", #rows = 5 })
}
</td>
</tr>
}
And assuming the model in the view is #model MyModel, change the POST method to
[HttpPost]
[ValidateInput(false)]
public ActionResult UpdateData(MyModel model)
and the value of model.AllDataObjects will contain a collection with its ObjectId and Value properties correctly bound.
For more information on why using a foreach loop will not work, refer to this answer.
This question already has answers here:
Post an HTML Table to ADO.NET DataTable
(2 answers)
Closed 7 years ago.
I am displaying a list of items in a Collection in edit mode in a view. after editing the documents, I want to submit. But I am unable to postback the list. List shows null.
here is my View
#model List<NewsLetter.Models.NewsLetterQuestions>
#using (Html.BeginForm("GetAnswersfromUser", "NewsLetter", FormMethod.Post, null))
{
#Html.AntiForgeryToken()
foreach (var item in Model) {
<div>
#Html.DisplayFor(modelItem => item.Question)
</div>
<div>
#Html.TextAreaFor(modelItem => item.Answer)
</div>
}
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Submit" class="btn btn-default" />
</div>
</div>
}
Here is my Controller
public ActionResult GetAnswersfromUser(string id)
{
id = "56c5afc9afb23c2df08dd2bf";
List<NewsLetterQuestions> questions = new List<NewsLetterQuestions>();
var ques = context.NewsLetterQuestionCollection.Find(Query.EQ("NewsLetterId", id));
foreach(var x in ques)
{
questions.Add(x);
}
return PartialView(questions);
}
[HttpPost]
public ActionResult GetAnswersfromUser(List<NewsLetterQuestions> nql)
{
string id = "56c5afc9afb23c2df08dd2bf";
foreach (var item in nql)
{
var query = Query.And(Query.EQ("NewsLetterId", id), Query.EQ("Question", item.Question));
var update=Update<NewsLetterQuestions>
.Set(r => r.Answer, item.Answer);
context.NewsLetterQuestionCollection.Update(query,update);
}
return RedirectToAction("NewsLetterIndex");
}
When i hit submit it throws error.
System.NullReferenceException: Object reference not set to an instance of an object.
In the line
foreach (var item in nql)
which means that nql is null.
In order for the model binder to be able to bind the posted data, all your input names need to be in the format of [N].Property, where N is the index of the item within the list. In order for Razor to generate the input names properly, then, you need to pass it an indexed item, which means you need a for loop, rather than a foreach:
#for (var i = 0; i < Model.Count(); i++)
{
...
#Html.TextAreaFor(m => m[i].Answer)
...
}
You're never passing the list back to the controller's Post handler. You need to route the list back to the controller.
You should be doing something similar to this untested code :)
Html.BeginForm("Index", "Home", new { #nql=Model }, FormMethod.Post)
Take a look at this post as well. It is similar to your issue: Pass multiple parameters in Html.BeginForm MVC4 controller action and this Pass multiple parameters in Html.BeginForm MVC
I am getting this error and I'm not sure if I am able to do this, here is my code..
Application controller
public ActionResult AppView()
{
List<Application> apps;
using (ISiteDbContext context = _resolver.GetService<ISiteDbContext>())
{
apps = context.Applications.ToList();
}
return PartialView("AppView", apps.OrderBy(a => a.Name).ToList());
}
Render partial - this is within a view which is in the home controller.
#{Html.RenderPartial("~/Views/Application/AppView.cshtml", new Example.Services.DAL.Application());}
and my application view
#model IEnumerable<Example.Services.DAL.Application>
#{
ViewBag.Title = "Applications";
}
<h2>Applications</h2>
<p>
#Html.ActionLink("Add New Application", "Create")
</p>
<table>
<tr>
<th>
#Html.DisplayNameFor(model => model.Name)
</th>
<th></th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Name)
</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>
full error message:
The model item passed into the dictionary is of type
'Example.Services.DAL.Application', but this dictionary requires a
model item of type
'System.Collections.Generic.IEnumerable`1[Example.Services.DAL.Application]'.
As error states you are passing a wrong type. Change
#{Html.RenderPartial("~/Views/Application/AppView.cshtml", new Example.Services.DAL.Application());}
to:
#{Html.RenderPartial("~/Views/Application/AppView.cshtml", new List<Example.Services.DAL.Application> { new Example.Services.DAL.Application() });}
Your AppView.cshtml is bind to strongly type of #model IEnumerable<Example.Services.DAL.Application> and while calling this view you are passing #{Html.RenderPartial("~/Views/Application/AppView.cshtml", new Example.Services.DAL.Application());}
It should be the list object. You must pass list of Example.Services.DAL.Application()
Change your
#{Html.RenderPartial("~/Views/Application/AppView.cshtml", new Example.Services.DAL.Application());}
to
#{Html.RenderPartial("~/Views/Application/AppView.cshtml", new List<Example.Services.DAL.Application> { new Example.Services.DAL.Application() });}
your code is looking for a Ienumerable because what you pass to your partial view must be the same as what is in your view so try to change the first line of your application view to
#model Example.Services.DAL.Application
it worked for me hopefully it was of good use to you as well :D
In order to use sorting on a collection of (custom) Objects, you need a method for sorting it. Usually, this is accomplished by inheriting the "IComparable" interface. In your Object class, you then need a method "Compare" to determine the method of comparing the instances of your Objects for sorting (I use "Date" in my project).
To recap:
You use this in your Application Controller:
return PartialView("AppView", apps.OrderBy(a => a.Name).ToList());
But in order to actually sort (or in this case OrderBy), you need a method in your "Application" class that compares the instances in the list to sort them. This is done using the "Compare" method:
int Compare(Object x, Object y)
How you compare is totally up to you. The result however, is either:
Less than zero: Object x < Object y
Zero: Object x = Object y
Greater than zero: Object x > Object y
I hope this helps. Good luck!
Dear regards,
Björn
Can't seem to get the list to remove the correct item.
I've put breakpoints in the code to test if the id is the correct index with shipment.shipmentfreight[id] within the Immediate Window and it shows the correct list item, but it always removes the last item.
In my view model I have a list of ShipmentFreightLine declared as:
public List<ShipmentFreightLine> shipmentfreight { get; set; }
I have a call back that removes the line that returns the new partial view:
public virtual ActionResult ShipmentNewFreightLineRemove(int id,NewShipment shipment)
{
int cnt = shipment.shipmentfreight.Count();
if (id >= 0 && cnt != 1 && id <= cnt)
{
shipment.shipmentfreight.RemoveAt(id);
//shipment.shipmentfreight.RemoveAt(shipment.shipmentfreight.IndexOf(shipment.shipmentfreight[id]));
//shipment.shipmentfreight.Remove(shipment.shipmentfreight[id]);
//shipment.shipmentfreight[id]
}
return PartialView("ShipmentNewFreightLineEdit", shipment);
}
As you can see I've commented out different variations that I have tried and each one removes the last item in the list.
What am I missing?
I am coding in VS2012 ASP.NET MVC using C#.
Any help would be aprreciated
As requested by Kirk the id field is from an anchor
Remove
Which calls this javascript function
function RemoveFreightLine(index) {
var ShipmentData = $('#fShipmentNew').serialize();
var url = "/Tracking/ShipmentNewFreightLineRemove/" + index
$.post(url, ShipmentData,
function (data) {
$("#dNewFreightLines").html(data);
});
}
These all have the correct id in them so i don't believe its the cause
I traced the view and found that it is removing the correct item in the list but the view is showing the old data
<table id="tFreight" class ="tblFreight">
<tbody>
#for (int i = 0; i < Model.shipmentfreight.Count; i++)
{
<tr>
<td>#(i+1)</td>
<td>#Html.TextBoxFor(m => m.shipmentfreight[i].Pieces, new { autocomplete = "off", #Class = "txtboxsmall",maxlength ="3"})</td>
<td>#Html.TextBoxFor(m => m.shipmentfreight[i].Description,new { autocomplete = "off", #Class = "txtareasmall"})</td>
<td>#Html.TextBoxFor(m => m.shipmentfreight[i].WeightPerPiece, new { autocomplete = "off", #Class = "txtboxsmall",maxlength ="3"})</td>
<td>#Html.TextBoxFor(m => m.shipmentfreight[i].ActualWeight, new { autocomplete = "off", #Class = "txtboxsmall",maxlength ="3"})</td>
<td>#Html.TextBoxFor(m => m.shipmentfreight[i].Length, new { autocomplete = "off", #Class = "txtboxsmall",maxlength ="3"})</td>
<td>#Html.TextBoxFor(m => m.shipmentfreight[i].Width, new { autocomplete = "off", #Class = "txtboxsmall",maxlength ="3"})</td>
<td>#Html.TextBoxFor(m => m.shipmentfreight[i].Height, new { autocomplete = "off", #Class = "txtboxsmall",maxlength ="3"})</td>
<td>Remove</td>
<td>#Model.shipmentfreight[i].Description</td>
</tr>
}
</tbody>
</table>
The last cell was for debugging purposes and does display the correct information.
I am thinking its some quirk with TextBoxFor that I don't know about.
Basically the last cell is showing the correct data from the new list but anything in the TextBoxFor is showing the old list data.
Any ideas on why I am getting this behavior?
Looks like this was a problem with the ViewData and not the List.
This seems to fix the problem:
ViewData = null;
or
ModelState.Clear();
Found the answer with Why won't a List of complex types bound to TextBoxes in a table show changes to the model in MVC 4? and TextBoxFor Helper retains previous value even when model value is empty
As with all things...Finding the real cause is key to finding the answer.
Here is what my view looks like:
#model Affiliate
<div class="box paint color_16">
<div class="title">
<h4><i class="icon-tasks"></i><span>#Model.CompanyName's Commissions</span> </h4>
</div>
<div class="content top ">
<div class="subtitle">
#Html.ActionLink("Void", "DeleteInvoice", new { commList = "??", affId = Model.Id }, new { #class = "btn" })
#Html.ActionLink("Create Invoice", "CreateInvoice", new { commList = "??", affId = Model.Id }, new { #class = "btn" })
#Html.ActionLink("Pay", "PayInvoice", new { commList = "??", affId = Model.Id }, new { #class = "btn" })
</div>
<table class="table table-striped table-hover">
<tr>
<h3>Commissions</h3>
</tr>
<tr>
<td></td>
<td>Amount</td>
<td>Status</td>
<td>Action</td>
</tr>
#foreach (var item in Model.Commissions)
{
<tr>
#if (item.Status == ViewBag.PaymentStatus || ViewBag.PaymentStatus == "All")
{
<td>#Html.CheckBox("commId", new { value = item.Id })</td>
<td>#Html.DisplayFor(x => item.PayoutAmount)</td>
<td>#Html.DisplayFor(x => item.Status)</td>
}
</tr>
}
</table>
</div>
What I want to be able to do is when I hit an actionlink on the top, grab all the items from the table that are checked, and pass that list of id's to the controller logic. I am assuming a viewmodel may be the solution, something like this:
public Affiliate affilite { get; set; }
public List<int> selectedItems { get; set; }
etc.
But how to I get the selected Items into that VM selectedItems container?
Based off your comments, you don't seem to be looking for the most "correct" answer, but rather just a quick and dirty "how would I do this" answer. If you just want to pass the list, you could setup your controller action like this:
public ActionResult MyAction(int[] id)
{
...
}
Or, you seem to indicate it is strongly typed to a view model with a property that contains a List (I would shorten the name of the property, you'll see why in a second).
In javascript, the easiest thing to do would be to use jQuery to bind a click event on your hyperlink that gets the list of items that are checked and appends that to the query string.
$("#myLink").click(function()
{
var url = "site.com/action?";
var ids = $(".table").find("input:checked");
ids.each(function()
{
url += "id=" + $(this).val() + "&"
});
window.location = url;
});
Basically, you want to create one long query string with the action parameter's name repeated over and over, which identifies an array. It looks something like this (id is for int[] id in MyAction):
id=15&id=20&id=25&id=30&....
And then once the query string is built, redirect the user to that url. MVC should then be able to bind that to an array and you're all set.
That's basically the idea, anyway; the syntax and the javascript I wrote could be way off so don't copy my code and expect it to work as is - I wrote that off the top of my head. If your action is bound to a viewmodel, then you need to set the parameter in the query string to the name of the property of your model:
selectedids=1&selectedids=2&selectedids=3...
Or, if the array is a property of an object, which is a property of the model...
model.selectedids=1&model.selectedids=2&model.selectedids=3...
You'll just need to play around with it some.
Use html checks inside form tag ( you could use helpers too) and post the model to a post action.
MVC will serialize the model automatically