I want to post quantity property to Controller (It's an edit action). I'm editing OrderedProductSet which is connected with ProductSet in my SQL Database (I get the name and price from there). How to pass multiple data from the view to controller? How to write method in controller class to receive the data (I'm asking about method arguments in this specific case).
My view:
#model Shop.Models.ProductViewModel#{
ViewBag.Title = "Edycja zamówienia";
}
<h2>Edycja zamówienie</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<table class="table">
<tr>
<th>
<b>Nazwa produktu</b>
</th>
<th>
<b>Cena</b>
</th>
<th>
<b>Ilość</b>
</th>
<th></th>
</tr>
#foreach (var item in Model.orderedProductSet)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.ProduktSet.name)
</td>
<td>
#Html.DisplayFor(modelItem => item.ProduktSet.price)
</td>
<td>
#Html.EditorFor(model => item.quantity, new { htmlAttributes = new { #class = "form-control" } })
</td>
</tr>
}
</table>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Potwierdź zmiany" class="btn btn-default" />
</div>
</div>
}
<div>
#Html.ActionLink("Powrót", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
My model (in separated classes of course):
public class ProductViewModel
{
public OrderSet orderSet { get; set; }
public IEnumerable<OrderedProductSet> orderedProduktSet { get; set; }
}
public partial class OrderedProduktSet
{
public int orderNumber{ get; set; }
public int productNumber { get; set; }
public int ilosc { get; set; }
public virtual ProduktSet ProduktSet { get; set; }
public virtual OrderSet OrderSet { get; set; }
}
You need to construct controls for you collection in a for loop or use a custum EditorTemplate for OrderedProduktSet so that the controls are correctly named with indexers and can be bound on post back. Note the for loop approach required that the collection be IList.
#model Shop.Models.ProductViewModel
#using(Html.BeginForm())
{
....
for(int i = 0; i < Model.orderedProductSet.Count; i++)
{
#Html.DisplayFor(m => m.orderedProductSet[i].ProduktSet.name)
....
#Html.EditorFor(m => m.orderedProductSet[i].quantity, new { htmlAttributes = new { #class = "form-control" } })
}
<input type="submit" />
}
Controller (the model will be bound, including the collection of OrderedProductSet)
public ActionResult Edit(ProductViewModel model)
{
....
}
Alternatively, you can create an EditorTemplate
/Views/Shared/EditorTemplates/OrderedProduktSet.cshtml
#model OrderedProduktSet
#Html.DisplayFor(m => m.ProduktSet.name)
#Html.EditorFor(m => m.quantity, new { htmlAttributes = new { #class = "form-control" } })
and in the main view
#model Shop.Models.ProductViewModel
#using(Html.BeginForm())
{
....
#Html.EditorFor(m => m.orderedProductSet)
<input type="submit" />
}
Viewbag is your friend here. You normally pass data from View to Controller in MVC. You can access data set in a Viewbag in the controller in your View.
The simplest way to let your controller handle your view is to create an actionresult method in your controller with the same name as your view.
For example, your view is called Index, thus you would have the following method in your controller to handle the view data:
public ActionResult Index()
{
return View();
}
Accessing a list:
Use a Viewbag.
Controller
Viewbag.MyList = myList
View
#foreach (var item in Viewbag.MyList)
Here is good link for more info:
http://www.asp.net/mvc/overview/older-versions/getting-started-with-aspnet-mvc4/adding-a-view
Related
This question already has answers here:
Post an HTML Table to ADO.NET DataTable
(2 answers)
Closed 5 years ago.
I am new to ASP .NET MVC. My problem is - I want to 'POST' a collection of the items , so that controller can process it.
My model is collection of -
public class CheckedRentalProperty
{
public bool IsSelected { get; set; }
public int Id { get; set; }
public String Address { get; set; }
}
My controller is defined like this -
public class RentalPropertiesController : Controller
{
public ActionResult Index()
{
List<CheckedRentalProperty> checkHsList = new List<CheckedRentalProperty>();
// Fill the list
return View(checkHsList);
}
[HttpPost]
public ActionResult Save(IEnumerable<CheckedRentalProperty> checkHsList)
{
// why checkHsList is coming as null ??
}
}
And the view is like this -
#model IEnumerable<XXX.Models.CheckedRentalProperty>
#using (Html.BeginForm("Save", "RentalProperties", FormMethod.Post))
{
<div class="form-horizontal">
<div class="form-group">
<table class="table">
<tr>
<th>
</th>
<th>
#Html.DisplayNameFor(model => model.Address)
</th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>#Html.CheckBoxFor(modelItem => item.IsSelected)</td>
<td>
#Html.DisplayFor(modelItem => item.Address)
</td>
</tr>
}
</table>
</div>
<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>
}
My expectations was - when I hit the "Save" button, the Model, which is IEnumerable<CheckedRentalProperty> item, will be passed to the Save() action of the controller. However, I find that the passed parameter is "null" all the time. What am I missing?
Model that are solely IEnumerable are not too friendly as MVC Model.
There are many issues arise here, but in a nutshell, MVC webform bindings needs form name requests to be send in the following format: PropertyName[Index].Property
Which is not the case at your example.
It is a good design practice, to create a wrapping ViewModel which will hold the properties you need for the given controller + pages.
ViewModel
public class RentalPropertiesViewModel
{
public List<CheckedRentalProperty> CheckedRentalProperties { get; set; }
}
Controller: Next we will want to use this ViewModel in our controller.
public ActionResult Index()
{
var checkHsList = new List<CheckedRentalProperty>();
checkHsList.Add(new CheckedRentalProperty { Id = 1, Address = "Address1", IsSelected = true });
checkHsList.Add(new CheckedRentalProperty { Id = 2, Address = "Address2", IsSelected = false });
checkHsList.Add(new CheckedRentalProperty { Id = 3, Address = "Address3", IsSelected = true });
var model = new RentalPropertiesViewModel
{
CheckedRentalProperties = checkHsList
};
return View(model);
}
[HttpPost]
public ActionResult Save(RentalPropertiesViewModel model)
{
// why checkHsList is coming as null ??
return null;
}
View: Now in our view we should set the Model as the new ViewModel type we created.
#model TestBindings.Models.RentalPropertiesViewModel
And our view form should be something like:
<table class="table">
<tr>
<th>
Is Selected
</th>
<th>
Address
</th>
</tr>
#for (int i = 0; i < Model.CheckedRentalProperties.Count(); i++)
{
<tr>
#Html.HiddenFor(model => model.CheckedRentalProperties[i].Id);
<td>#Html.CheckBoxFor(model => model.CheckedRentalProperties[i].IsSelected)</td>
<td>#Html.TextBoxFor(model => model.CheckedRentalProperties[i].Address)</td>
</tr>
}
</table>
I've use the following format model => model.CheckedRentalProperties[i].IsSelected and now MVC InputExtensions will bind it correctly. e.g: CheckedRentalProperties[0].IsSelected
Important Note: Notice i'm passing Id property as hidden, so MVC Binder will know to set the Id to the correct item.
I'm quite new to MVC and still making myself familiar to how MVC works. So basically, I have a User model that has a Create view. I'm using Razor syntax to get the variables from User model:
Create.cshtml
#model CDS.Models.UserModels
#{
ViewBag.Title = "Create User";
}
#using (Html.BeginForm())
{
#Html.LabelFor(m => m.firstname)
#Html.TextBoxFor(m => m.firstname)
<input type="submit" id="btnSave" value="Save" class="btn btn-default" />
}
UserModels.cs
namespace CDS.Models
{
public class UserModels
{
public string userid { get; set; }
[Display(Name = "First Name")]
public string firstname{ get; set; }
public IEnumerable<SelectListItem> filteroptions { get; set; }
}
}
I tried auto-generating an Index view from the controller's Index method to list the database records, but found out that the generated Index view is using the Database model (first line of code). I just want to move the code from the Index.cshtml to my Create.cshtml to have the latter View also display the database records. So how will I do that? I've heard that I need to use Javascript for that?
UserController.cs
namespace CDS.Controllers
{
public class UserController : Controller
{
CDSEntities _odb = new CDSEntities(); //My Database
// GET: User
public ActionResult Index()
{
return View(_odb.USR_MSTR.ToList());
}
// GET: User/Create
public ActionResult Create()
{
var filters = GetAllFilters();
var model = new UserModels();
model.filteroptions = GetSelectListItems(filters);
return View(model);
}
}
}
Index.cshtml
#model IEnumerable<CDS.USR_MSTR>
#{
ViewBag.Title = "Index";
}
<p>#Html.ActionLink("Create New", "Create")</p>
<table class="table">
<tr>
<th>#Html.DisplayNameFor(model => model.FIRST_NM)</th>
<th>#Html.DisplayNameFor(model => model.LAST_NM)</th>
</tr>
#foreach (var item in Model) {
<tr>
<td>#Html.DisplayFor(modelItem => item.FIRST_NM)</td>
<td>#Html.DisplayFor(modelItem => item.LAST_NM)</td>
<td>
#Html.ActionLink("Edit", "Edit", new { /* id=item.PrimaryKey */ }) |
#Html.ActionLink("Details", "Details", new { /* id=item.PrimaryKey */ }) |
#Html.ActionLink("Delete", "Delete", new { /* id=item.PrimaryKey */ })
</td>
</tr>
}
</table>
Please note that I removed the codes that I think unnecessary to post here. These are all just summaries of my classes and HTMLs
There are a lot of topics related to this question but I still did't figured out what I'm doing wrong.
I have a database where I manage access of different users to folders. On my View the User can select Employees which should have access to certain folder. Then I want to pass the selected Employees to Controller, where the database will be updated.
My Problem is: The right Action in the Controller class didn't get invoked.(I have a breakpoint inside)
Here is the View
#model DataAccessManager.Models.EmployeeSelectionViewModel
#{
ViewBag.Title = "GiveAccessTo";
}
#using (Html.BeginForm("SubmitSelected", "FolderAccessController", FormMethod.Post, new { encType = "multipart/form-data"}))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
#Html.HiddenFor(model => model.fr_folder_uid_fk)
<div class="form-horizontal">
<input type="submit" value="Save" id="submit" class="btn btn-default" />
<table id="tableP">
<thead>
<tr>
<th>Selection</th>
<th>Second Name</th>
<th>First Name</th>
<th>Department</th>
</tr>
</thead>
<tbody id="people">
#Html.EditorFor(model => model.People)
</tbody>
</table>
</div>
</div>
</div>
}
Here is the Controller reduced to the minimum
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult SubmitSelected(EmployeeSelectionViewModel model)
{
return View();
}
More Details: I am not sure what is causing the problem, so here some more details.
The view is strongly typed to EmployeeSelectionViewModel, it represets the table with all Employees as a List here is the the code:
public class EmployeeSelectionViewModel
{
public List<SelectEmployeeEditorViewModel> People { get; set; }
public EmployeeSelectionViewModel()
{
this.People = new List<SelectEmployeeEditorViewModel>();
}
public Int64 fr_folder_uid_fk { get; set; }
public IEnumerable<string> getSelectedIds()
{
// Return an Enumerable containing the Id's of the selected people:
return (from p in this.People where p.Selected select p.fr_mavnr_fk).ToList();
}
}
The SelectEmployeeEditorViewModel represents one row of the table with all Employees.
public class SelectEmployeeEditorViewModel
{
public bool Selected { get; set; }
public string fr_mavnr_fk { get; set; }
public string firstName { get; set; }
public string secondName { get; set; }
public string dpt { get; set; }
}
And it has a View which create the checkboxes for each Employee
#model DataAccessManager.Models.SelectEmployeeEditorViewModel
<tr>
<td style="text-align:center">
#Html.CheckBoxFor(model => model.Selected)
#Html.HiddenFor(model => model.fr_mavnr_fk)
</td>
<td>
#Html.DisplayFor(model => model.secondName)
</td>
<td>
#Html.DisplayFor(model => model.firstName)
</td>
<td>
#Html.DisplayFor(model => model.dpt)
</td>
</tr>
The /FolderAccessController/SubmitSelected URL is called in the browser when I press the Submit button, but as mentioned the Action isn't invoked.
EDIT: I get the HTTP 404 not found error after pressing the button
Try removing the "Controller" word from your Html.BeginForm() second parameter, it's not needed.
#using (Html.BeginForm("SubmitSelected", "FolderAccess", FormMethod.Post, new { encType = "multipart/form-data"}))
Thiago Ferreira and haim770 Thanks a lot! The solution is to use the combination of your comments. So:
#using (Html.BeginForm("SubmitSelected", "FolderAccess", FormMethod.Post))
at the Controller
I have seen similar questions to this and followed the routine answer which is to ensure all model data is rendered in the HTML.
I have done that and the model is rendered in the view with #Html.HiddenFor() but when the posting back to the controller there are no items in the list ?
The view will happily render multiple items in the list, but List<Item> Items in the posted data is always an empty list (not null)
Model
public class ItemCollection
{
public List<string> AvailiableActions { get; set; }
public List<Item> Items { get; set; }
}
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
public string SelectedAction { get; set; }
}
View
#model ItemCollection
#using (Html.BeginForm("myAction", "myController", FormMethod.Post))
{
<fieldset>
<div>
#Html.HiddenFor(m => Model.Items)
#Html.DisplayNameFor(x => x.AvailiableActions)
<table>
#{
foreach (var item in Model.Items)
{
#Html.HiddenFor(m => item)
#Html.HiddenFor(s => item.Id)
<tr>
<td>#item.Name</td>
<td>#Html.DropDownList(item.SelectedAction, new SelectList(Model.AvailiableActions))</td>
</tr>
}
}
</table>
</div>
</fieldset>
}
Controller
[HttpPost]
private ActionResult myAction(ItemCollection model)
{
if (model.Items.Count() == 0)
{
// this is true.. something is wrong......
}
}
You cannot use a foreach loop to render controls for a collection. It renders duplicate id and name attributes without the necessary indexers to bind to a collection. Use a for loop
for (int i = 0; i < Model.Items.Count; i++)
{
<tr>
<td>
#Html.HiddenFor(m => m.Items[i].Id)
#Html.DisplayFor(m => m.Items[i].Name)
</td>
<td>#Html.DropDownList(m => m.Items[i].SelectedAction, new SelectList(Model.AvailiableActions))</td>
</tr>
}
Note your view also included #Html.HiddenFor(m => Model.Items) and #Html.HiddenFor(m => item) which would also have failed because item is a complex object and you can only bind to value types. You need to remove both.
Instead of iterating over all items to make sure the index is added to the generated output, you may consider using EditorTemplates (an example on an other site).
EditorTemplates allow you to specify a template for a single Item in \Views\Shared\EditorTemplates\Item.cshtml:
#model Item
#{
var options= (List<string>)ViewData["Options"];
}
<tr>
<td>
#Html.HiddenFor(m => m.Id)
#Html.DisplayFor(m => m.Name)
</td>
<td>#Html.DropDownList(m => m.SelectedAction, new SelectList(options))</td>
</tr>
Then you may simply change your view to:
#model ItemCollection
#using (Html.BeginForm("myAction", "myController", FormMethod.Post))
{
<fieldset>
<div>
<table>
#Html.EditorFor(m => m.Items, new {Options = Model.AvailiableActions })
</table>
</div>
</fieldset>
}
VS'12 KendoUI InternetApplication Template C# asp.net EF Code First
My Question is how to pass both the Regular ( are passing now ) values and the Ienumerable(passing null) into my controller and saving them to the Database using EF Code First in a Many-2-Many Relationship manor.
The Following is what i have tried
Main View
#model OG.Models.UserProfiles
#using (Html.BeginForm())
{
<div class="editor-field">
<div class="Containter">
<div>
#Html.DisplayFor(model => model.UserName)
</div>
<div class="contentContainer">
#foreach (var item in Model.Prospects)
{
<table>
<tr>
<td>
#Html.Label("Current Prospects")
</td>
</tr>
<tr>
<td>
#Html.DisplayNameFor(x=>item.ProspectName)
</td>
</tr>
</table>
}
</div>
</div>
<div class="contentContainer2">
#Html.Partial("_UsersInProspectsDDL", new OG.ModelView.ViewModelUserInProspects() { Users = Model.UserName })
</div>
</div>
}
Partial View
#model OG.ModelView.ViewModelUserInProspects
<label for="prospects">Prospect:</label>
#(Html.Kendo().DropDownListFor(m=>m.Prospects)
.Name("Prospects")
.HtmlAttributes(new { style = "width:300px"}) //, id = "countys"})
.OptionLabel("Select Prospect...")
.DataTextField("ProspectName")
.DataValueField("ProspectID")
.DataSource(source => {
source.Read(read =>
{
read.Action("GetCascadeProspects", "ChangeUsersInfo")
.Data("filterProspects");
})
.ServerFiltering(true);
})
.Enable(false)
.AutoBind(false)
.CascadeFrom("Clients")
</div>
Model for PartialView
public class ViewModelUserInProspects
{
public string Clients { get; set; }
public IEnumerable<dbClient> AvailableClients { get; set; }
public string Prospects { get; set; }
public IEnumerable<dbProspect> AvailableProspects { get; set; }
public string Users { get; set; }
public IEnumerable<UserProfiles> AvailableUsers {get;set;}
}
}
Main Model
Standart SimpleMemberShipUserTable
Post Method
[HttpPost]
public ActionResult UsersInProspect(
[Bind(Include= "ProspectName, ProspectID")]
UserProfiles userprofiles, ViewModelUserInProspects values, FormCollection form)
//<- Trying different things sofar
{
if (ModelState.IsValid)
{
//string something = form["Prospects"];
int prosID = Convert.ToInt16(values.Prospects);
int UserID = userprofiles.UserID; // <- THIS VALUE is null atm.
This is where i need to save both ID's to the EF Generated / Mapped Table. Unsure how.
db.Entry(userprofiles).CurrentValues.SetValues(userprofiles);
db.Entry(userprofiles).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(userprofiles);
}
Please take a look Here
Goes over ViewModels
What EditorTemplate are and how to use them
What the GET Method would look like
What the Edit View would look like
Give you a View Example
What the Post Method would look like