Ola.. I have a problem with editing of few models in table. I have a List of models on page (with foreach), something like
<table id="grid-table" >
#foreach (var image in ViewBag.Images)
{
<tr>
<td >
<a href="#Url.Action("ShowFullImage", new { id = #image.ID })" rel="lightbox[roadtrip]" title="#image.Description" >
<img src="#Url.Action("ShowImageThumbneil", new { id = #image.ID })" alt="#image.AlternateText" />
</a>
</td>
<td >
#using (Html.BeginForm("SaveImageInfo", "Admin", FormMethod.Post))
{
#Html.TextAreaFor(m => m.Description) <br />
#Html.TextBoxFor(m => m.AlternateText) <br />
#Html.LabelFor(m => m.ID)
<div id="item-post" >
<input title="Подтвердить" type="submit" value="Подтвердить" />
</div>
}
</td>
</tr>
}
and I want to have a way to edit ONE model item. In controller I have something like this:
[HttpPost]
public ActionResult SaveImageInfo(ImageModel imageModel)
{
Image img = _core.GetImageByID(_client, imageModel.ID);
img.AlternateText = imageModel.AlternateText;
img.Description = imageModel.Description;
_core.SaveImageInfo(_client, img);
return View();
}
but, of course, it's not works..
Can somebody help me?
Change #Html.LabelFor(m => m.ID) with #Html.HiddenFor(m => m.ID). Contents of labels are not sent with POST, but contents of hidden fields are..
I am guessing a bit, as I don't know exactly what problem you are seeing, but if it is to do with it trying to redirect, following the save, then try changing from:
Html.BeginForm
to
Ajax.BeginForm
Related
I have an application that shows a checklist with a dynamic list of tasks. For now all the tasks have 2 radio buttons: one for Done and one for Not Done. I will be adding new task types (textboxes etc.) later.
The main view calls an editor template that creates each task. The checklist model keeps the tasks in a property "Tasks" of type List<Task>. This is fine for display, but has trouble on form submit.
I want to have each task save independently, so I have placed a <form> element around the controls in the editor template.
Controller
public class CheckController : Controller
{
[HttpGet]
public ActionResult Index()
{
var checklist = new Checklist();
GetData(checklist);
return View(checklist);
}
[HttpPost]
public ActionResult Index(Task model)
{
var task = (Task)model;
return Content($"Save Task:{task.TaskId} Value:{task.IsDone}");
}
}
Models
public class Checklist
{
public List<Task> Tasks { get; set; }
public Checklist()
{
Tasks = new List<Task>();
}
}
public class Task
{
public int TaskId { get; set; }
public string TaskName { get; set; }
public bool IsDone { get; set; }
}
Views
#model Checkpoint.Models.Checklist
<table>
#Html.EditorFor(x => x.Tasks);
</table>
#model Checkpoint.Models.Task
<tr>
<td>#Model.TaskName</td>
<td data-ftid="#Model.TaskId">
#{
using (Html.BeginForm("Index", "Check", FormMethod.Post))
{
#Html.HiddenFor(x => x.TaskId)
#Html.RadioButtonFor(x => x.IsDone, false)<span>Incomplete</span>
#Html.RadioButtonFor(x => x.IsDone, true)<span>Complete</span>
<button role="button" type="submit" name="taskAction">Submit</button>
}
}
</td>
</tr>
Rendered HTML
<tr>
<td>First thing</td>
<td>
<form action="/Checklist" method="post">
<input id="Tasks_0__TaskId" name="Tasks[0].TaskId" type="hidden" value="1" />
<input checked="checked"id="Tasks_0__IsDone" name="Tasks[0].IsDone" type="radio" value="False" />
<span>Incomplete</span>
<input id="Tasks_0__IsDone" name="Tasks[0].IsDone" type="radio" value="True"
<span>Complete</span>
<button role="button" type="submit" name="taskAction">Submit</button>
</form>
</td>
</tr>
<tr>
<td>Second thing</td>
<td>
<form action="/Checklist" method="post">
<input id="Tasks_1__TaskId" name="Tasks[1].TaskId" type="hidden" value="2" />
<input checked="checked" id="Tasks_1__IsDone" name="Tasks[1].IsDone" type="radio" value="False" />
<span>Incomplete</span>
<input id="Tasks_1__IsDone" name="Tasks[1].IsDone" type="radio" value="True" />
<span>Complete</span>
<button role="button" type="submit" name="taskAction">Submit</button>
</form>
</td>
</tr>
This does submit the request, but the data looks like this:
Tasks[1].TaskId: 1
Tasks[1].IsDone: True
When it reaches the controller action (which accepts type Task), the property values are null.
How can I get my task data correctly in the controller action?
Or am I going about this in the totally wrong way?
Bonus: what would be the best approach for adding the new task types?
Since you're not trying to post the collection all in one submission you'll need to lose the index on the model name.
Tasks[0].TaskId
Adjust your views. The variable name "task" needs to match the action parameter because the HtmlHelpers will generate name attributes based off this string.
#model Checkpoint.Models.Checklist
<table>
#foreach(Task task in Model.Tasks)
{
#Html.EditorFor(m => task)
}
</table>
The rendered html looks something like
<input id="task_TaskId" name="task.TaskId" type="hidden" value="1">
...
The action needs a "task" parameter to match to bind its values
[HttpPost]
public ActionResult Index(Task task)
{
return Content($"Save Task:{task.TaskId} Value:{task.IsDone}");
}
If we use a partial view instead of an editor template we can somewhat relax the name dependency on "task".
#model Checkpoint.Models.Checklist
<table>
#foreach(Task t in Model.Tasks)
{
#Html.PartialView("_TaskForm", t)
}
</table>
And your editor template content will work just the same.
_TaskForm
#model Checkpoint.Models.Task
<tr>
<td>#Model.TaskName</td>
<td data-ftid="#Model.TaskId">
#{
using (Html.BeginForm("Index", "Check", FormMethod.Post))
{
#Html.HiddenFor(x => x.TaskId)
#Html.RadioButtonFor(x => x.IsDone, false)<span>Incomplete</span>
#Html.RadioButtonFor(x => x.IsDone, true)<span>Complete</span>
<button role="button" type="submit" name="taskAction">Submit</button>
}
}
</td>
</tr>
And the rendered html
<input id="TaskId" name="TaskId" type="hidden" value="1">
...
The gotcha with all of the above is the html id attributes will not be unique. So if that's critical you'll need to manually iterate the collection to build each form manually so you can exactly specify the id values. That is, you'd lose the small modular editor templates.
Letting the framework auto-generated "unique" ids... we override the name attribute (mind the capitalization for "Name") we have:
#model Checkpoint.Models.Checklist
<table>
#for(int i = 0; i < Model.Tasks.Count; i++)
{
<tr>
<td>#Model.Tasks[i].TaskName</td>
<td data-ftid="#Model.Tasks[i].TaskId">
#using (Html.BeginForm("Index", "Check", FormMethod.Post))
{
#Html.HiddenFor(x => Model.Tasks[i].TaskId, new { Name="TaskId" })
#Html.RadioButtonFor(x => Model.Tasks[i].IsDone, false, new { Name="IsDone" })<span>Incomplete</span>
#Html.RadioButtonFor(x => Model.Tasks[i].IsDone, true, new { Name="IsDone" })<span>Complete</span>
<button role="button" type="submit" name="taskAction">Submit</button>
}
</td>
</tr>
}
</table>
I was able to make this work with the Editor Template by dropping the helpers and creating the controls manually.
#model Checkpoint.Models.Task
<tr>
<td>#Model.TaskName</td>
<td data-ftid="#Model.TaskId">
#using (Html.BeginForm("Index", "Check", FormMethod.Post))
{
<input name="TaskId" type="hidden" value="#Model.TaskId" />
<input name="IsDone" type="radio" value="True" #Html.Raw(Model.IsDone ? "checked=checked" : null) />
<span>Complete</span>
<input name="IsDone" type="radio" value="False" #Html.Raw(!Model.IsDone ? "checked=checked" : null) />
<span>Incomplete</span>
<button role="button" type="submit" name="taskAction" value="#Model.TaskId">Submit</button>
}
</td>
</tr>
I wasn't overly happy with this and opted instead for #Jasen's partial view solution.
I was able to obtain unique element IDs by overriding the 'id' attribute in the helper.
I used the TaskId since it's unique, but could otherwise have passed in a loop index from the main view.
#model Checkpoint.Models.Task
<tr>
<td>#Model.TaskName</td>
<td data-ftid="#Model.TaskId">
#using (Html.BeginForm("Index", "Check", FormMethod.Post))
{
#Html.HiddenFor(x => x.TaskId, new { id = "Task_" + Model.TaskId + "_hidIsDone" })
#Html.RadioButtonFor(x => x.IsDone, false, new { id = "Task_"+ Model.TaskId + "_radIsDoneF" })<span>Incomplete</span>
#Html.RadioButtonFor(x => x.IsDone, true, new { id = "Task_" + Model.TaskId + "_radIsDoneT" })<span>Complete</span>
<button role="button" type="submit" name="taskAction">Submit</button>
}
</td>
</tr>
i have problem creating view with multiple forms with in same view where i have get action for data view and post action for data collection.
Here is my main view:
#model Models.BuildingNewBuildingViewModel
#{
ViewBag.Title = "Index";
}
#if (TempData["Added"] != null)
{
<div class="alert alert-success alert-dismissible fade in" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
#TempData["Added"]
</div>
}
#if (TempData["AddError"] != null)
{
<div class="alert alert-danger alert-dismissible fade in" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
#TempData["AddError"]
</div>
}
<h1>Svěřené budovy pro: #ViewBag.Name</h1>
<table class="table-bordered table-responsive table table-condensed">
<thead>
<tr>
<th>Id budovy</th>
<th>Podlaží</th>
<th>Použití</th>
<th>Výměra</th>
<th>Datum</th>
<th>Administrace</th>
</tr>
</thead>
<tbody>
#for (int i = 0; i < Model.NewBuildingViewModels.Count; i++)
{
using (Html.BeginForm("Index", "Home", FormMethod.Post, new { #class = "form-horizontal", id = "Form" }))
{
Html.RenderPartial("IndexPartial", Model.NewBuildingViewModels[i]);
}
}
</tbody>
</table>
Here is form:
#model Models.NewBuildingViewModel
#Html.HiddenFor(x => x.Building, Model.Building)
<tr>
<td>#Model.Building</td>
<td>
#if (Model.Floor > 0)
{
#Model.Floor
#Html.HiddenFor(x => x.Floor)
}
else
{
#Html.TextBoxFor(x => x.Floor, new {#Value = ""})
#Html.ValidationMessageFor(x => x.Floor)
}
</td>
<td>
#if (Model.Usage != null)
{
#Model.Usage
#Html.HiddenFor(x => x.Usage)
}
else
{
#Html.TextBoxFor(x => x.Usage)
#Html.ValidationMessageFor(x => x.Usage)
}
</td>
<td>
#if (Model.Size > 0)
{
#Model.Size
#Html.HiddenFor(x => x.Size)
}
else
{
#Html.TextBoxFor(x => x.Size, new {#Value = ""})
#Html.ValidationMessageFor(x => x.Size)
}
</td>
<td>
#if (Model.Date != DateTime.MinValue)
{
#Model.Date.Date
#Html.HiddenFor(x => x.Date)
}
else
{
#Html.TextBoxFor(x => x.Date)
#Html.ValidationMessageFor(x => x.Date)
}
</td>
<td>
#if (!Model.NewBuildingDataInDatabase)
{
<button type="submit">Odeslat</button>
}
else
{
if (Model.MarksInDatabase)
{
<i class="fa fa-check checkIcon" aria-hidden="true"></i>
}
else
{
Zadat Hodnocení
}
}
</td>
</tr>
}
And my problem is when i send non valid data to controller. Validation message shows for all forms. I want to see all forms but validation messages only for form which sended data.
Here is my action:
[HttpPost]
public ActionResult Index(NewBuildingViewModel buildingNewBuildingViewModel)
{
BuildingForIcoCommand buildingForIcoCommand = new BuildingForIcoCommand();
var buildingsForIco = buildingForIcoCommand.GetBuildingListForIco(AppContext);
if (ModelState.IsValid)
{
ViewBag.Name = User.Identity.Name;
ViewModelBuilder viewModelBuilder = new ViewModelBuilder();
DaoBase<NewBuilding, int> daoBase = new DaoBase<NewBuilding, int>(AppContext.NhSession);
daoBase.Save(viewModelBuilder.CreateNewBuilding(buildingNewBuildingViewModel));
return View(buildingForIcoCommand.GetBuildingListForIco(AppContext));
}
return View("Index",buildingsForIco);
}
All of your form fields will have the name and id attributes because you are rendering them in a partial. This is why they are all flagged as invalid.
Any good solution will probably involve ajax.
One approach to revolving this would be to use a single edit form. Each rendered item would have an edit button, clicking this button would populate the form with the item's data via javascript. This could be a modal or similar. This approach works well with jQuery / unobtrusive validation.
Another approach would be to return a custom response from your controller eg status 400 and the invalid fields derived from the model state. You can then show a suitable error message in the right field.
On my Main View I have 4 partial views.. two are tables.. the others are create forms.
Partial View Table 1
#model IEnumerable<ProjectName.Models.code_AutoMake>
<h3>Auto Make List</h3>
<table id="Auto-Make-Table" class="table table-bordered table-striped">
<thead>
<tr>
<th class="col-md-5">
#Html.DisplayNameFor(model => model.AutoMake)
</th>
<th class="col-md-5">
#Html.DisplayNameFor(model => model.Active)
</th>
<th></th>
</tr>
</thead>
<tbody>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.AutoMake)
</td>
<td>
#Html.DisplayFor(modelItem => item.Active)
</td>
#if (!item.Active)
{
<td>
#Html.ActionLink("Edit", "Edit", new { id = item.MakeID }) |
Activate
</td>
}
else
{
<td>
#Html.ActionLink("Edit", "Edit", new { id = item.MakeID }) |
Deactivate
</td>
}
</tr>
}
</tbody>
</table>
Partial View Table 2
#model IEnumerable<ProjectName.Models.code_Funding>
<h3>Funding List</h3>
<table class="table table table-bordered table-striped">
<tr>
<th>
#Html.DisplayNameFor(model => model.Funding)
</th>
<th>
#Html.DisplayNameFor(model => model.Active)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Funding)
</td>
<td>
#Html.DisplayFor(modelItem => item.Active)
</td>
<td>
#Html.ActionLink("Edit", "Edit", "code_Funding",new { id=item.FundID }, null) |
</td>
</tr>
}
</table>
Partial View 1 Create
#model ProjectName.Models.code_AutoMake
#using (Html.BeginForm("Create", "code_AutoMake", FormMethod.Post))
{
#Html.AntiForgeryToken()
<h3>Add Auto Make</h3>
<div class="form-horizontal">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="row">
<div class="col-sm-12 col-md-3">
#Html.Label("Auto Make")
#Html.EditorFor(model => model.AutoMake, new {htmlAttributes = new {#class = "form-control"}})
</div>
<div class="col-sm-12 col-md-3">
#Html.Label("Active")
<div class="checkbox">
#Html.EditorFor(model => model.Active)
</div>
</div>
</div>
<div class="form-group">
<div class="col-sm-12 col-md-3">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
Partial View 2 Create
#model ProjectName.Models.code_Funding
#using (Html.BeginForm("Create", "code_Funding", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
<h3>Add Funding</h3>
<div class="form-horizontal">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="row">
<div class="col-sm-12 col-md-3">
#Html.Label("Funding")
#Html.EditorFor(model => model.Funding, new {htmlAttributes = new {#class = "form-control"}})
</div>
<div class="col-sm-12 col-md-3">
#Html.Label("Active")
<div class="checkbox">
#Html.EditorFor(model => model.Active)
</div>
</div>
</div>
<div class="form-group">
<div class="col-sm-12 col-md-3">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
Main View
<div id="AutoMake" class="tab-pane fade active in">
<div id="AutoMake-Index">#{Html.RenderAction("Index", "code_AutoMake");}</div>
<hr/>
#{Html.RenderAction("Create", "code_AutoMake");}
</div>
#*Funding*#
<div id="Funding" class="tab-pane fade">
#{Html.RenderAction("Index", "code_Funding");}
<hr/>
#{Html.RenderAction("Create", "code_Funding");}
</div>
Now here is the scenario.. When I want to create a new autoMake.. I fill out the form and hit submit.. this goes through fine.. until I get back to the Main View.. specifically this line:
#{Html.RenderAction("Create", "code_Funding");}
and I get a runtime error saying:
Child Actions are not allowed to perform redirect actions
I have debugged.. and for some reason.. the HttpPost Create action for code_Funding is being hit.. even when I'm not filling out the create form for code_funding.. How is that possible?
Here are my Create Methods for code_autoMake and code_funding:
code_Funding
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "FundID,Funding,Active")] code_Funding code_Funding)
{
try
{
if (ModelState.IsValid)
{
db.code_Funding.Add(code_Funding);
db.SaveChanges();
return RedirectToAction("EditDDL", "tblNewAutos");
}
}
catch (DbEntityValidationException ex)
{
foreach (var entityValidationErrors in ex.EntityValidationErrors)
{
foreach (var validationError in entityValidationErrors.ValidationErrors)
{
Response.Write("Property: " + validationError.PropertyName + " Error: " + validationError.ErrorMessage);
}
}
}
return RedirectToAction("EditDDL", "tblNewAutos");
}
code_autoMake
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "MakeID,AutoMake,Active")] code_AutoMake code_AutoMake)
{
if (ModelState.IsValid)
{
db.code_AutoMake.Add(code_AutoMake);
db.SaveChanges();
return PartialView("~/Views/PartialViews/_AutoMakeCreate.cshtml");
}
return RedirectToAction("EditDDL", "tblNewAutos");
}
Why when I try and create a new automake.. both HttpPost Create methods are hit?
Any help is appreciated.
Well, the problem is following. In your main view you have got this code:
...
#{Html.RenderAction("Create", "code_AutoMake");}
...
Which triggers the Create action which finishes with the following line of code if ModelState.IsValid == false:
return RedirectToAction("EditDDL", "tblNewAutos");
That is obviously a bad idea. Why? You are already in a process of rendering a parent view. Child actions might be a bit confusing at first because they are not real actions - no client/server communication. You are still on the server side. Therefore no redirect is allowed in the child action.
Solutions
First of all, I am not quite sure what you want to achieve so my solution recommendation might be a bit off, but let's see.
Option 1
You may want to use two different actions. One that is called on submit of the form and another one that is called from your main view. The latter one should not make a redirect - instead it should wisely choose which view to render based on the ModelState.IsValid if this is really what you need.
Option 2
There is a hack way which allows you to make redirect from a child action. Instead of making a redirect, only store information about required redirect for instance in HttpContext.Items collection. Then, implement an ActionFilter and in its OnResultExecuted event, check if the redirect request was set to the HttpContext.Items. If so, make a redirect. The ActionFilter should be applied on the parent action, not on the child action.
#{Html.RenderAction("Create", "code_Funding");}
in this RenderAction Method is call the GET request but, your controller you written only post method. and you write the
[ChildActionOnly]
public ActionResult Create(string parm)
{
reurn view()
}
and use [ChildActionOnly] this attribute is allowing restricted access via code in View.
Check and reply me..
I am trying to return the results of a table back to the controller for further manipulation. Once returned to the controller the value shows as null. In the past I have been able to use #Html.HiddenFor to return the values but it doesn't seem to be working in this instance. Not sure what I am doing wrong here. Any help is greatly appreciated.
#model IEnumerable<Project.Models.Item>
#{
ViewBag.Title = "Welcome to The Project";
}
#using (Html.BeginForm("UpdateQuality", "Home", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
{
<div class="row">
<div class="form-group">
<table class="table table-bordered">
<tr>
<th>#Html.DisplayNameFor(m => m.Name)</th>
<th>#Html.DisplayNameFor(m => m.SellIn)</th>
<th>#Html.DisplayNameFor(m => m.Quality)</th>
</tr>
#for (int i = 0; i < Model.Count(); i++)
{
<tr>
<td>#Html.DisplayFor(m => m.ElementAt(i).Name)</td>
<td>#Html.DisplayFor(m => m.ElementAt(i).SellIn)</td>
<td>#Html.DisplayFor(m => m.ElementAt(i).Quality)</td>
#Html.HiddenFor(m => m.ElementAt(i).Name)
#Html.HiddenFor(m => m.ElementAt(i).SellIn)
#Html.HiddenFor(m => m.ElementAt(i).Quality)
</tr>
}
</table>
<div class="form-group">
<div style="margin-top: 50px">
<input type="submit" class="btn btn-primary" value="Advance Day"/>
</div>
</div>
</div>
</div>
}
And here is the controller which returns null.
public ActionResult UpdateQuality(List<Item> Items )
{
return View("Index", (object)Items);
}
You cannot use ElementAt() in a HtmlHelper method that generates form controls (look at the name attribute your generating - it does not match your model).
Either change the model to be IList<T>
#model List<Project.Models.Item>
and use a for loop
#for (int i = 0; i < Model.Count; i++)
{
....
#Html.HiddenFor(m => m.[i].Name)
....
or change use a custom EditorTemplate for typeof Item, and in the main view, use #Html.EditorFor(m => m) to generate the correct html for each item in the collection.
After many years of getting a lot of great advice here, I finally have hit a wall teaching myself MVC4 ASP.net.
I used this post this post to pass a List of Type Class from my controller, to my view, and back to the controller..
public ActionResult SelectProducts()
{
displayProductsList = db.Products.ToList();
displayProductsList.ForEach(delegate(Product p)
{
//get list of recievables for the product
GetReceivablesByProductId(p.ProductID).ForEach(delegate(Receivable r)
{
//Get count of items in inventory for each recievable
p.CurrentInventory += this.CountItemsByReceivableID(r.RecievableID);
});
});
return View(FilterProductInventoryList(displayProductsList));
}
And here is my view code..
#model List<CarePac2.Models.Product>
#{
ViewBag.Title = "SelectProducts";
}
<h2>SelectProducts</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<table>
#*row values*#
#for (int i = 0; i < Model.Count; i++)
{
<tr>
<td>#Html.DisplayFor(m => m[i].Brand)</td>
<td>#Html.DisplayFor(m => m[i].ProductName)</td>
<td>#Html.DisplayFor(m => m[i].UnitType)</td>
<td>#Html.DisplayFor(m => m[i].SalePrice)</td>
<td>#Html.DisplayFor(m => m[i].CurrentInventory)</td>
<td>
#Html.EditorFor(m => m[i].OrderQuantity)
#Html.ValidationMessageFor(m => m[i].OrderQuantity)
</td>
<td></td>
</tr>
}
</table>
<p>
<input type="submit" value="Save" />
#*<input type="submit" value="Cancel" />*#
</p>
}
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
Here the view show it has values for the List that was passed from controller to view..
Visually the view displays the data correctly (apparently i can't post an image of it until i have 10 reputation to post images)
when i hit submit and return to the controller:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult SelectProducts(List<Product> selectedProducts)
{
if (ModelState.IsValid)
{
}
}
The variable selectedProducts Is NOT NULL. The list has 3 Product items in it, however, as you can see in the image below in the debugger, even though i have 3 product items, none of the values exist from when the List of Product was originally passed to the view...
for example (since i can't post images yet):
selectedProducts[0].ProductID=0
selectedProducts[0].ProductName=null
selectedProducts[1].ProductID=0
selectedProducts[1].ProductName=null
You need to use #Html.HiddenFor():
#Html.HiddenFor(m => m[i].ProductID)
#Html.HiddenFor(m => m[i].ProductName)
This will send the data back to the controller. This creates an <input type="hidden"> that will be part of the form POST.