When passing a view model (as below) to a view, how can I ensure that the checkboxes I'm creating (mapped to item "Product" in here) get passed back to the controller?
I've included my view model and "post" product controller below.
Unfortunately, when posted back to the controller, "Products" is null.
namespace MyProject.Models
{
public class ChartViewModel
{
public Chart ChartItem { get; set; }
public IEnumerable<Product> Products { get; set; }
}
}
Controller:
[Authorize]
[ValidateInput(false)]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(ChartViewModel objChartViewModel)
{
if (!TryUpdateModel(objChartViewModel))
{
return View(objChartViewModel);
}
else
{
} return View("Details", objChartViewModel);
}
How the checkboxes are added to my view, mapped to the "Product" object within my view model:
#{
foreach (MyProject.Models.Product objProduct in Model.Products)
{
#Html.CheckBox("product" + objProduct.Id, Model.ChartItem.ChartProducts.Select(t => t.ProductId).Contains(objProduct.Id));
#String.Format("{0} {1}", objProduct.Manufacturer.Name, objProduct.Name);<br />
}
}
You can send your lists (IEnumerable<T>) down to the view but when they don't come back up to the controller. The only properties of your ViewModel that have values are those with exactly matching items in the forms collection. So add a properties to your ViewModel like SelectedProductID.
This sets up a drop down list that sends its selected value back to the controller:
<div class="editor-field">
#Html.DropDownListFor(m => m.SelectedEmployeeID,
new SelectList(Model.EmployeeList, "EmployeeID", "EmployeeName"), "--Please select an Employee--")
#Html.ValidationMessageFor(model => model.SelectedEmployeeID)
</div>
Notice: the property being set is SelectedEmployeeID but this comes from the EmployeeList.
In your case the values many be in the collection (only "checked" checkboxes get sent in a post) so you could do:
string value = collection["myProductID"];
if its there its checked.
sorry for the messiness, this was in a bit of a rush. See this for more info:
MVC 3 form post and persisting model data
Related
I have a view that renders 2 partial views. One partial is a list of people. The other partial is a modal edit form to edit a single person. I have a viewmodel that contains a person model and a list of person model. I pass the viewmodel to the view and specify the specific models for the partials. I get an error saying the view requires the PersonModel instead of the PersonViewModel. However, the view needs the viewmodel or it wont work.
The Viewmodel:
public class PersonViewModel
{
public Person Person { get; set; }
public List<Person> PeopleList { get; set; }
}
The view (Index):
#model App.Models.ViewModels.PersonViewModel
<h1>Manage People</h1>
#Html.Partial("_personEditForm", Model.Person)
#Html.Partial("_PersonGrid", Model.PersonList)
The person edit partial calls the Person model:
#model App.Models.Person
and the grid partial calls the same model as a list:
#model List<App.Models.Person>
This works fine if the page has only one partial, but this is the first time I have tried it with 2 and it fails. The Index view must have the ViewModel for the partials to work, so not sure why it wont take it.
I was able to fix this by passing the ViewModel to the view, and passing it to both partials:
#model App.Models.ViewModels.PersonViewModel
<h1>Manage People</h1>
#Html.Partial("_PersonEditForm", Model)
#Html.Partial("_PersonGrid", Model)
Then inside the partials, I specified the models within the viewmodel. For example, in the form partial, the textbox helpers went from:
#Html.TextBoxFor(m => m.FirstName, new { #class = "form-control" })
To
#Html.TextBoxFor(m => m.Person.FirstName, new { #class = "form-control" })
Just wanted to add my 2 cents since this has also been driving me crazy. What I have done is created separate viewmodels for each of the partial views. One similar to PersonEdit and one similar to PersonList.
So you end up with your main VM, and 2 smaller ones.
public class PersonIndexViewModel
{
public PersonEditViewModel Person { get; set; }
public List<PersonListViewModel> PeopleList { get; set; }
}
public class PersonEditViewModel
{
// ...whatever info you want in your edit view
}
public class PersonListViewModel
{
// ... whatever info you want in your table grid
}
Then you call it like so
#model App.Models.ViewModels.PersonViewModel
<h1>Manage People</h1>
#Html.Partial("_PersonEditForm", Model.Person)
#Html.Partial("_PersonGrid", Model.PeopleList)
I'm new to MVC so this may sound silly, but here goes: I have a model that contains two lists that need to be passed to an edit form:
public class BaseViewModel
{
public IEnumerable<portal_notifications_types> Types { get; set; }
public IEnumerable<portal_notifications_importances> Importances { get; set; }
}
In the edit form, i Have two dropdownlists for this fields:
#Html.DropDownListFor(m => m.Notification.TypeId, new SelectList(Model.Types, "Id", "Type"), "-- Select type --", new { onchange = "GetNotifType();", style = "width:150px;" })
#Html.DropDownListFor(m => m.Notification.ImportanceId, new SelectList(Model.Importances, "Id", "Importance"), "-- Select importance --", new { style = "width:150px;" })
When I first enter the edit view, everything is ok, the dropdownlists are populated and the corresponding value is selected.
However, when I submit the form, the dropdownlists throw an error, because the Model.Types and Model.Importances lists are null.
How could I overcome this ? I would like to avoid using ViewBag to store those lists, although I know it would work.
Pass the View Model again in your Post Action Method.
[HttpPost]
public ActionResult Index(ViewModel m)
{
return View(m); //Pass the View Model again.
}
You have to repopulate these two SelectLists in the Controller POST method for Edit and again pass the ViewModel in the view for edit. Please share your Controller code for Edit for more details.
I have a model with two entities (linked with a foreign key) and each entity has its own tab rendered using a partial view. Each tab also has its own Ajax form. When I save the entity in the first tab I now have the ID of the entity which I want to return to the two partial views in order to enable the saving of the second entity or saving updates to the first entity. I cannot get this value back to the view.
The model:
public class Entity1
{
int ID1 { get; set; }
[Some attributes]
string field1 { get; set; }
}
public class Entity2
{
int ID2 { get; set; }
[Some attributes]
string field2 { get; set; }
}
public class MyModel
{
Entity1 entity1 = new Entity1()
Entity2 entity2 = new Entity2()
}
The controller:
public class MyController : Controller
{
[HttpGet]
public ActionResult Index()
{
var model = new MyModel();
model.entity1.ID1 = 0;
model.entity2.ID2 = 0;
return PartialView(model);
}
[HttpPost]
public ActionResult Index(MyModel model)
{
SaveMyModel(model)
// have tried ModelState.Clear(); here
return PartialView(model);
}
}
And finally one of the two partial views
#model MyModel
#using (Ajax.BeginForm("Index", "Home",
new AjaxOptions
{
HttpMethod = "POST"
}
))
{
#Html.LabelFor(m => m.Entity1.field1)
#Html.EditorFor(m => m.Entity1.field1)
#Html.HiddenFor(m => m.Entity1.ID1)
<div class="form-actions">
<button type="submit">
Next section</button>
</div>
}
My save function either inserts or updates depending on the value of ID1.
The problem is that the values of ID1 always stays at zero and the hidden field is not refreshed on the return. I have tried single stepping through the razor refresh and the correct ID is being sent to the view.
The above is a simplification but it does encapsulate the problem.
Thank you in advance.
UPDATE
I can get this to work if:
I only have a single entity in my model
I add ModelState.Clear(); before the save
I was running into the same issue on my project. The only way for me to resolve it was to not include the id when the it was 0. That way when it came back the id was replaced. So in your example you would do the following:
#model MyModel
#using (Ajax.BeginForm("Index", "Home",
new AjaxOptions
{
HttpMethod = "POST"
}
))
{
#Html.LabelFor(m => m.Entity1.field1)
#Html.EditorFor(m => m.Entity1.field1)
#if(Model.Entity1.ID1 !=0){
Html.HiddenFor(m => m.Entity1.ID1)
}
<div class="form-actions">
<button type="submit">
Next section</button>
</div>
}
You need to remove the value from the ModelState if you intend to modify it in your POST controller action:
ModelState.Remove("Entity1.ID1");
This way you don't need to clear the entire ModelState using ModelState.Clear but only the value you are actually modifying. This way the Html helper will pick the value from your model and not the one in the ModelState.
I have a generic "Index" page which lists all the entries for a given table and there is a side-bar which allows filtering the data in the grid. My model is as follows:
public class GenericFormIndexModel
{
public IEnumerable<IGenericForm> Entries { get; set; }
public FormSearchQueryModel Query { get; set; }
}
In the razor file I have an html like this:
#using (Html.BeginForm("Search", controllerName, FormMethod.Post, new { id = "fSearch" }))
{
#Html.HiddenFor(m => m.Query.PageIndex)
#Html.HiddenFor(m => m.Query.PageSize)
#Html.HiddenFor(m => m.Query.SortBy)
...etc
#Html.TextBoxFor(m => m.Query.SerialNumber, null, new { #class = "inputbox right-search-field" })
...etc
and I have defined an action as follows:
[HttpPost]
public virtual ActionResult Search(FormSearchQueryModel queryModel)
{
//Implementation ommited
}
Now, the problem is that the values from the form are indeed submitted, but do not bind to my "queryModel" argument in the action. I can see them in Request.Form["Query.Something"].
I do not wish to submit the entire Model, as it is not necessary to post all the entries and whatever else back. Is it possible to get MVC to bind to a nested property or am I stuck with using Reqest.Form[""] ?
Did you try setting the Prefix property as below,
[HttpPost]
public virtual ActionResult Search([Bind(Prefix="Query")]FormSearchQueryModel queryModel)
{
//Implementation ommited
}
The Bind attribute has other properties like Include, Exclude through which you can control what are the posted values need to be binded.
I have a Model Wrapper class that wrap another model:
public class LogicPage {
public MyPage Pg { get; set; }
}
public class MyPage {
public string name { get; set; }
}
In my create form view I use strongly typed form helper:
#model MyAppCore.Components.LogicPage
#using (Html.BeginForm("Create","PageAdmin",FormMethod.Post)) {
<div class="editor-field">
#Html.EditorFor(model => model.Pg.name)
#Html.ValidationMessageFor(model => model.Pg.name)
</div>
...
}
In my controller, I define the action as:
[HttpPost]
public ActionResult Create(LogicPage logicpg)
{
return View("View",logicpg.Pg);
}
However, when the MyPage Pg is displayed in the "View" which list details of the model (MyPage), no user inputted data are shown, the details of the page shown are all MyPage default values (initialized with default no parameter constructor of LogicPage). Looks like data in the form are not passing to the model in the action. Can someone please help me why the data is not passing?
To clarify more, I have two views "Create" form view of model LogicPage and the "View" detail view for model MyPage
Create.cshtml
#model MyAppCore.Components.LogicPage
View.cshtml
#model MyAppCore.Components.MyPage
Thanks
You say you're doing
[HttpPost]
public ActionResult Create(LogicPage logicpg)
{
return View("View",logicpg.Pg);
}
But given that MyPage doesn't inherit from LogicPage, and your view expects an instance of LogicPage, I think you should be doing:
[HttpPost]
public ActionResult Create(LogicPage logicpg)
{
return View("View",logicpg);
}