How to do a bulk persist on a partial view? - asp.net-mvc-3

I have written a partial view that displays a list of rows, where some fields in the row are editable using a textbox, checkbox, or whatever.
I would like the parent view to have a "submit" button that posts the whole collection of updated rows so that I can do a group update rather than posting once for every updated row.
Here's what I have:
public class GroupModel {
public string SomeGenericProperty { get; set; }
public IEnumerable<ItemModel> Items { get; set; }
}
public class ItemModel {
public long ID { get; set; }
public string Field1 { get; set; }
public string Field2 { get; set; }
}
Then I have a view "GroupDetails":
#model MyNamespace.GroupModel
...
#using (Html.BeginForm("SaveItems", "Home")) {
<fieldset>
...
#Html.Partial("ItemList", Model.Items)
...
<input type="submit" value="Approve" />
</fieldset>
}
And a partial view "ItemList":
#model IEnumerable<MyNamespace.ItemModel>
<table>
<tr>
<th>
Field 1
</th>
<th>
Field 2
</th>
<th>
</th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.TextBoxFor(modelItem => item.Field1)
</td>
<td>
#Html.TextBoxFor(modelItem => item.Field2)
</td>
<td>
#Html.HiddenFor(i => item.ID)
</td>
</tr>
}
</table>
But when I post, the information from partial view is not being posted; the GroupModel object's Items property is null.
What's the right way to do this?

I would recommend you to always use editor templates. The reason your code doesn't work is because you used a partial and this partial doesn't inherit the parent template context which means that helpers that are used inside it will generate wrong names for the input fields.
For example if you look at the source code of your page you will see this:
<td>
<input type="text" name="[0].Field1" id="[0]_Field1" />
</td>
instead of the correct name which is:
<td>
<input type="text" name="Items[0].Field1" id="Items[0]_Field1" />
</td>
I would recommend you reading the following blog post to better understand the wire format that the default model binder expects.
So start by fixing your main view first and replace the partial with an editor template:
#model MyNamespace.GroupModel
#using (Html.BeginForm("SaveItems", "Home"))
{
<fieldset>
...
<table>
<thead>
<tr>
<th> Field 1 </th>
<th> Field 2 </th>
<th></th>
</tr>
</thead>
<tbody>
#Html.EditorFor(x => x.Items)
</tbody>
</table>
...
<input type="submit" value="Approve" />
</fieldset>
}
and then define a custom editor template that will be rendered for each element of the model (~/Views/Shared/EditorTemplates/ItemModel.cshtml - this works by convention, it should be either inside ~/Views/Shared/EditorTemplates if you want it to be reused among controllers or inside ~/Views/Home/EditorTemplates if you want this template to be available only for the Home controller - and the name of the template must be the type of the collection - in your case it is ItemModel.cshtml):
#model MyNamespace.ItemModel
<tr>
<td>
#Html.TextBoxFor(x => x.Field1)
</td>
<td>
#Html.TextBoxFor(x => x.Field2)
</td>
<td>
#Html.HiddenFor(x => x.ID)
</td>
</tr>
Now everything will bind fine and dandy in your controller action:
[HttpPost]
public ActionResult SaveItems(GroupModel model)
{
...
}

Related

ASP.NET Core 3.1 MVC : sending selected checkboxes to controller

I have following list view with checkboxes:
#model IEnumerable<PaketServisAracTakip.Models.Item>
#{
ViewData["Title"] = "Yükleme Yap";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2 class="text-center text-success">#ViewBag.name İsimli Araca Yükleme Yap</h2>
<form asp-controller="Vehicle"
asp-action="LoadItem" method="post">
<br />
<input type="submit" name="submit" value="Oluştur" class="btn btn-primary" />
</form>
<table class="table table-bordered table-striped table-hover">
<thead>
<tr>
<th class="text-center">
Yüklensin mi?
</th>
<th class="text-center">
#Html.DisplayNameFor(model => model.Name)
</th>
<th class="text-center">
#Html.DisplayNameFor(model => model.Price)
</th>
<th class="text-center">
#Html.DisplayNameFor(model => model.Description)
</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model)
{
<tr>
<td>
<div class="form-check">
<label class="form-check-label" for="#item.Id" asp-validation-for="#item.Id"></label>
<input class="form-check-input" name="ids" type="checkbox" value="#item.Id" id="#item.Id">
</div>
</td>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
₺#Html.DisplayFor(modelItem => item.Price)
</td>
<td>
#Html.DisplayFor(modelItem => item.Description)
</td>
</tr>
}
</tbody>
</table>
And my item model with database integrated:
[Table("Items")]
public class Item
{
[Key]
public int Id { get; set; }
[Display(Name = "İsim")]
[Required(ErrorMessage ="{0} alanı boş bırakılamaz.")]
[MaxLength(50, ErrorMessage ="İsim 50 karakteri geçemez.")]
public String Name { get; set; }
[Display(Name = "Fiyat")]
[Required(ErrorMessage = "{0} alanı boş bırakılamaz.")]
[Range(0, Double.MaxValue, ErrorMessage = "Minimum 0 girmelisin.")]
public int Price { get; set; }
[Display(Name = "Açıklama")]
public String Description { get; set; }
}
view
So when button is clicked i want to get checked items in my controller. I tried this but its empty:
[HttpPost]
public ActionResult LoadItem(IEnumerable<Item> model)
{
return RedirectToAction("Index");
}
I also tried int array and FormCollection but didn't work. I think I need some tag helpers but don't know which.
when button is clicked i want to get checked items in my controller. I
tried this but its empty
Please check the code in the View Page, since the table doesn't in the <form> element, when you click the Submit button, the submitted form doesn't contain the related data.
Besides, to submit the model data to the controller using model binding, we should use the #for statement to loop through the entities and use hidden fields to store the related data. Please refer the following sample and change your code:
Model:
[Table("Items")]
public class Item
{
[Key]
public int Id { get; set; }
[Display(Name = "İsim")]
[Required(ErrorMessage = "{0} alanı boş bırakılamaz.")]
[MaxLength(50, ErrorMessage = "İsim 50 karakteri geçemez.")]
public String Name { get; set; }
[Display(Name = "Fiyat")]
[Required(ErrorMessage = "{0} alanı boş bırakılamaz.")]
[Range(0, Double.MaxValue, ErrorMessage = "Minimum 0 girmelisin.")]
public int Price { get; set; }
[Display(Name = "Açıklama")]
public String Description { get; set; }
public Boolean IsChecked { get; set; } //add a property to store whether the item is check or not.
}
View page:
#model List<netcore3_1sample.Models.Item>
#{
ViewData["Title"] = "ItemIndex";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2 class="text-center text-success">#ViewBag.name İsimli Araca Yükleme Yap</h2>
<form asp-controller="Home"
asp-action="LoadItem" method="post">
<br />
<input type="submit" name="submit" value="Oluştur" class="btn btn-primary" />
<br />
<table class="table table-bordered table-striped table-hover">
<thead>
<tr>
<th class="text-center">
Yüklensin mi?
</th>
<th class="text-center">
Name
</th>
<th class="text-center">
Price
</th>
<th class="text-center">
Description
</th>
</tr>
</thead>
<tbody>
#for( var i = 0; i < Model.Count;i++)
{
<tr>
<td>
<div class="form-check">
<label class="form-check-label" for="#Model[i].Id" asp-validation-for="#Model[i].Id"></label>
<input class="form-check-input" name="ids" type="checkbox" value="#Model[i].Id" id="#Model[i].Id">
#*<input type="checkbox" asp-for="#Model[i].IsChecked" />*#
<input type="hidden" asp-for="#Model[i].Id" />
</div>
</td>
<td>
#Html.DisplayFor(modelItem => Model[i].Name)
<input type="hidden" asp-for="#Model[i].Name" />
</td>
<td>
₺#Html.DisplayFor(modelItem => Model[i].Price)
<input type="hidden" asp-for="#Model[i].Price" />
</td>
<td>
#Html.DisplayFor(modelItem => Model[i].Description)
<input type="hidden" asp-for="#Model[i].Description" />
</td>
</tr>
}
</tbody>
</table>
</form>
Code in the controller:
[HttpPost]
public ActionResult LoadItem(List<Item> model, int[] ids)
{
return RedirectToAction("ItemIndex");
}
According to your code, you are using a html checkbox element to store the selected Item ID, so here we need to add an array to get the selected ids.
Besides, you could add a IsChecked property in the Item model, then change the following code:
<input class="form-check-input" name="ids" type="checkbox" value="#Model[i].Id" id="#Model[i].Id">
to
<input type="checkbox" asp-for="#Model[i].IsChecked" />
By using this method, in the controller, you could filter the selected item based on the IsChecked property.
The result like this:

Value is not getting pass from view to action method

i am new in mvc. i have design a form and when i am click the submit button then right action method is calling but form field's value are not getting pass.
here is my view code
<div id="mydiv">
#using (Html.BeginForm("Save", "Game", FormMethod.Post, new { #Id = "Form1" }))
{
<table border="0">
<tr>
<td>Name :</td>
<td><input name="name" type="text" /></td>
</tr>
<tr>
<td>Salary :</td>
<td><input name="salary" type="text" /></td>
</tr>
<tr>
<td colspan="2"><input type="submit" value="Save" /> </td>
</tr>
</table>
}
</div>
here is my action method
public ActionResult Save(string str1, string str2)
{
return View("Message");
}
when save is getting called the str1 & str2 is being null please help me to pass value and also discuss various trick to pass value from view to action method. thanks
Change you controller
public ActionResult Save(string name, string salary)
{
return View("Message");
}
As you have to use variable name which you have defined in input
<input name="name" type="text" />
<input name="salary" type="text" />
If you want to return partial view.
return PartialView("Message", <<OptionalPartialViewModel>>);
You should start by learning about conventions in ASP.NET MVC. You should use models for communicating between controllers and views.
Create a model type first:
public class SalaryModel
{
public string Name { get; set; }
public string Salary { get; set; }
}
Create your form by using HTML helpers and strongly typing your view:
#model SalaryModel
<div id="mydiv">
#using (Html.BeginForm("Save", "Game", FormMethod.Post, new { #Id = "Form1" }))
{
<table border="0">
<tr>
<td>Name :</td>
<td>#Html.TextBoxFor(item => item.Name)</td>
</tr>
<tr>
<td>Salary :</td>
<td><input name="salary" type="text" /></td>
</tr>
<tr>
<td colspan="2">#Html.TextBoxFor(item => item.Salary)</td>
</tr>
</table>
}
</div>
Then you can get the form values inside the model:
[HttpPost]
public ActionResult Save(SalaryModel model)
{
return View("Message");
}
There's a great tutorial on ASP.NET MVC website that can help you with the basics.
MVC Bind form inputs to Action by their names. You should change your method params as the same of the form. Also, you are missing the HttpPost attribute:
[HttpPost]
public ActionResult Save(string name, string salary)
{
/*Do Stuff here*/
return View("Message");
}

HTTP POST, GET in MVC?

In MVC3, i created one register form. I created one model,,controller and view. This is my code:
[HttpGet]
public ActionResult Insert()
{
Models.Employees objEmpl = new Models.Employees();
return View(objEmpl);
}
[HttpPost]
[AcceptVerbs("Post")]
public ActionResult Insert(Models.Employees objs)
{
var v = new Models.test().InsertDl(objs);
return View();
}
The above is my controller
#model MvcVideo.Models.Employees
#{
ViewBag.Title = "Insert";
Layout = "~/Views/Shared/VideoLayout.cshtml";
}
<h2>Insert</h2>
#using(Html.BeginForm("Insert","Home",FormMethod.Post ))
{
<table>
<tr>
<td>
Employee Name
</td>
<td>
#Html.TextBox("Ename",#Model.Enames)
</td>
</tr>
<tr>
<td>
Department Id
</td>
<td>
#Html.TextBox("Departmentid",#Model.DepartId )
</td>
</tr>
<tr>
<td>
Email Id
</td>
<td>
#Html.TextBox("Emailid",#Model.EmailIds)
</td>
</tr>
<tr>
<td>
Address
</td>
<td>
#Html.TextBox("Address",#Model.Adress)
</td>
</tr>
<tr>
<td colspan="2" style="text-align:center;" >
<button title ="Ok" value="OK"></button>
</td>
</tr>
</table>
}
But the objs object at public actionresult Insert(models.Empoyees objs) action method parameter is showing null values. Imean Ename=Null, Department=0, Emailid=Null and Address=null
It isn't working because the names you've provided in your Html helpers don't match up with the property names on your model, so the default model binder can't resolve them when the values get posted back.
Using Html.TextBoxFor instead of Html.TextBox will provide you with strong typing against your model, and is the safer approach.
Replace this
#Html.TextBox("Ename",#Model.Enames)
With
#Html.TextBoxFor(model => model.Enames)
This will fix your issue.

Updating multiple items within same view

I am trying to make a stock take application, My view loads all my stock with one editor.
My controller is not getting any of the data from the view?
I want to be able to edit all my stock at the same time?
How can I do this
Model Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace FlatSystem.Models
{
public class Stock
{
public int ID { get; set; }
public string Item_Name { get; set; }
public int Actual_Stock { get; set; }
public int Wanted_Stock { get; set; }
}
}
View Code
#model IEnumerable<FlatSystem.Models.Stock>
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<div class="sidemenu">
<div class="sidemenu-heading">
ReStock
</div>
<div class="div-body">
<table>
<tr>
<th>
Item Name
</th>
<th>
Wanted Stock
</th>
<th>
Stock On Hand
</th>
<th></th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Item_Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.Wanted_Stock)
</td>
<td>
<div class="editor-field">
#Html.EditorFor(modelItem => item.Actual_Stock)
#Html.ValidationMessageFor(modelItem => item.Actual_Stock)
</div>
</td>
#Html.HiddenFor(modelItem => item.ID)
</tr>
}
</table>
</div>
</div>
<input type="submit" value="Submit" />
}
Controller Code
[HttpPost]
public ActionResult ReStock(List<Stock> stock)
{
foreach (var item in stock)
{
if (ModelState.IsValid)
{
GR.InsertOrUpdate(item);
}
}
GR.Save();
return RedirectToAction("Restock");
}
It's hard to answer your question without model class, but idea is that your edit inputs must contain index in name attribute.
Something like this:
#for(int i = 0: i < Model.Count(); i++)
{
<tr>
<td>
#Html.DisplayFor(modelItem => Model[i].Item_Name)
</td>
<td>
#Html.DisplayFor(modelItem => Model[i].Wanted_Stock)
</td>
<td>
<div class="editor-field">
#Html.EditorFor(modelItem => Model[i].Actual_Stock)
#Html.ValidationMessageFor(modelItem => Model[i].Actual_Stock)
</div>
</td>
#Html.HiddenFor(modelItem => Model[i].ID)
</tr>
}
Added:
Sorry, thanks to Darin Dimitrov, you can't access IEnumerable by index, use List or Array.
You could use editor templates. I would recommend you to first read the following article in order to understand why your code doesn't correctly bind the collection. Once you understand that you could do the following:
#model IEnumerable<FlatSystem.Models.Stock>
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<div class="sidemenu">
<div class="sidemenu-heading">
ReStock
</div>
<div class="div-body">
<table>
<thead>
<tr>
<th>Item Name</th>
<th>Wanted Stock</th>
<th>Stock On Hand</th>
<th></th>
</tr>
</thead>
<tbody>
#Html.EditorForModel()
</tbody>
</div>
</div>
<input type="submit" value="Submit" />
}
and now define a custom editor template for the Stock type which will automatically be rendered for each element of the collection (~/Views/Shared/EditorTemplates/Stock.cshtml) - the name and location of the editor template is important as it works by convention:
#model FlatSystem.Models.Stock
<tr>
<td>
#Html.DisplayFor(x => x.Item_Name)
</td>
<td>
#Html.DisplayFor(x => x.Wanted_Stock)
</td>
<td>
<div class="editor-field">
#Html.EditorFor(x => x.Actual_Stock)
#Html.ValidationMessageFor(x => x.Actual_Stock)
</div>
</td>
#Html.HiddenFor(x => x.ID)
</tr>
Remark: You might also want to include the Wanted_Stock and Item_Name as hidden fields along with the ID in the editor template in order for their values to be sent to the server, because you don't have a corresponding input field for them.

Post mvc3 table to controller action

I have a simple table
#using (Html.BeginForm("Save", "Subscription", FormMethod.Post))
{
<table>
<tr>
<th>
Name
</th>
<th>
Report
</th>
</tr>
#foreach (var item in Model.ReportSubscriptions)
{
<tr>
<td>
#Html.HiddenFor(item.Id)
#Html.TextBoxFor(item.Name)
</td>
<td>
#Html.TextBoxFor(item.Report)
</td>
</tr>
}
</table>
<button type="submit">Save</button>
}
The save action
public ViewResult Save(IEnumerable<ReportSubscription> list)
{
throw new NotImplementedException();
}
}
How can I get MVC to deserialize the post back to my model?
For collections you need to do a Editor template and use
#Html.EditorFor(m => m.Collection);

Resources