I have this bit of code in my model:
public class ContactDaySelectorAttribute : SelectorAttribute
{
public override IEnumerable<SelectListItem> GetItems()
{
var contactDay = new List<ContactDay>
{
new ContactDay {Id = 1, Name = "Monday"},
new ContactDay {Id = 2, Name = "Tuesday"},
new ContactDay {Id = 3, Name = "Wednesday"},
new ContactDay {Id = 4, Name = "Thursday"},
new ContactDay {Id = 5, Name = "Friday"},
new ContactDay {Id = 6, Name = "Saturday"},
};
return contactDay.ToSelectList(m => m.Id, m => m.Name);
}
}
//
public class ContactDay
{
public int Id { get; set; }
public string Name { get; set; }
}
//
[ContactDaySelector]
public virtual int? ContactDayCheckBox { get; set; }
//
[ContactDaySelector]
[ReadOnly(true)]
public string ContactDayCheckBoxPost
{
get { return ContactDayCheckBox.ToString(); }
}
I use ContactDayCheckBox to store the int? value in the database. I use ContactDayCheckBoxPost to display the selected items (checkbox) in a confirmation view.
If I do #Html.EditorFor(m => m.ContactDayCheckBoxPost) in my view, and the user previously selected MORE than one day (for example), it only displays the "Name" for one day (i.e., if Monday, Tuesday previously selected, it only displays "Monday" in the view).
How can I go about getting the view to display all of the choices previously selected? I tried #foreach but it won't work with int, and I tried variations of looping that involved .Split(',') to insert a comma between the choices, but all I get is the actual "Id" value (e.g., "1" for "Monday", instead of "Monday").
Thanks in advance.
ContactDayCheckBoxPost is defined as a string. What are you putting in this string before passing it to the view ?
If in case say you are passing a value as
ContactDayCheckBoxPost = "1, 2, 3, 4";
then in such case you can apply the Split(',') function and getting a list of these ids as string, which then you can use foreach to get the ids.
Also show us what you are using in your view, I found it a little difficult to understand what you are asking here.
THE PROBLEM (as I see it):
I think the problem you're having is that SelectList is a 'key/value' pair, but you're tying to use the 'key' as the 'value'. So for a key 'Monday' with a value '1', 1 would be the value I would expect. See http://codeclimber.net.nz/archive/2009/08/10/how-to-create-a-dropdownlist-with-asp.net-mvc.aspx for an examples and http://msdn.microsoft.com/en-us/library/system.web.mvc.selectlistitem(v=vs.108).aspx for the definitions of the SelectListItem.
If you wanted an easy way to do this, you could probably just use:
return contactDay.ToSelectList(m => m.Name, m => m.Name); //Both the key and the value are Name
This will fail though, since your ContactDaySelector expects a nullable int:
[ContactDaySelector]
public virtual int? ContactDayCheckBox { get; set; }
Does that make sense?
If you want to use this approach, use your class and perform a lookup for the value "1" in your [HttpPost] action to find "Monday". On this note, why do you need to get this as a string (I'll extrapolate on this below)?
THE SOLUTION (as I see it):
A better solution might be to make a enum for your days of the week (actually, days of the week is given example on the enum help page - http://msdn.microsoft.com/en-us/library/sbbt4032(v=vs.80).aspx and it is even a built in enumeration http://msdn.microsoft.com/en-us/library/system.dayofweek.aspx). Then use this throughout your application (e.g. on the [HttpPost] action for your controller) to set this as a numeric value (e.g. (int)Days.Sun), but be able to retrieve its keyword value (e.g. Days.Sun.ToString();). See http://blogs.msdn.com/b/stuartleeks/archive/2010/05/21/asp-net-mvc-creating-a-dropdownlist-helper-for-enums.aspx for an example of using an enum for a DropDownList.
If you have a need to work with the days of the week as a string, I don't really see a need to have a class for the days of the week. Since you're trying to select the same key and value as a string, why not add a list of strings to your ViewBag/ViewModel, e.g.
List<String> daysOfTheWeek = new List<String>() { "Mon", "Tue", "Wed", "Thur", "Fri", "Sat", "Sun" };
ViewBag.DaysOfTheWeek = daysOfTheWeek;
Have a property called "WeekDay" in your Model or ViewModel:
public string WeekDay { get; set; }
Then you could use it to set a string property like so:
#Html.DropDownListFor(model => model.WeekDay, new SelectList(ViewBag.DaysOfTheWeek))
Note: you should really define an edit ViewModel for this approach, but ViewBag will be fine for debugging purposes.
Related
I'm new to MVC 3. I want 3 dropdown lists. The user picks an item from each list and then retrieves the selections based on the matches. For my problem I'm just using 2 dropdowns and code snippets. I have all 3 dropdowns populated - the first two from Linq to Sql and the 3rd is an option list created with Razor (A-Z).
I'm missing the step of setting the values of what was chosen into MemberSetup.SelectedProgramID and MemberSetup.SelectedOrganizationID. I'd like the text stored in MemberSetup.SelectedProgramName and MemberSetup.SelectedOrganizationName respectively. I think if I can get the selected ids into the model, I can pass them through ActionLink and not deal with #Html.Hidden variables. However, no matter what is selection the model SelectedProgramID and SelectedOrganizationID are zero (expect 1, 2, 3 or 4 for Program ID and 1, 2, 5, or 6 for Organization ID).
Any help / corrections would be appreciated.
Model:
{
public int SelectedProgramID { get; set; }
public string SelectedProgramName {
get { return this._myProgramName; }
set { this._myProgramName = value; }
}
public IEnumerable<SelectListItem> ProgramList { get; set; }
public int SelectedOrganizationID { get; set; }
public string SelectedOrganizationName
{
get { return this._myOrganizationName; }
set { this._myOrganizationName = value; }
}
public IEnumerable<SelectListItem> OrganizationList { get; set; }
}
Controller:
{
var db = new STARDataContext();
MemberSetup setupModel = new MemberSetup();
setupModel.ProgramList = db.ProgramAlls.ToList()
.Select(p => new SelectListItem
{
Value = p.ProgramID.ToString(),
Text = p.ProgramName.ToString()
});
setupModel.OrganizationList = db.AreaAlls.ToList()
.Select(p => new SelectListItem
{
Value = p.AreaID.ToString(),
Text = p.AreaName.ToString()
});
return View(setupModel);
}
View:
{
#using ( Html.BeginForm() ) {
#Html.DropDownListFor(model => model.SelectedProgramID, Model.ProgramList)
#Html.DropDownListFor(model => model.SelectedOrganizationID, Model.OrganizationList, "Please select Organization", new { #class = "DropDownList" } )
#Html.Hidden("SelectedProgramID", Model.SelectedProgramID)
#Html.Hidden("SelectedProgramName", Model.SelectedProgramName)
#Html.Hidden("SelectedOrganizationID", Model.SelectedOrganizationID)
#Html.Hidden("SelectedOrganizationName", Model.SelectedOrganizationName)
#Html.ActionLink("Get Members", "Select", new {programID=Model.SelectedProgramID,orgID=Model.SelectedOrganizationID })
}
}
use hiddenFor instead of hidden,so your syntax should look like this
#Html.HiddenFor(a=>a.SelectedProgramName)
Since you have SelectedProgramID and SelectedOrganizationID defined, you won't need to use hidden field for it. To construct the dropdown, you should do something like this
Html.DropDownListFor(x=>x.ID,
new SelectList(Model.Products,"ID", "Sku", Model.ID), " select ")
For your reference: Use Html.DropDownListFor to get a selected value
The above class, controller and view are correct for presenting the drop-downs. This is why I was perplexed.
However, I had the SelectMember in the Get section. [HttpPost] and SelectMember can see what was selected.
I am building a simple ASP.NET MVC 3 site, using Entity Framework to build the model based on some tables in an already existing Oracle database (in other words, I used the 'database first' methodology to have my model built). I now have a simple blog type site, which I am quite familiar with as I have been learning MVC in a number of languages.
I want to change some of the auto-generated views. One piece I would like to change in particular is that I have a field in one of my tables called 'Visible'. This is simply a numeric value (0 or 1) indicating whether or not a display application should use the row as display data. Currently, I have the simple text field that is auto-generated:
<div class="editor-field">
#Html.EditorFor(model => model.VISIBLE)
#Html.ValidationMessageFor(model => model.VISIBLE)
</div>
What I would like to do is replace this with a drop down box with string values like True and False. The application should display any entry with a 0 as false and vice versa. If a user wants to flip the toggle, the drop down should allow them to do that and understand to make the numeric update when clicking submit. How can this be done?
I have seen countless examples where the drop-down was going to be filled with more then just two values, and in those cases I understand that you can add logic to your controller that pulls all the distinct values, puts them in a list, then adds the list to the ViewBag. However, in my case with only two possible numeric values, it seems like there should be a simpler, more accepted way to do it.
Any help is greatly appreciated.
UPDATE
Following Quinton's answer, I am trying to place said code in my model. Here is my current model:
namespace CurrentActivityBlog
{
using System;
using System.Collections.Generic;
using System.Web.UI.WebControls;
public partial class TBLCURRENTACTIVITY
{
public string TITLE { get; set; }
public string DESCRIPTION { get; set; }
public System.DateTime DATETIME { get; set; }
public short VISIBLE { get; set; }
public decimal ID { get; set; }
public IEnumerable<SelectedListItem> PossibleValues { get; set; }
public TBLCURRENTACTIVITY() {
PossibleValues = new[] { new SelectListItem { Value = "0", Text = "Hidden" },
new SelectListItem { Value = "1", Text = "Visible" } };
}
}
}
I am unable to build this solution, but Visual Studio 2010 is telling me that
"the type or namespace name 'SelectedListItem' could not be found (are you missing a using directive or an assembly reference?)"
As you can see, I have
using System.Web.UI.controls
and have added the reference to System.Web. Is there anything I am forgetting, or anything I should know about (such as models generated using EF behaving differently then one might expect, etc.)?
Thanks again.
The one line solution to it would be:
#Html.DropDownList(Model.VISIBLE.ToString(), new [] {new SelectListItem { Value = "0", Text = "Hidden"}, new SelectListItem { Value = "1", Text = "Visible"}})
but you probably don't want domain logic in your view. So add the possible items to your Model (or from the controller):
public class MyModel {
public int VISIBLE { get; set; }
public IEnumerable<SelectListItem> PossibleValues { get; set; }
public MyModel() {
PossibleValues = new[] { new SelectListItem { Value = "0", Text = "Hidden" }, new SelectListItem { Value = "1", Text = "Visible" } };
}
}
and then your razor code:
#Html.DropDownList(Model.VISIBLE.ToString(), Model.PossibleValues)
Obviously "Hidden" and "Visible" descriptions can be replaced with "False" and "True" or whatever.
You could also create and Editor and Display Template for that specific field. Checkout ScottGu's blog post here, search for "UI Helper Templating Support" and you'll see how to create a editor template and how to render a specific template by name.
EDIT:
If your model is not part of your MVC project, then referencing any classes that are in the MVC assemblies would require a explicit add reference. You can avoid this though by initializing any MVC assembly types in your model from your controller, like such:
public class MyModel {
public int VISIBLE { get; set; }
public IEnumerable<SelectListItem> PossibleValues { get; set; }
}
controller action method:
public ActionResult Edit(int Id) {
...
myModelInstance. PossibleValues = new[] { new SelectListItem { Value = "0", Text = "Hidden" }, new SelectListItem { Value = "1", Text = "Visible" } };
...
Return View(myModel);
}
I am teaching myself asp .net mvc3. I have researched a lot but the more I read the more confused I become. I want to create a page where users can register their property for sale or rent.
I have created a database which looks like this:
public class Property
{
public int PropertyId { get; set; }
public int PropertyType { get; set; }
ยทยทยท
public int Furnished { get; set; }
...
}
Now, I want dropdownlistfor = PropertyType and Furnished.
Property type would be
1 Flat
2 House
3 Detached House
...
Furnished would be:
1 Furnished
2 UnFurnished
3 PartFurnished
...
Now, I am really not sure where to keep this information in my code. Should I have 2 tables in my database which store this lookup? Or should I have 1 table which has all lookups? Or should I just keep this information in the model?
How will the model bind to PropertyType and Furnished in the Property entity?
Thanks!
By storing property types and furnished types in the database, you could enforce data integrity with a foreign key, rather than just storing an integer id, so I would definitely recommend this.
It also means it is future proofed for if you want to add new types. I know the values don't change often/will never change but if you wanted to add bungalow/maisonette in the future you don't have to rebuild and deploy your project, you can simply add a new row in the database.
In terms of how this would work, I'd recommend using a ViewModel that gets passed to the view, rather than passing the database model directly. That way you separate your database model from the view, and the view only sees what it needs to. It also means your drop down lists etc are strongly typed and are directly in your view model rather than just thrown into the ViewBag. Your view model could look like:
public class PropertyViewModel
{
public int PropertyId { get; set; }
public int PropertyType { get; set; }
public IEnumerable<SelectListItem> PropertyTypes { get; set; }
public int Furnished { get; set; }
public IEnumerable<SelectListItem> FurnishedTypes { get; set; }
}
So then your controller action would look like:
public class PropertiesController : Controller
{
[HttpGet]
public ViewResult Edit(int id)
{
Property property = db.Properties.Single(p => p.Id == id);
PropertyViewModel viewModel = new PropertyViewModel
{
PropertyId = property.Id,
PropertyType = property.PropertyType,
PropertyTypes = from p in db.PropertyTypes
orderby p.TypeName
select new SelectListItem
{
Text = p.TypeName,
Value = g.PropertyTypeId.ToString()
}
Furnished = property.Furnished,
FurnishedTypes = from p in db.FurnishedTypes
orderby p.TypeName
select new SelectListItem
{
Text = p.TypeName,
Value = g.FurnishedTypeId.ToString()
}
};
return View();
}
[HttpGet]
public ViewResult Edit(int id, PropertyViewModel propertyViewModel)
{
if(ModelState.IsValid)
{
// TODO: Store stuff in the database here
}
// TODO: Repopulate the view model drop lists here e.g.:
propertyViewModel.FurnishedTypes = from p in db.FurnishedTypes
orderby p.TypeName
select new SelectListItem
{
Text = p.TypeName,
Value = g.FurnishedTypeId.ToString()
};
return View(propertyViewModel);
}
}
And your view would have things like:
#Html.LabelFor(m => m.PropertyType)
#Html.DropDownListFor(m => m.PropertyType, Model.PropertyTypes)
I usually handle this sort of situation by using an enumeration in code:
public enum PropertyType {
Flat = 1,
House = 2,
Detached House = 3
}
Then in your view:
<select>
#foreach(var val in Enum.GetNames(typeof(PropertyType)){
<option>val</option>
}
</select>
You can set the id of the option equal to the value of each item in the enum, and pass it to the controller.
EDIT: To directly answer your questions:
You can store them as lookups in the db, but for small unlikely to change things, I usually just use an enum, and save a round trip.
Also look at this approach, as it looks better than mine:
Converting HTML.EditorFor into a drop down (html.dropdownfor?)
I don't know if I'm missing something obvious, but I really want to grab names of clients associated with a composite key.
Controller Code:
Job job = db.Jobs.Find(id);
ViewBag.jobClientsList = new SelectList(job.JobClients.ToList(), "ClientNumber", "ClientNumber");
View Code:
<%: Html.DropDownList("ClientNumber", ViewData["JobClientsList"] as SelectList)%>
Model:
namespace Sample.CustomerService.Domain {
using System.ComponentModel.DataAnnotations;
public class JobClient {
public JobClient() { }
[Key]
[Column(Order = 1)]
public virtual int JobNumber { get; set; }
[Key]
[Column(Order = 1)]
public virtual int ClientNumber { get; set; }
public virtual Client Client { get; set; }
public virtual Job Job { get; set; }
}
}
This code works, but all I get in the dropdownlist is a bunch of numbers. What I would really like is the client names associated with the numbers but I'm really not sure how to do it! I've been looking around for ages!
After re-reading your question your answer seems simpler then expected.
Check out the Select list class http://msdn.microsoft.com/en-us/library/system.web.mvc.selectlist.aspx
The constructor your using in your controller is wrong, it should be:
ViewBag.jobClientsList = new SelectList(job.JobClients.ToList(), "ClientNumber", "Client");
You were setting the text value of the selectList to be "ClientNumber" which is why you had a list of numbers and not names!
By default the select list is showing you the property that is marked [Key]
<%: Html.DropDownList("ClientNumber",
ViewData["JobClientsList"].Client as SelectList)%>
Should print the client name (assuming the primary Key on the Client object is their name, otherwise You'd need something like ViewData["JobClientsList"].Client.FullName
The best solution would be to use a ViewModel instead of using ViewBag or ViewData for this, it'll help avoid a lot of headaches both now and in the future.
What I have done in the past to get DropDownLists working is save the List to the Session Variable, and then create my SelectList in the actual DropDownList.
Controller:
Job job = db.Jobs.Find(id).ToList();
ViewBag.jobClientList = job;
View:
<%: Html.DropDownList("ClientNumber", new SelectList((If you view is strongly typed, put that here) ViewData["JobClientsList"],"ClientNumber","ClientNumber")%>
This may be poorly worded, so I think I can clarify if need be
Anyone looking for a solution, try the following:
In your controller:
1) Get the list:
var allCountries = countryRepository.GetAllCountries();
2) define a variable:
var items = new List<SelectListItem>();
3)loop each item:
foreach(var country in allCountries)
{
items.Add(new SelectListItem() {
Text = coutry.Name,
Value = Country.Id.ToString(),
// Put all sorts of business logic in here
Selected = country.Id == 13 ? true : false
});
}
model.Countries = items;
In your view:
#Html.ActionLink("StringToDisplay", "actionName", "controllerName", new SelectList(Model.Countries, "Value","Text"),"--Please Select--", new{#class="form-control"})
Not to mention Model should have a property with
public IEnumerable<Country> Countries {get; set;}
Ok, this was supposed to be a question asking for help getting my form to work properly. In the process of creating an example to post, I figured out what the fix is.
So now it's a become a question about why it works one way and not the other. I really cannot understand the behaviour. Have I found a bug in MVC? Or is there something I don't understand about html requests which makes this behaviour correct?
The example invloves setting the selected value in a dropdown through an enum property in a view model which is bound from the querystring (it's probaly clearer what I'm talking about if you just read the code):
Controller/Model
public class HomeController : Controller
{
public ActionResult Index(TestModel model)
{
return View(model);
}
}
public class TestModel
{
public SelectList EnumOptions { get; set; }
public TestEnum EnumValue { get; set; }
public TestModel()
{
var options = from Enum e in Enum.GetValues(typeof(TestEnum))
select new { Value = e, Name = e.ToString() };
EnumOptions = new SelectList(options, "Value", "Name", TestEnum.NotSet);
}
}
public enum TestEnum
{
NotSet = 0,
Dog = 1,
Cat = 2
}
View
#Html.DropDownListFor(m => m.EnumValue, Model.EnumOptions)
Dog numeric
Dog string
It's all pretty simple.
The question is, why doesn't the second "Dog" link work properly? Note it submits the enumValue as a numeric property, instead of as a "string" property.
But the model binder has no problem with this. The model supplied to the View is exactly the same in either case. So how does the dropdown selected value get rendered correctly in one case but not the other?
DropDownListFor looks into the modelstatedictionary for getting the current value of the field/property.
The ValueProviderResult for the second link has a value from 1.
The modelbinder knows that the requested type is a TestEnum. A 1 can be converted to Dog.
The dropwdownlist converts the value of the ValueProviderResult into a string. A 1 converts to "1" as a string. There is no entry in the selectlist with a value of "1".
Therefor the dropdownlist has a wrong current value.