ASP.Net MVC view model for dropdownlist? - asp.net-mvc

I am trying to work with a generated Month dropdownlist in MVC.
My viewmodel is:
public class MyViewModel{
public MyViewModel()
{
var monthNames = DateTimeFormatInfo.CurrentInfo.MonthNames.Take(12).ToList();
Months = new SelectList(monthNames.Select(m=> new{Id=monthNames.IndexOf(m)+1, Name=m}).ToList(),"Id","Name");
}
public IEnumerable<SelectListItem> Months{ get; set; }
public string Month{ get; set; }
}
My View is:
#Html.DropDownListFor(model=>model.Month, new SelectList(Model.Months))
The problem is that the Months property always returns a null value so the page errors when trying to render the DDL.
Seems pretty simple. What am I missing?

You're missing the part where you actually set the Months property to something other than null.
You should just define a custom getter on the property so it always returns an enumerable:
public IEnumerable<SelectListItem> Months
{
List<string> monthNames = DateTimeFormatInfo.CurrentInfo.MonthNames.Take(12).ToList();
foreach (var month in monthNames)
{
yield return new SelectListItem
{
Value = monthNames.IndexOf(month) + 1,
Text = month
};
}
}

As another possible solution using templates:
// in your model, decorate it to use the template
[UIHint("MonthName")]
public String Month { get; set; }
Then in ~/Views/Shared/EditorTemplates/MonthName.cshtml:
#model String
#Html.DropDown(
String.Empty,
#Model,
new SelectList(
System.Globalization.DateTimeFormatInfo.CurrentInfo.MonthNames
.Where(x => !String.IsNullOrEmpty(x))
.Select((x,y) => new { Text = x, Value = y + 1 }),
"Value",
"Text"
)
)
And finally, in your view:
#Html.EditorFor(x => x.Month)
though this is really only worth while on fixed-lists (like months), and not for things that may be dynamic based on the view being displayed.

Related

dropdownlistfor not displaying correct value

My query is getting all the correct values and the selectlist is populated, but my dropdownlist only shows the first value in the selectlist. how can i get it to show the returned value of Captain?
Viewmodel:
public class EditTeamsView : BaseCommunityView
{
...
public IList<Domain.Team> ExistingTeams { get; set; }
...
public EditTeamsView()
{
ExistingTeams = new List<Domain.Team>();
}
}
public class Team
{
...
public string Captain { get; set; }
public IEnumerable<SelectListItem> MemberSelectList { get; set; }
}
View:
#for (var c = 0; c < Model.ExistingTeams.Count; c++)
{
#Html.DropDownListFor(x => x.ExistingTeams[c].Captain, Model.ExistingTeams[c].MemberSelectList,
new { #class = "form-control "})
}
Each option in the dropdown will need a value property and a display property so you may need to change up your Team class's MemberSelectList object to IEnumerable that contains value and text properties.
To set the default value of a drop down list based on a string value like what you have, new up a SelectList that requires an IEnumerable list of objects like what you have, a value property, a text property, and then the default value object.
See here: https://msdn.microsoft.com/en-us/library/system.web.mvc.selectlist.selectlist(v=vs.118).aspx#M:System.Web.Mvc.SelectList.
#Html.DropDownListFor(x => x.ExistingTeams[c].Captain,
new SelectList(Model.MemberSelectList, "Value", "Text", Model.Captain)
)

Formatting a Drop Down List in ASP.NET MVC

I'm building an app with ASP.NET MVC 4. I'm binding my model to a view. In my view, I need a drop down list. That drop down list needs to show quarters. The quarters should be displayed as "Q1", "Q2", "Q3", and "Q4". My model, only has quarter numbers. They are defined like this:
public List<short> Quarters = new List<short>() { get; set; }
public short? SelectedQuarter = null;
public void Initialize() {
Quarters.Add(1);
Quarters.Add(2);
Quarters.Add(3);
Quarters.Add(4);
}
Somehow, I need to prepend "Q" to each value. However, I'm not sure how to do this in ASP.NET MVC. How does someone do this?
Thanks!
Create a SelectList to be used by DropdownListFor() so that you bind the selected option to property SelectedQuarter, but display the 'friendly' name.
View model
public class MyViewModel
{
[Display(Name = "Quarter")]
[Required]
public short? SelectedQuarter { get; set; } // must be a property, not a field!
IEnumerable<SelectListItem> QuarterList { get; set; }
}
Controller
public ActionResult Edit()
{
MyViewModel model = new MyViewModel();
ConfigureViewModel(model);
return View(model);
}
public ActionResult Edit(MyViewModel model)
{
if(!ModelState.IsValid)
{
ConfigureViewModel(model);
return View(model);
}
// model.SelectedQuarter contains the selected value
}
private void ConfigureViewModel(model)
{
model.SelectedQuarter = new List<SelectListItem>()
{
new SelectListItem() { Value = "1", Text = "Q1" },
new SelectListItem() { Value = "2", Text = "Q2" },
new SelectListItem() { Value = "3", Text = "Q3" },
new SelectListItem() { Value = "4", Text = "Q4" },
}
}
View
#model MyViewModel
#using(Html.BeginForm())
{
#Html.LabelFor(m => m.SelectedQuarter)
#Html.DropDownListFor(m => m.SelectedQuarter, Model.QuarterList, "-Please select-")
#Html.ValidationMessageFor(m => m.SelectedQuarter)
<input type="submit" />
}
Assuming you have this property:
public List<short> Quarters { get; set; }
Then in your view or any other consuming code you can generate a list of strings with something like:
model.Quarters.Select(q => "Q" + q)
or:
model.Quarters.Select(q => string.Format("Q{0}", q))
However, semantically it really feels like this belongs on a view model and not in consuming code. Ideally the view should only ever need to bind directly to properties on the view model, not transform those properties. Something like this:
public IEnumerable<string> QuartersDisplay
{
get { return Quarters.Select(q => string.Format("Q{0}", q)); }
}
Then consuming code can just bind to that property:
model.QuartersDisplay
(If the model is a domain model then I'd recommend introducing a view model between the domain and the view, since this wouldn't belong on a domain model.)
Thinking about this a little more... Do you want one property with both the displays and the backing values for the drop down list? That would likely be a IDictionary<short, string>, I imagine? Something like this:
public IDictionary<short, string> QuartersOptions
{
get { return Quarters.ToDictionary(q => q, q => string.Format("Q{0}", q)); }
}
In which case you'd bind to that property:
model.QuartersOptions
Keep in mind that a drop down list often binds to two things. The property which holds the list of possible values (which is what we've built here) and the property which holds the selected value (which remains your SelectedQuarter property).

Display description instead of code in .net MVC 5

I have a table where I store all the different code/value keywords that I need in my app:
public class Keyword
{
public int id { get; set;}
public string name { get; set; }
public string valuecode { get; set; }
public string valuename { get; set; }
}
Then I use Keyword to store records like these
name valuecode valuename
.DealState 1 Draft
.DealState 2 Final
.DealState 3 Cancelled
.DealType NEW New Business
.DealType RNW Renewal
.DealType WFA Waiting for approval
Then in other models I have fields that are filled using these keywords. For example,
public class Deal
{
....
public string state { get; set; }
public string type { get; set; }
....
}
I have managed to have the fields filled with "valuecode" while displaying "valuename" in Create and Edit views (I use DropDownList with a SelectList built in the controller), but I cannot find a way to display valuename instead of valuecode in Index and Details views.
I'm trying to pass the same SelectList in the ViewBag for Index, but then I do not know which syntax to use in order to replace the "state" code with the state "description" for each record returned.
Any hint?
PS: I'm quite new to .net and mvc, usually work with RoR and ActiveRecord...
EDIT
In my KeywordController I have a method
public SelectList selectKeywordValues(string kwname, object selectedKeyword = null)
{
var keywordsQuery = from d in db.Keywords
where d.name == kwname
orderby d.valuename
select d;
SelectList kwlist = new SelectList(keywordsQuery, "valuecode", "valuename", selectedKeyword);
return kwlist;
}
Then in my DealController i have the index method
public ActionResult Index()
{
var kw = new KeywordController();
ViewBag.state = kw.selectKeywordValues(".DealState");
return View(db.Deals.ToList());
}
SOLVED
In DealController the index method is the following
public ActionResult Index()
{
var kw = new KeywordController();
SelectList states = kw.selectKeywordValues(".DealState");
SelectList types = kw.selectKeywordValues(".DealType");
foreach (var item in db.Deals.ToList())
{
SelectListItem mystate = states.Where(row => row.Value == item.state).ElementAt(0);
SelectListItem mytype = types.Where(row => row.Value == item.type).ElementAt(0);
item.state = mystate.Text;
item.type = mytype.Text;
}
return View(db.Deals.ToList());
}
Now the db.Deals.ToList() is filled with descriptions and not with codes.
You can define a view model called DealViewModel that contains DealState and DealType properties. Then populate the DealViewModel with joins in LINQ before passing to the views that reference the view model.
Another approach is to use enums in EF5.

How do I add a Custom Query for a drop down and retain the View Model Pattern?

I've read many articles which they state that querying should not be placed in the Controller, but I can't seem to see where else I would place it.
My Current Code:
public class AddUserViewModel
{
public UserRoleType UserRoleType { get; set; }
public IEnumerable<SelectListItem> UserRoleTypes { get; set; }
}
public ActionResult AddUser()
{
AddUserViewModel model = new AddUserViewModel()
{
UserRoleTypes = db.UserRoleTypes.Select(userRoleType => new SelectListItem
{
Value = SqlFunctions.StringConvert((double)userRoleType.UserRoleTypeID).Trim(),
Text = userRoleType.UserRoleTypeName
})
};
return View(model);
}
The View:
<li>#Html.Label("User Role")#Html.DropDownListFor(x => Model.UserRoleType.UserRoleTypeID, Model.UserRoleTypes)</li>
How do I retain the View Model and Query and exclude the User Type that should not show up?
I think that you are doing it just fine.
Any way... all you can do to remove the querying logic from controller is having a ServiceLayer where you do the query and return the result.
The MVC pattern here is used correctly... what your are lacking is the other 2 layers (BusinessLayer and DataAccessLayer)... since ASP.NET MVC is the UI Layer.
UPDATE, due to comment:
Using var userroletypes = db.UserRoleTypes.Where(u=> u.UserRoleType != 1);
is OK, it will return a list of UserRoleType that satisfy the query.
Then, just create a new SelectList object using the userroletypes collection... and asign it to the corresponding viewmodel property. Then pass that ViewModel to the View.
BTW, I never used the db.XXXX.Select() method before, not really sure what it does... I always use Where clause.
SECOND UPDATE:
A DropDownList is loaded from a SelectList that is a collection of SelectItems.
So you need to convert the collection resulting of your query to a SelectList object.
var userroletypes = new SelectList(db.UserRoleTypes.Where(u=> u.UserRoleType != 1), "idRoleType", "Name");
then you create your ViewModel
var addUserVM = new AddUserViewModel();
addUserVM.UserRoleTypes = userroletypes;
and pass addUserVM to your view:
return View(addUserVM );
Note: I'm assuming your ViewModel has a property of type SelectList... but yours is public IEnumerable<SelectListItem> UserRoleTypes { get; set; } so you could change it or adapt my answer.
I don't see anything wrong with your code other than this db instance that I suppose is some concrete EF context that you have hardcoded in the controller making it impossible to unit test in isolation. Your controller action does exactly what a common GET controller action does:
query the DAL to fetch a domain model
map the domain model to a view model
pass the view model to the view
A further improvement would be to get rid of the UserRoleType domain model type from your view model making it a real view model:
public class AddUserViewModel
{
[DisplayName("User Role")]
public string UserRoleTypeId { get; set; }
public IEnumerable<SelectListItem> UserRoleTypes { get; set; }
}
and then:
public ActionResult AddUser()
{
var model = new AddUserViewModel()
{
UserRoleTypes = db.UserRoleTypes.Select(userRoleType => new SelectListItem
{
Value = SqlFunctions.StringConvert((double)userRoleType.UserRoleTypeID).Trim(),
Text = userRoleType.UserRoleTypeName
})
};
return View(model);
}
and in the view:
#model AddUserViewModel
<li>
#Html.LabelFor(x => x.UserRoleTypeId)
#Html.DropDownListFor(x => x.UserRoleTypeId, Model.UserRoleTypes)
</li>

DropDownListFor selection not working -- again

Yeah, I know, this question's been asked/answered 34798796873.5 times. I looked through all 3 bajillion of them, and I still have the problem. What am I missing here?
I tried several approaches and none of them work. Here are my latest attempts:
<%:Html.DropDownList("Author",
Model.AuthorItems.Select(i =>
new SelectListItem
{
Text = i.Name,
Value = i.Id.ToString(),
Selected = i.Id == Model.Author.Id
}), "無し")%>
<%:Html.DropDownListFor(m => m.Author,
new SelectList(Model.AuthorItems,
"Id",
"Name",
Model.Author),
"無し") %>
My view model is very straightforward:
public class EditArticleViewModel
{
public AuthorItem Author { get; set; }
public IList<AuthorItem> AuthorItems { get; set; }
public class AuthorItem
{
public int Id { get; set; }
public string Name { get; set; }
}
}
I made sure my action is working correctly; sure enough, Author has an Id of 5, and AuthorItems has an entry whose Id is 5.
I even tried overriding Equals and GetHashCode in the model.
Blahhhhh!!1
In your view model replace:
public AuthorItem Author { get; set; }
with
public int? SelectedAuthorId { get; set; }
and in your view bind the dropdown list to this SelectedAuthorId:
<%:Html.DropDownListFor(
m => m.SelectedAuthorId,
new SelectList(Model.AuthorItems, "Id", "Name"),
"無し"
) %>
Now as long as you provide a valid SelectedAuthorId value in your controller action:
model.SelectedAuthorId = 123;
The HTML helper that renders the dropdown will correctly preselect the item from the list that has this given id.
The reason for this is that in a dropdown list you can select only a single value (a scalar type) and not an entire Author (all that is sent in the HTTP request when you submit the form is this selected value).
DropDownList/DropDownListFor used ModelState value. So set ViewDataDictionary selectedValue in controller.
public ActionResult Index()
{
var model = new EditArticleViewModel
{
Author = new EditArticleViewModel.AuthorItem() {Id = 3, Name = "CCC"},
AuthorItems = new List<EditArticleViewModel.AuthorItem>()
{
new EditArticleViewModel.AuthorItem() {Id = 1, Name = "AAA"},
new EditArticleViewModel.AuthorItem() {Id = 2, Name = "BBB"},
new EditArticleViewModel.AuthorItem() {Id = 3, Name = "CCC"},
new EditArticleViewModel.AuthorItem() {Id = 4, Name = "DDD"},
}
};
ViewData["Author"] = model.Author.Id;
return View(model);
}
View code simple.
<%:Html.DropDownList("Author",
new SelectList(Model.AuthorItems,
"Id",
"Name"), "無し")%>
<%:Html.DropDownListFor(m => m.Author,
new SelectList(Model.AuthorItems,
"Id",
"Name"),
"無し")%>
ViewData key "Author" is using model state binding for selected value.
Hope this help.

Resources