I'm trying to implement what this answer suggests but without storing the display name in my model code, so I believe it's a separate question.
I have an MVC view
<%# Page Language="C#" MasterPageFile="PathToMaster" Inherits="System.Web.Mvc.ViewPage<ModelData>" %>
and a model:
public class ModelData {
public bool Option1 { get; set; }
}
and I have a .aspx with HTML markup for a form that contains a checkbox:
<label for="MyCheckbox">Your choice</label>
<%= Html.CheckBoxFor(Model=>Model.Option1, new { id="Option1", #class="checkbox", onchange="return myValidation();", name="MyCheckbox", value="Option one" } ) %>
which when it is compiled yields this HTML:
<label for="MyCheckbox">Your choice</label>
<input class="checkbox" id="Option1" name="Option1" onchange="return myValidation();" type="checkbox" value="Option one" /><input name="Option1" type="hidden" value="false" />
and whenever I check the checkbox and submit the form the controller-action
class MyController : Controller {
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult RequestStuff( ModelData data )
{
}
}
receives data where Option1 is false.
What am I doing wrong? How do I make the checkbox map to the model member?
Since you said changing the alias in the helper expression didn't cut it, I took the time to prepare the MVC2 test project, here's how it looks:
MyModel.cs:
namespace TestAppMVC2.Models
{
public class MyModel
{
public bool Option1 { get; set; }
}
}
Controller's actions:
public ActionResult MyAction()
{
var viewModel = new MyModel();
return View(viewModel);
}
[HttpPost]
public ActionResult MyAction(MyModel viewModel)
{
bool option = viewModel.Option1;
return View();
}
The view (MyAction.aspx):
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<TestAppMVC2.Models.MyModel>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
MyAction
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>MyAction</h2>
<% using (Html.BeginForm()) {%>
<%: Html.ValidationSummary(true) %>
<fieldset>
<legend>Fields</legend>
<div class="editor-label">
<%: Html.LabelFor(model => model.Option1) %>
</div>
<div class="editor-field">
<%: Html.CheckBoxFor(model => model.Option1) %>
<%: Html.ValidationMessageFor(model => model.Option1) %>
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
<% } %>
<div>
<%: Html.ActionLink("Back to List", "Index") %>
</div>
Looks like this:
And the result after checking the checkbox:
By the way, the View was generated entirely (well, almost entirely, I changed TextBoxFor to CheckBoxFor) by Add View -> Strongly Typed View for the MyModel class and template: Edit.
So it looks like the binding above should be OK. Try this and then you can fiddle with custom options on checkboxes. :)
Like I wrote in the other answer, the idea is essentially the same, only in MVC3 the Razor syntax looks a bit cleaner. But that's about it when it comes to differences. :)
In your line:
<%= Html.CheckBoxFor(Model=>Model.Option1, new { id="Option1", #class="checkbox", onchange="return myValidation();", name="MyCheckbox", value="Option one" } ) %>
Replace Model with another alias, i.e. m. So it looks like:
<%= Html.CheckBoxFor(m=>m.Option1, new { id="Option1", #class="checkbox", onchange="return myValidation();", name="MyCheckbox", value="Option one" } ) %>
You referred to the Model directly where CheckBoxFor takes an an HTML helper instance.
I believe the following maybe appropriate to help you understand further:
MVC HTML Helpers and Lambda Expressions
Related
I need to send two objects to my controller using bindings but I have no idea how to do it. I just know how to send one object not multiple objects.
This is an example of how I'm doing it with a singular object.
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.master" Inherits="System.Web.Mvc.ViewPage<Inventario.Models.Report>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Create
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>Create</h2>
<script src="<%: Url.Content("~/Scripts/jquery.validate.min.js") %>" type="text/javascript"></script>
<script src="<%: Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js") %>" type="text/javascript"></script>
<% using (Html.BeginForm()) { %>
<%: Html.ValidationSummary(true) %>
<fieldset>
<legend>Report</legend>
<div class="editor-label">
<%: Html.LabelFor(model => model.Client, "Client_1") %>
</div>
<div class="editor-field">
<%: Html.DropDownList("Client", String.Empty) %>
<%: Html.ValidationMessageFor(model => model.Client) %>
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
<% } %>
<div>
<%: Html.ActionLink("Back to List", "Index") %>
</div>
</asp:Content>
Then, It is received by the controller this way:
[HttpPost]
public ActionResult Create(Report report)
{
//Some code here
}
And what I need is to get two objects. IDK maybe something like this:
[HttpPost]
public ActionResult Create(Report report, AnotherObject ao)
{
//Some code here
}
I'm a beginer on this. Any guide you can give me will help me a lot. Thanks by advance.
You have to create a ViewModel like this:
public class MyViewModel
{
public Report report {get;set;}
pulic AnotherObject ao {get;set;}
}
From you Get Action pass them:
public ActionResult Create()
{
MyViewModel model = new MyViewModel();
model.report = new Report();
model.ao = new AnotherObject();
return View(model);
}
In your view set Model to MyViewModel
and post it in action:
[HttpPost]
public ActionResult Create(MyViewModel myModel)
{
//Some code here
}
I am following the NerdDinner part in the Book Professional ASP.NET MVC 2. Currently i am at the part where i need to implement the DinnerFormViewModel and the Renderpartial Dinnerform.
The book contains some errors here so I tried to search on the internet and fix it myself..
I have put the DinnerFormViewModel in the Models folder this is the code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace NerdDinner.Models
{
public class DinnerFormViewModel : Controller
{
private static string[] _countries = new[]{
"USA",
"Ireland",
"Scotland",
"Namibia"
};
//Properties
public Dinner Dinner { get; private set; }
public SelectList Countries { get; private set; }
//Constructor
public DinnerFormViewModel(Dinner dinner)
{
Dinner = dinner;
Countries = new SelectList(_countries, dinner.Country);
}
// GET: /DinnerFormViewModel/
public ActionResult Index()
{
return View();
}
}
}
Then i have made the DinnerForm.ascx (Partial class):
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewPage<NerdDinner.Models.DinnerFormViewModel>" %>
<%: Html.ValidationSummary("Please correct the errors and try again.") %>
<% using (Html.BeginForm()) { %>
<fieldset>
<p>
<%: Html.LabelFor(m => m.Dinner.Title) %>
<%: Html.TextBoxFor(m => m.Dinner.Title) %>
<%: Html.ValidationMessageFor(m => m.Dinner.Title, "*") %>
ETC...
and i have made the edit.aspx as follows:
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<NerdDinner.Models.DinnerFormViewModel>" %>
<asp:Content ID="Title" ContentPlaceHolderID="TitleContent" runat="server">
Edit: <%: Model.Dinner.Title %>
</asp:Content>
<asp:Content ID="Edit" ContentPlaceHolderID="MainContent" runat="server">
<h2>Edit Dinner</h2>
<% Html.RenderPartial("DinnerForm"); %>
</asp:Content>
Now if i start the application, an error at <% Html.RenderPartial("DinnerForm"); %> will popup saying:
c:\Windows\Microsoft.NET\Framework\v4.0.30319\Temporary ASP.NET Files\root\c8cca855\23406a1e\App_Web_dinnerform.ascx.32d6c807.tczxq3bd.0.cs(166): error CS0030: Cannot convert type 'ASP.views_dinners_dinnerform_ascx' to 'System.Web.Mvc.ViewUserControl'
I think it has something to do with the namespaces, but i can't fix the error, someone faced the same problem or someone here that can help me out?? Thank you!:)
Your partial view should inherit from System.Web.Mvc.ViewUserControl.
ViewPage is for a full view.
This is my model:
public class IndexViewModel
{
public FilterConditions conditions { get; set }
public IEnumerable<SelectListItem> Countries { get; set }
}
public class FilterConditions
{
public string condition11 { get; set }
// ...
}
And I have an Index action method like so:
public ActionResult Index()
{
var model = new IndexViewModel();
// fill the model here with default values
return View(model);
}
The view renders a form with the filterconditions as input types.
Now I want the post back from that form be handled by this action method:
[HttpPost]
public ActionResult Index(FilterConditions model)
{
// do some magic with model and return another view here
}
and this actually works (I put a breakpoint in the method, and it gets called), but the properties of my model are always empty (default values), while they should contain the values which were posted by the form.
When I modify the action method like this:
[HttpPost]
public ActionResult Index(IndexViewModel model)
{
// do some magic with model.conditions and return another view here
}
It all works like it should, but this is not "right" (IMHO), as I don't need the ´Countries´ list on return, I only need the selected country (which is one of the conditions).
What is a nice (best practice) way to make this work without having to take the whole original viewmodel as an input parameter?
Btw, I'm using ASP.NET MVC 2 (I don't think it really matters, as I think it's the same problem in MVC 3, but I'm not entirely sure of that).
(I have been looking around the internet for "best practices" regarding dropdownlists and viewmodels within asp.net mvc, but the different recommendations I found didn't really line up with each other, and a lot is already outdated as well. I didn't find an "official" best practice around this. I hope I'm going in the right direction (having the list as part of my viewmodel), feel free to correct me on this matter if I'm not. Also feel free to point me to "endorsed best practices" about this if you know of any.)
Update:
I found out that I can use the [Bind] attribute with a Prefix of "filterconditions". And this indeed works for this view. But my original problem (I admit, it was not included in my question) is not solved.
It happens that this particular action method is also called from another view (it is an ajax call) where it doesn't have that prefix, in that case it doesn't work any more now. Any suggestions?
I've found the solution.
Apparently, when I use the same name for the parameter variable as the name of the type (the case doesn't have to match), like this:
[HttpPost]
public ActionResult Index(FilterConditions filterConditions)
{
// do some magic with model and return another view here
// now the filterConditions variable actually contains values!
}
Everything works like it should (the values of my filterConditions are not empty/null anymore). Apparently, the default modelbinder uses the name of the parameter as the potential prefix for the binding.
I'm glad I found out, but it would be nice if this is more clearly documented somewhere. It's not obvious at all.
Edit:
On request: this is the code in my view (aspx):
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MyProject.Models.IndexViewModel>" %>
<%-- ... more stuff here ... --%>
<% using (Html.BeginForm())
{%>
<%= Html.ValidationSummary(true)%>
<fieldset>
<div class="editor-label">
<%= Html.LabelFor(model => model.FilterConditions.Country)%>
</div>
<div class="editor-field">
<%= Html.DropDownListFor(model => model.FilterConditions.Country, Model.Countries)%>
<%= Html.ValidationMessageFor(model => model.FilterConditions.Country)%>
</div>
<div class="editor-label">
<%= Html.LabelFor(model => model.FilterConditions.Make)%>
</div>
<div class="editor-field">
<%= Html.TextBoxFor(model => model.FilterConditions.Make)%>
<%= Html.ValidationMessageFor(model => model.FilterConditions.Make)%>
</div>
<%-- ... more fields inserted here ... --%>
<p>
<input type="submit" value=" Search... " />
</p>
</fieldset>
<% } %>
Hi fretje: Now I can use your way to solve your problem, First I have two Models "IndexViewModel" & "Index", and the DropDownList(it's doesn't matter, just offer the DropDown items):
public class IndexViewModel : Index
{
//public int value { get; set; }
public List<System.Web.Mvc.SelectListItem> items { get; set; }
}
public class Index
{
public int value { get; set; }
}
class DropDownList
{
public List<System.Web.Mvc.SelectListItem> GetDropDownList()
{
List<System.Web.Mvc.SelectListItem> result = new List<System.Web.Mvc.SelectListItem>();
result.Add(new System.Web.Mvc.SelectListItem
{
Value = "1",
Text = "Apple"
});
result.Add(new System.Web.Mvc.SelectListItem
{
Value = "2",
Text = "Milk"
});
return result;
}
}
And two Controllers is Test() and Test(Models.Index), I pass the IndexViewModel and postback IndexModel:
public ActionResult Test()
{
var result =
new Models.IndexViewModel
{
value = 1,
items = new Models.DropDownList().GetDropDownList()
};
return View(result);
}
[HttpPost]
public ActionResult Test(Models.Index posatback)
{
return View();
}
The View of Test() is:
<% using (Html.BeginForm()) {%>
<%: Html.ValidationSummary(true) %>
<fieldset>
<legend>Fields</legend>
<div class="editor-field">
<%: Html.DropDownListFor(m=>m.value, Model.items )%>
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
<% } %>
that's work! thank you fretje, I learned one more tech. : )
Maybe you can try
[HttpPost]
public ActionResult Index([Bind(Exclude="Countries")]IndexViewModel model)
{
// do some magic with model.conditions and return another view here
}
Hi~
You don't need to combine the whole SelectListItem to ViewModel, actually your ViewModel just only have a field to store user's choise, integer or string, then use DropDownListFor like:
<%: Html.DropDownListFor(item.WeaponID, MyApplication.Models.DropDownList.GetDropDownList() )%>
please see my post in my blogspot, I use a very simple example to explain:
http://maidot.blogspot.com/2011/04/aspnet-mvc-viewdropdownlistfor.html
let me know if you have any problems : )
I'm trying to create a view that contains a list of checkboxes that is dynamically created from a database, and then retrieve the list of selected ones when the form is posted back.
My EF model contains a class:
public class ItemIWouldLikeACheckboxFor {
public int Id { get; set; }
public string Description { get; set; }
}
I have a view model that contains a list of these:
public class PageViewModel {
// various other properties
public List<ItemIWouldLikeACheckboxFor> checkboxList { get; set; }
}
My controller get method:
public ActionResult Create() {
var viewModel = new PageViewModel();
viewModel.checkboxList = db.ItemIWouldLikeACheckboxFors.ToList();
return View(viewModel);
}
My view:
<% using (Html.BeginForm()) { %>
<%-- other stuff here... %>
<% foreach (var item in checkboxList) { %>
<%: Html.CheckBox( <!-- what exactly ?????? -->) %>
<% } %>
<%-- other stuff here...%>
<input type="submit" />
<% } %>
My controller post method:
[HttpPost]
public ActionResult Create(PageViewModel viewModel) {
// do stuff with other fields
// I would like to do something like:
foreach (var item in selectedCheckBoxes) {
// do stuff
}
}
I can't seem to get it to work. My basic questions are mixed in as comments in the code snippets, but to recap:
Is my view model OK? (do I need to add anything to capture the selected ones as opposed to simply the list to display?)
What exactly should I put in the view to render each check box?
How do I access the selected checkboxes in the controller after the post?
Have you seen: http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx?
We basically wrote our own control to render the HTML like
<label for="Products"> Select Products </label>
<ul class="checkBoxList">
<li>
<input type="hidden" value="0" name="Products.Index">
<input type="checkbox" value="3424" name="Products[0].Id" id="Products0">
<label for="Products0">iPod touch 3rd Generation</label>
</li>
<li>
<input type="hidden" value="1" name="Products.Index">
<input type="checkbox" value="3123" name="Products[1].Id" id="Products1">
<label for="Products1">Creative Zen</label>
</li>
</ul>
</div>
Model Looks Ok, we wrote a custom helper, so our aspx pages look like:
<%= Html.DropDownFor(m=>m.products) %>
If you follow phil haacks post, your model should automatically bind in your controller.
Also a good answer in this question: CheckBoxList multiple selections: difficulty in model bind back
It has a solution that uses a custom Editor Template.
I am having this issue where I have a form in a partialview but when validating it jumps out of the parent view and appears on its own (having the submit action url).
Also I tried with Render.Action, where validation does not fire at all.
I am using ASP.NET MVC 2 RC and need the server-side validation to work, using the built in validation. Anyone got any suggestions?
Thanks
Code Partial View:
<%=Html.ValidationSummary() %>
<% using (Html.BeginForm("Edit", "Category", FormMethod.Post))
{%>
<fieldset>
<legend>Edit category</legend>
<p>
<label for="CategoryName">Category name:</label>
<%= Html.TextBox("CategoryName", Model.CategoryName)%>
<%= Html.ValidationMessage("CategoryName", "*")%>
</p>
<p class="submit">
<input type="submit" value="Edit" />
<%=Html.AntiForgeryToken()%>
<%= Html.Hidden("CatId", Model.Id)%>
</p>
</fieldset>
<% }
Model Property:
[Required(ErrorMessage="Required")]
public string CategoryName { get; set; }
Edit Action:
[ValidateAntiForgeryToken]
[HttpPost()]
public ActionResult Edit(int catId, CategoryPageViewModel categoryModel)
{
if (ModelState.IsValid)
{
//TODO
}
return View("list", categoryModel);
}
Does your main view also display category? You're passing "list" a categoryModel. Is this enough for the main view as well as the partial view.