I'm looking for a way to select from a lists of items that I have retrieve from a database. I send these items to a view and I want to select from the list and return this to the controller to populate a secondary table within the database. I can pass the items to the view and get them to display but I can not seem to pass these items back to the controller.
Controller Calls (Updated Again):
public ActionResult Create()
{
var myMeal = new CreateMeal();
List<ProductsToInclude> pti = new List<ProductsToInclude>();
myMeal.ProductsToInclude = pti;
IList<Product> prods = db.Products.ToList();
foreach(var prod in prods)
{
pti.Add(new ProductsToInclude { Product = prod });
}
return View(myMeal);
}
//
// POST: /Meal/Create
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(CreateMeal myMeal)
{
if (ModelState.IsValid)
{
/* Add code to handle my meal and create a meal for data base*/
db.Meals.Add(meal);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(pti);
}
The ProductsToInclude ViewModel
public class ProductsToInclude
{
public Product Product { get; set; }
public Boolean Include { get; set; }
}
New CreateMeal ViewModel:
public class CreateMeal
{
public String Name { get; set; }
public IList<ProductsToInclude> ProductsToInclude { get; set; }
}
The Create View:
#model MealPlanner.ViewModels.CreateMeal
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>Meal</legend>
<div>
#Html.LabelFor(m => m.Name)
#Html.TextBoxFor(m => m.Name)
</div>
<div>
#Html.EditorFor(m => m.ProductsToInclude)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
And the Editor Templete (UpDated) :
#model MealPlanner.ViewModels.ProductsToInclude
<tr>
<td>
#Html.CheckBoxFor(m => m.Include)
</td>
<td>
#Model.Product.Name
#Model.Product.Quantity
#Model.Product.unit.UnitName
#Html.HiddenFor(m => m.Product.Name)
#Html.HiddenFor(m => m.Product.Quantity)
#Html.HiddenFor(m => m.Product.unit.UnitName)
</td>
</tr>
Meal Model:
public class Meal
{
public int MealId { get; set; }
public String Name { get; set; }
//public virtual IList<Product> Products { get; set; }
public int UserId { get; set; }
}
UPDATE:
Switching over to EditorTemplete I can not get this to display. I get an error now with myMeal.ProductsToInclude.Add(new ProductsToInclude { Product = prod, Include = false}); in the Create Method. prod is populated has 8 products in it.
You have two basic problems here. (and a third, you're not passing any model to the view, but your view is expecting a Meal object) First, is that you are posting a different model than you are rendering from. So, when you post your form it's not going to recognize the Meal.Name object because the post action is expecting a list of ProductsToInclude.
If you don't intend to change the value of the Meal.Name, then I would suggest rendering it with a DisplayFor rather than an EditorFor.
Second, the way you are rendering your form fields will not create the correct naming structure to allow the default model binder to bind to the collections. You can use EditorTemplates, which is the method I prefer.
Instead of using the Partial, you would create an editor template called ProductsToInclude.cshtml, set the model type to be ProductsToInclude (without the List) and then render as if it were a single item. You will also want to move the table and header info out into the main view.
EditorTemplates handle this situation without having to do anything special. They automatically iterate over collections, and they render names correctly. Use them wherever you can.
<div>
<table>
<tr>
<th>
#Html.DisplayName("Include")
</th>
<th>
#Html.DisplayName("Available Products")
</th>
<th></th>
</tr>
#Html.EditorFor(m => m.ProductsToInclude)
</table>
</div>
Your ProductsToInclude.cshtml should live in a folder called EditorTemplates in either ~/Views/Shared or in your local view folder.
#model ProductsToInclude
<tr>
<td>
#Html.CheckBoxFor(m => m.Include)
</td>
<td>
#Html.DisplayFor(m => m.Product.Name)
#Html.DisplayFor(m => m.Product.Quantity)
#Html.DisplayFor(m => m.Product.unit.UnitName)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=Model.PrimaryKey }) |
#Html.ActionLink("Details", "Details", new { id=Model.PrimaryKey }) |
#Html.ActionLink("Delete", "Delete", new { id=Model.PrimaryKey })
</td>
</tr>
Related
I am doing some calculations on the server side and displaying a table in a view. I want to be able to edit each row of the table in a single view.
How can I bind the model to the view so that after editing in the view I get a list of the model objects in the POST controller action?
My model:
public class Item
{
public float floatValue1;
public string stringValue1;
public float floatValue2;
public double doubleValue1;
}
From this model, I created a table view that lists the values in a HTML table.
However, in the edit view I don't need to edit each field. For example, only floatValue1, stringValue1, floatValue2 need to be editable. The doubleValue1 should remain its current value and uneditable by the user.
I've tried the suggestions I found online:
My controller sends a list of Item objects to the edit view as an IList<Item>
The edit view has a html form with a for loop, each iteration creates a table row with Html.EditorFor
public ActionResult PricingEdit(int? i)
{
var result = calculations(); // returns IList<Item>
return View(result.ToList());
}
My edit view:
#model IList<Item>
#{
ViewBag.Title = "Edit sheet";
Layout = "~/Views/Shared/_Layout.cshtml";
}
#using (Html.BeginForm("EditItems", "Controller", FormMethod.Post))
{
<table class="table table-sm">
<tr>
<th>
floatValue1
</th>
<th>
stringValue1
</th>
<th>
floatValue2
</th>
</tr>
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
#for(int i= 0; i < Model.Count(); i++)
{
<tr>
<td>
#Html.EditorFor(x => x[i].floatValue1)
</td>
<td>
#Html.EditorFor(x => x[i].stringValue1)
</td>
<td>
#Html.EditorFor(x => x[i].floatValue2)
</td>
<td>
#Html.EditorFor(x => x[i].doubleValue1, new { #readonly = "readonly" })
</td>
</tr>
}
</table>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-secondary btn-lg mt-1" />
</div>
</div>
}
My HTTP POST controller action:
public ActionResult EditItems(IList<Item> table)
{
return View(new List<Item>());
}
I get a List<Item> of values in my action, but each item in the list has 0 or null values for the fields.
Your model should have getters and setters so the model binder can set the values. It should work when your model matches the following:
public float floatValue1 { get; set; }
public string stringValue1 { get; set; }
public float floatValue2 { get; set; }
public double doubleValue1 { get; set; }
In C# it's usual to start properties like these with an uppercase so i advice you to change that.
I need pass two models in the Edit page (because I want to build a MVC 4 project with ViewModel), but when I try insert the view model in the edit page, I have the follow error:
The model item passed into the dictionary is of type
'System.Data.Entity.DynamicProxies.People_E82A8FE6694DFF4D5ED1869045FE3E0A1855CC3CA65B873F5F1556DABC2DC9F4',
but this dictionary requires a model item of type
'MVC_ViewModel.Models.ViewModel'.
Edit page (view):
#model MVC_ViewModel.Models.ViewModel
#{
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>People</legend>
#Html.HiddenFor(model => model.People.PeopleID)
<div class="editor-label">
#Html.LabelFor(model => model.People.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.People.Name)
#Html.ValidationMessageFor(model => model.People.Name)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
Index page (view):
#model IEnumerable<MVC_ViewModel.People>
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<p>
#Html.ActionLink("Create New", "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.PeopleID }) |
#Html.ActionLink("Details", "Details", new { id=item.PeopleID }) |
#Html.ActionLink("Delete", "Delete", new { id=item.PeopleID })
</td>
</tr>
}
</table>
ViewModel class (Model):
namespace MVC_ViewModel.Models
{
public class ViewModel
{
public Car Car { get; set; }
public List<Car> Cars { get; set; }
public People People { get; set; }
public List<People> Peoples { get; set; }
public Detail Detail { get; set; }
public List<Detail> Details { get; set; }
}
}
CRUD (controller):
public ActionResult Index()
{
return View(db.People.ToList());
}
...
public ActionResult Edit(int id = 0)
{
People people = db.People.Find(id);
if (people == null)
{
return HttpNotFound();
}
return View(people);
}
//
// POST: /CRUDViewModel/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(People people)
{
if (ModelState.IsValid)
{
db.Entry(people).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(people);
}
I need understand the error, if anybody can help me. Thanks!
You've specified in your ASP.NET Edit page that your model type is ViewModel:
#model MVC_ViewModel.Models.ViewModel
You then pass it a People object. Nowhere in the code you have provided do you use ViewModel.
If you were to change this to:
#model MVC_ViewModel.People
and change the references from model.People.* to model.*, then the error should go away.
However, I'd suggest you should be returning a PeopleViewModel and mapping various properties to this. It may be useful to go and read some blogs, tutorials and examples of how best to build this.
I am creating a simple MVC3 application in which I am using editorfor template to display a simple form containing two fields and these properties are having model-level validation having "Required" attribute.
But when I click the submit button on form and check the ModelState in the controller action, it comes as Invalid but the error messages are not displaying in the form.
I am pasting the code below:
1) Models:
public class EmployeeList
{
public List<Employee> ListOfEmployees { get; set; }
}
public class Employee
{
[Required(ErrorMessage="{0} is required.")]
public int? Id { get; set; }
[Required(ErrorMessage="{0} is required.")]
public string Name { get; set; }
}
2) Controller action:
[HttpPost]
public ActionResult AddEmployee(EmployeeList ListOfEmployees1)
{
if (ModelState.IsValid)
{
service.AddEmployee(ListOfEmployees1);
return RedirectToAction("ListofEmployees");
}
return View();
}
3) Main View (AddEmployee.cshtml):
#using (Html.BeginForm("AddEmployee", "Home", FormMethod.Post, new { #id = "testForm" }))
{
#Html.EditorFor(x => x.ListOfEmployees)
<p>
<input type="submit" value="Add" />
</p>
}
EditorFor template View (Employee.cshtml):
#model test.Models.Employee
<table border="0">
<tr>
<td>#Html.LabelFor(model => model.Id)</td>
<td>#Html.TextBoxFor(model => model.Id)
#Html.ValidationMessageFor(model => model.Id)
</td>
</tr>
<tr>
<td>#Html.LabelFor(model => model.Name)</td>
<td>#Html.TextBoxFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</td>
</tr>
</table>
If I use a partial view instead of editorfor template to display those two fields, than the validation messages comes on the form but the same is not happening with editorfor template.
Can someone please help?
In your action method, return the view with the model
[HttpPost]
public ActionResult AddEmployee(EmployeeList ListOfEmployees1)
{
if (ModelState.IsValid)
{
....
}
return View(ListOfEmployees1);
}
I ques it does not work because you use EditorFor for a model of collection type. Instead try something like:
#for(var i=0; i< Model.ListOfEmployees.Count; i++){
Html.EditorFor(m => m.ListOfEmployees[i])
}
We have a list of action links
Partial View
#foreach (var item in Model.Regions) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.RegionName)
</td>
<td>
<input type="submit" value="Select" />
</td>
#Html.HiddenFor(modelItem => Model.Id)
</tr>
}
</table>
I assume that this isn't the correct way to do this, but if you could point me in the right direction it would be appreciated. I want to submit this data into an existing form
Region View
#using (Html.BeginForm()){
<fieldset>
#Html.Partial("_RegionsPartial");
<legend>Create new region</legend>
<ol>
<li>#Html.LabelFor(m => m.RegionName)</li>
<li>#Html.EditorFor(m => m.RegionName)</li>
</ol>
<input type="submit" value="Next" />
#Html.HiddenFor(model => model.RegionId)
</fieldset>
}
So you can either submit a new one or submit an existing one. Im not sure how to get the id of an existing one into my model. Here is the controller:
public ActionResult Region()
{
var model = new WizardModel();
var getRegions = _facade.FetchRegion();
model.Regions = getRegions;
return View(model);
}
[HttpPost]
public ActionResult Region(WizardModel model)
{
if (model.RegionName != null)
{
var newRegion = _facade.CreateRegion(model.RegionName);
model.RegionId = newRegion.Id;
}
else
{
model.RegionName = _facade.FetchRegion(model.RegionId).RegionName;
}
TempData["suburbModel"] = model;
return RedirectToAction("Suburb");
}
Thanks for taking the time
So heres my example of passing an instance of a model. I've got a view with many courses so I need to click a button and fire an action, thus carrying all data (including relevant ID) of the course clicked. So in the end I carry the instance I need with the hidden fields.:)
My course model...
public class CourseModel
{
public int RecordId { get; set; }
public string StudentNameField { get; set; }
public string SubjectField { get; set; }
public string CatalogField { get; set; }
public string SectionField { get; set; }
public string InstrNameField { get; set; }
public string MtgStartField { get; set; }
public string MtgEndField { get; set; }
}
My main View...Called "CourseList" in Views folder
<div id="container">
<div class="selectLabel">Select a Course:</div><br />
#foreach (var item in Model)
{
#Html.DisplayFor(model=>item)
}
</div>
My Display template - Its a view called "CourseModel" in Shared\DisplayTemplates ...For your display template, you could make a unique model for existing & new. Using your "existing" model in the displaytemplate, it results in multiple forms, each using a button type=submit to submit the form with model instance. Use CSS to model the button like a link. If you still need to use actionlink, carry the iD as one of the params.
#using LecExamRes.Helpers
#model LecExamRes.Models.SelectionModel.CourseModel
#using (Html.BeginForm("CourseList", "Home", null, FormMethod.Post))
{
<div class="mlink">
#Html.AntiForgeryToken()
#Html.EncryptedHiddenFor(model => model.RecordId)
#Html.EncryptedHiddenFor(model => model.CatalogField)
#Html.EncryptedHiddenFor(model => model.SectionField)
#Html.EncryptedHiddenFor(model => model.SubjectField)
#Html.EncryptedHiddenFor(model => model.InstrNameField)
#Html.EncryptedHiddenFor(model => model.MtgStartField)
#Html.EncryptedHiddenFor(model => model.MtgEndField)
<p>
<input type="submit" name="gbtn" class="groovybutton" value="#Model.SubjectField - #Model.CatalogField - #Model.SectionField : #Model.InstrNameField">
</p>
</div>
}
My controller, Courselist [POST] Action...
[ValidateAntiForgeryToken]
[HttpPost]
public ActionResult CourseList(SelectionModel.CourseModel model)
{
//....do something with my model
}
I have a model women which is part of my db context and a view Model womenEditmodel which conatins a list of women items. I am using a partialview to loop through this list and display an editable Grid or List in my view. These are my models:
public class Women
{
public string ID { get; set; }
public string FirstName {get; set;}
public string LastName {get; set;}
}
public class WomenEditModel
{
public List<Women> WomenList { get; set; }
}
My view has this loop for injecting into my view rows for the women records
#foreach (Women women in Model.Womens)
{
Html.RenderPartial("WomenEditor", women);
}
which i display using a table. So now users can edit the list and post or save changes.
my partialview looks like:
#model XXX.Models.Women
#using (Html.BeginCollectionItem("Women")) {
<td>
#Html.HiddenFor(model => model.ID)
</td>
<td>
#Html.TextBoxFor(model => model.FirstName)
#Html.ValidationMessageFor(model => model.FirstName)
</td>
<td>
#Html.TextBoxFor(model => model.LastName)
#Html.ValidationMessageFor(model => model.LastName)
</td>
}
My http post action method looks like this
[HttpPost]
public ActionResult PostWomen(WomenEditModel model)
{
/*I need to iterate through the returned list and save all
changes to the db.*/
return RedirectToAction("Index");
}
How do i loop through the model WomenEditModel recieived at the post action method and save changes to the women list back to db?
Thanks in advance!!
I just got back to my machine. Here is how you can acheive it, if you haven't already.
My Action which renders the list
public ActionResult Index()
{
List<Women> womens = new List<Women>
{
new Women
{
Id=1,
FirstName = "Women1",
LastName = "Lastname1"
},
new Women
{
Id=2,
FirstName = "Women2",
LastName = "Lastname2"
}
};
WomenList womenList=new WomenList();
womenList.Womens = womens;
return View(womenList);
}
The action where the list is posted.
public ActionResult SaveWomens(List<Women> womenList)
{
System.Diagnostics.Debugger.Break();
//Your save logic goes here
return View("");
}
Partial View (Dont know whether it is required)
#model List<MvcApplication1.Models.Women>
<table>
#for (var i = 0; i < Model.Count; i++)
{
<tr>
<td>
#Html.TextBoxFor(m => m[i].Id)
</td>
<td>#Html.TextBoxFor(m => m[i].FirstName)
</td>
<td>#Html.TextBoxFor(m => m[i].LastName)
</td>
</tr>
}
</table>
And here is the view
#model MvcApplication1.Models.WomenList
#{
ViewBag.Title = "Home Page";
}
#section featured {
}
#using (Html.BeginForm("SaveWomens", "Home", FormMethod.Post))
{
#Html.Partial("_Women", Model.Womens)
<input type="submit" value="save" />
}
You can achieve it as follows
foreach (var item in womenList)
{
var obj = new Womens();
//Assign values to obj for eg: obj.prop = item.prop
dataContext.Womens.AddObject(obj);
}
dataContext.SaveChanges();