I have a model which has a field
[DisplayName("Receive occasional email notifications about new services or features")]
public bool ReceiveEmail { get; set; }
In my view I want a checkbox which will come checked by default.
I tried this:
<%:Html.CheckBoxFor(m => m.registerModel.ReceiveEmail, new { #checked = "checked" })%>
But did not work...
Any help will be appreciated.
Thanks
Arnab
The proper way to do this is to set your view model property in the controller action rendering this view:
public ActionResult Foo()
{
var model = new MyViewModel
{
registerModel = new registerModel
{
ReceiveEmail = true
}
};
return View(model);
}
Now all you need in your strongly typed view is:
<%= Html.CheckBoxFor(m => m.registerModel.ReceiveEmail) %>
and the checkbox will be automatically checked.
Related
I am using ASP.Net MVC with C#. I have a model which has a member for filter criteria. This member is a IList>. The key contains value to display and the value tells if this filter is selected or not. I want to bind this to bunch of checkboxes on my view. This is how I did it.
<% for(int i=0;i<Model.customers.filterCriteria.Count;i++) { %>
<%=Html.CheckBoxFor(Model.customers.filterCriteria[i].value)%>
<%=Model.customers.filterCriteria[i].key%>
<% } %>
This displays all checkboxes properly. But when I submit my form, in controller, I get null for filtercriteria no matter what I select on view.
From this post I got a hint for creating separate property. But how will this work for IList..? Any suggestions please?
The problem with the KeyValuePair<TKey, TValue> structure is that it has private setters meaning that the default model binder cannot set their values in the POST action. It has a special constructor that need to be used allowing to pass the key and the value but of course the default model binder has no knowledge of this constructor and it uses the default one, so unless you write a custom model binder for this type you won't be able to use it.
I would recommend you using a custom type instead of KeyValuePair<TKey, TValue>.
So as always we start with a view model:
public class Item
{
public string Name { get; set; }
public bool Value { get; set; }
}
public class MyViewModel
{
public IList<Item> FilterCriteria { get; set; }
}
then a controller:
public class HomeController : Controller
{
public ActionResult Index()
{
return View(new MyViewModel
{
FilterCriteria = new[]
{
new Item { Name = "Criteria 1", Value = true },
new Item { Name = "Criteria 2", Value = false },
new Item { Name = "Criteria 3", Value = true },
}
});
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
// The model will be correctly bound here
return View(model);
}
}
and the corresponding ~/Views/Home/Index.aspx view:
<% using (Html.BeginForm()) { %>
<%= Html.EditorFor(x => x.FilterCriteria) %>
<input type="submit" value="OK" />
<% } %>
and finally we write a customized editor template for the Item type in ~/Views/Shared/EditorTemplates/Item.ascx or ~/Views/Home/EditorTemplates/Item.ascx (if this template is only specific to the Home controller and not reused):
<%# Control
Language="C#"
Inherits="System.Web.Mvc.ViewUserControl<AppName.Models.Item>"
%>
<%= Html.CheckBoxFor(x => x.Value) %>
<%= Html.HiddenFor(x => x.Name) %>
<%= Html.Encode(Model.Name) %>
We have achieved two things: cleaned up the views from ugly for loops and made the model binder successfully bind the checkbox values in the POST action.
I am trying to figure out how this DropDownListFor works but with no success.
My controller creates a list of SelectListItems where I put all the groups found in the database:
viewModel.Groups = LoadGroups(viewModel.User.AssociatedGroups);
this it the method:
private IList<SelectListItem> LoadGroups(List<String> associatedGroups)
{
var groups = _SecurityService
.LoadGroups()
.Select(e => new SelectListItem
{
Selected = associatedGroups.Contains<System.String>(e.Id.ToString()),
Value = e.Id.ToString(),
Text = e.Name
}).ToList();
return (groups);
}
As you can see I set the Selected element if there are associated groups in the list.
I put this list in a field (Groups) of my custom viewmodel:
public class UsersViewModel
{
public UsersViewModel()
{
this.Groups = new List<SelectListItem>();
}
public Models.User User { get; set; }
public IList<SelectListItem> Groups { get; set; }
}
and send UsersViewModel to the view. I use this code to build a dropdown with multi-selection:
<%=Html.DropDownListFor(m => m.User.AssociatedGroups, (List<System.Web.Mvc.SelectListItem>)Model.Groups, new { #class = "dropDownGroups", multiple = "multiple" })%>
AssociatedGroups is a field the class Users (which is a member of my viewmodel):
public List<String> AssociatedGroups { get; set; }
There's nothing peculiar here.
If I use this code I can I can't see the elements of the dropdown selected (and they have the attribute set, I've double checked), but I can bind the selections with AssociatedGroups when I post the form.
If I change AssociatedGroups (field of the User class) with a string:
public String AssociatedGroups { get; set; }
I have the opposite behaviour:
I can see the elements of the dropdown checked but when I post the form there's no binding, or better, only one element is bound.
I've spend most of my day trying to figure out what the problem and I've tried different combinations but none of them seem to work.
Is there anyone who can try to help me?
Thanks.
You need two properties on your view model: one that will contain the selected group ids and one that will contain the list:
public class UsersViewModel
{
public IEnumerable<SelectListItem> Groups { get; set; }
public IEnumerable<string> SelectedGroupIds { get; set; }
}
and then you would use the ListBoxFor helper to allow for multiple choices:
<%= Html.ListBoxFor(
m => m.SelectedGroupIds,
new SelectList(Model.Groups, "Value", "Text"),
new { #class = "dropDownGroups" }
) %>
Now assuming the following view model is passed to the view:
public ActionResult Index()
{
var model = new UsersViewModel
{
// TODO: Use a repository to fetch those values
// and map them to the view model
Groups = new[]
{
new SelectListItem { Value = "1", Text = "group 1" },
new SelectListItem { Value = "2", Text = "group 2" },
new SelectListItem { Value = "3", Text = "group 3" },
},
// We want to preselect the last two groups in the listbox
SelectedGroupIds = new[] { "2", "3" }
};
return View(model);
}
[HttpPost]
public ActionResult Index(IEnumerable<string> selectedGroupIds)
{
// Here we will get the list of selected ids
// when the form containing the listbox is
// submitted
...
}
Then on the view the last two elements will be automatically preselected.
Im new to MVC2/3 so keep that in mind. Also, using Ajax or jQuery is NOT an option.
I have a web page where the user must choose an item out of a dropdown list and then hit a "Filter" button. (Clicking this button will simply trigger the default POST action in my controller and return a filtered list of results.
I have everything working except I am running into one issue. When the Filter action is complete and returns control back to my view, the dropdown list contents are lost (ie, null). The results are returned no problem, it's just that my dropdown list is blank - thus preventing user from selecting another item out of the list.
Am I supposed to re-fill the dropdown list on the Filter action or is there a cleaner way to do this?
Here is a snapshot of my code:
My ViewModel
public class MyViewModel {
[DisplayName("Store")]
public IEnumerable<Store> StoreList { get; set; }
public string SelectedStore { get; set; }
}
My View (Index.cshtml)
#using (Html.BeginForm()) {
<h2>Search</h2>
#Html.LabelFor(m => m.StoreList)
#Html.DropDownListFor(m => m.SelectedStore, new SelectList(Model.StoreList, "StoreCode", "StoreCode"), "Select Store")
<input type="submit" value="Filter" />
}
My Controller:
public class MyController : Controller
{
public ActionResult Index() {
MyViewModel vm = new MyViewModel();
var storelist = new List<Store>();
storelist.Add(new Store { StoreCode = "XX" });
storelist.Add(new Store { StoreCode = "YY" });
storelist.Add(new Store { StoreCode = "ZZ" });
vm.StoreList = storelist;
return View(vm);
}
[HttpPost]
public ActionResult Index(MyViewModel model, string SelectedStore, FormCollection collection) {
if (ModelState.IsValid) {
/* this works, model state is valid */
/* BUT, the contents of model.StoreList is null */
}
return View( model);
}
}
Yes, you have to repopulate any models (including ViewData) that are passed to the view. Remember, it's a stateless system, your controller is re-instantiated with every call and starts effectively from scratch.
I would do it thus:
public class MyController : Controller
{
private List<Store> GetStoreList()
{
List<Store> StoreList = new List<Store>();
// ... Do work to populate store list
return StoreList;
}
public ActionResult Index() {
MyViewModel vm = new MyViewModel();
vm.StoreList = GetStoreList();
return View(vm);
}
[HttpPost]
public ActionResult Index(MyViewModel model, string SelectedStore, FormCollection collection) {
if (ModelState.IsValid) {
/* this works, model state is valid */
/* BUT, the contents of model.StoreList is null */
}
model.StoreList = GetStoreList();
return View( model);
}
}
Short answer is yes, you need to refill the drop-down list in the Filter action. ASP.NET MVC isn't WebForms - there is no ViewState to preserve the contents of your list.
Again fill the dropdown as mvc don't have View state
[HttpPost]
public ActionResult Index(MyViewModel model, string SelectedStore, FormCollection collection) {
if (ModelState.IsValid) {
/* this works, model state is valid */
/* BUT, the contents of model.StoreList is null */
}
model.StoreList = GetStoreList();
return View( model);
}
I am having a problem with a selectlistitem, the values of which are being retrieved from a database.
It is displaying the list of items in the view, but it is not passing through (POSTing) the selected value into the model.
So when the user submits, or the page reloads due to validation, the select value (PositionApplied) is empty.
Can anyone give me some pointers as to where I am going wrong?
In my controller:
[HttpGet]
public ActionResult Index()
{
PopulateJobsDropdown();
return View();
}
private void PopulateJobsDropdown()
{
IEnumerable<SelectListItem> items = _service.GetJobs()
.Select(c => new SelectListItem
{
Value = c.JobID.ToString(),
Text = c.JobTitle
});
ViewData["PositionApplied"] = items;
}
In my ViewModel
public IEnumerable<SelectListItem> PositionApplied { get; set; }
In my View
<%=Html.DropDownList("PositionApplied")%>
Thanks in advance for any pointers!
So, where is the code line that get's the
ViewData["PositionApplied"] = items;
into
public IEnumerable<SelectListItem> PositionApplied { get; set; }
something like:
this.PositionApplied = ViewData["PositionApplied"] as IEnumerable<SelectListItem>;
and you can simple use in your View:
<%
IEnumerable<SelectListItem> PositionApplied =
ViewData["PositionApplied"] as IEnumerable<SelectListItem>;
%>
...
<%= Html.DropDownList("myDropDOwnId", PositionApplied) %>
or is there some of automagical happening under MVC2 that I'm not aware about? As I use the example I give you, all the time.
Added
in order to avoid Linq to Entities error (if you are using it) change your method to
private void PopulateJobsDropdown()
{
IQueryble<Your_Table> jobs = _service.GetJobs();
List<SelectListItem> items = new List<SelectListItem>();
foreach(var job in jobs)
items.add(new SelectListItem
{
Value = c.JobID.ToString(),
Text = c.JobTitle
});
ViewData["PositionApplied"] = items;
}
and all will work fine.
I have a ViewModel with a property as below:
[DisplayName("As Configured On:")]
[DisplayFormat(DataFormatString="{0:d}", ApplyFormatInEditMode=true)]
public DateTime ConfigDate { get; set; }
The Form that displays the ConfigDate is as below:
<%= Html.EditorFor(Model => Model.ConfigDate)%>
When the Index Action comes back, everything looks formatted correctly, i.e. the <input> box has the date value as 12/12/2001. When the form is posted, the result that comes back is as though the DisplayFormat attribute isn't being applied.
EDIT:
More info was requested: here is the code en toto:
The Search Form
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Config.Web.Models.AirplanesViewModel>" %>
<% using (Html.BeginForm("Details", "Airplanes", FormMethod.Post, new { id = "SearchForm" })) { %>
<%= Html.LabelFor(model => model.ConfigDate) %>
<%= Html.EditorFor(Model => Model.ConfigDate)%>
<input id="searchButton" type="submit" value="Search" />
<% } %>
The AirplanesViewModel
public class AirplanesViewModel
{
[DisplayName("As Configured On:")]
[DisplayFormat(DataFormatString="{0:d}", ApplyFormatInEditMode=true)]
public DateTime ConfigDate { get; set; }
}
}
The Controller
[HttpGet]
public ActionResult Index()
{
AirplanesViewModel avm = new AirplanesViewModel
{
ConfigDate = DateTime.Now
};
return View(avm);
}
[HttpPost]
[ActionName("Details")]
public ActionResult Details_Post(AirplanesViewModel avm)
{
return RedirectToAction("Details", avm);
}
[HttpGet]
public ActionResult Details(AirplanesViewModel avm)
{
int page = 0;
int pageSize = 10;
if (!ModelState.IsValid)
{
avm.Airplanes = new PaginatedList<Airplane>();
return View(avm);
}
try
{
Query q = new Query(avm.Query);
PaginatedList<Airplane> paginatedPlanes = new PaginatedList<Airplane>(repo.ByQuery(q), page, pageSize);
avm.Airplanes = paginatedPlanes;
return View(avm);
}
catch (Exception)
{
// Should log exception
avm.Airplanes = new PaginatedList<Airplane>();
return View(avm);
}
}
Additional Information
It has something to do with the redirection to the GET Action. When I take out the POST Action and remove the GET attribute (so both GET and POST use the Details() Action) the problem goes away - but this also gets rid of my pretty URL's when the form is submitted (and causes the annoying "are you sure?" popup on refresh). Strangely, the only problem is the loss of formatting in that field. Everything else is fine.
While waiting for you to clearly specify the problem, here's a full working counter example that what you describe in your question doesn't actually happen:
Model:
public class MyViewModel
{
[DisplayName("As Configured On:")]
[DisplayFormat(DataFormatString = "{0:d}", ApplyFormatInEditMode = true)]
public DateTime ConfigDate { get; set; }
}
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
var model = new MyViewModel
{
ConfigDate = DateTime.Now
};
return View(model);
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
return View(model);
}
}
View:
<% using (Html.BeginForm()) { %>
<%= Html.EditorFor(x => x.ConfigDate) %>
<input type="submit" value="OK" />
<% } %>
You can submit the form as much as you wish, the formatting will be preserved.
UPDATE:
After providing additional information here's why the problem occurs. When you redirect to the Details action with return RedirectToAction("Details", avm); a query string parameter is applied to the url:
http://localhost:1114/Airplanes/Details?ConfigDate=11/30/2010%2000:00:00
Notice how the hour is included and that's normal. Now when you return the view in the Details GET action the HTML helper responsible for generating the editor template will do the following tasks:
Check to see whether there's a ConfigDate parameter (either GET or POST). If none was found check the value of the Model which is passed to the view and use the ConfigValue property of the model and generate the textbox.
As a ConfigValue is found in the query string the model is not used at all. So it simply takes the value passed in the redirect which also contains the time and uses it to bind to it.