Passing DateTime[] to the view to deploy as a dropdown in the view - asp.net-mvc-3

I had a look at this:
ASP.NET MVC - How to pass an Array to the view?
and am struggeling with deploying my DateTime[] as a dropdownlist in the view, herewith my line but it is not correct:
<p style="color:Red"><%: Html.DropDownList("Choose a Sunday: ", (IEnumerable<SelectListItem>)ViewData["Sundays"], "--Select--")%>
</p>
In the controller i have:
[HttpGet]
public ActionResult MyAction()
{
DateTime[] allSundaysInMonth = GetDatesOfSundays(System.DateTime.Now);
ViewData["Sundays"] = allSundaysInMonth;
return View();
}
can someone help please?
thanks

Perhaps what you want to do is this:
[HttpGet]
public ActionResult MyAction()
{
IEnumerable<SelectListItem> allSundaysInMonth = GetDatesOfSundays(System.DateTime.Now).Select(day => new SelectListItem() { Text = day.ToString(), Value = day.ToString() });
ViewData["Sundays"] = allSundaysInMonth;
return View();
}
That is depending on your display format. What I'm trying to point out is that DateTime and SelectListItem may not have an implicit cast, so you get a type mismatch at runtime.

If you ever think (realize) dynamic magic variables like ViewData/ ViewBag is an evil and thinking about using strongly typed ViewModel for your view, you can do like this
create a ViewModel specific for your View (UI).
public class EventViewModel
{
public IEnumerable<SelectListItem> AllSundays { set;get;}
public string SelectedSunday { set;get;}
}
In your action method, set the value and send it to the view
public ActionResult MyAction()
{
var vm=new EventViewModel();
vm.AllSundays = GetDatesOfSundays(System.DateTime.Now).
Select(d => new SelectListItem() {
Text = d.ToString(), Value = d.ToString() }).ToList();
return View(vm);
}
Change your view like this
#model EventViewModel
#Html.DropDownListFor(x => x.SelectedSunday, Model.AllSundays, "--Select One--")
ViewBag/ViewData is bad. It makes your code ugly. Stick to strongly typed.

You cast the list to IEnumerable<SelectListItem>, but its a list of DateTimes. Casting the list to IEnumerable<DateTime> should fix it.
Or you might want to put list of SelectListItem on the ViewData instead!

Related

MVC3 Persisting data from controller to view back to controller

I have a model containing a couple of lists:
[Display(Name = "Facilities")]
public List<facility> Facilities { get; set; }
[Display(Name = "Accreditations")]
public List<accreditation> Accreditations { get; set; }
I populate these lists initially from my controller:
public ActionResult Register()
{
var viewModel = new RegisterModel();
viewModel.Facilities = m_DBModel.facilities.ToList();
viewModel.Accreditations = m_DBModel.accreditations.ToList();
return View(viewModel);
}
When they get to my view they are populated with the DB records (great). I then pass the model to the partial view which displays these lists as checkboxes, ready for user manipulation (I have tried based on another suggestion using for loop instead of foreach loop, made no difference):
#model LanguageSchoolsUK.Models.RegisterModel
#foreach (var item in Model.Facilities)
{
#Html.Label(item.name);
#Html.CheckBox(item.name, false, new { id = item.facility_id, #class = "RightSpacing", #description = item.description })
}
When I submit the form and it ends up back at my controller this time calling the overloaded register function on the controller:
[HttpPost]
public ActionResult Register(RegisterModel model)
{
if (ModelState.IsValid)
{
// Do stuff
}
return View(model);
}
The problem is that the model parameter containing the lists (Facilities and Accreditations) is telling me that the lists are null.
Please can somebody tell me what I am doing wrong, why aren't they populated with the collections that I originally passed through and hopefully a way of asking whick ones have been checked?
Thanks.
I have tried based on another suggestion using for loop instead of
foreach loop, made no difference
Try again, I am sure you will have more luck this time. Oh and use strongly typed helpers:
#model LanguageSchoolsUK.Models.RegisterModel
#for (var i = 0; i < Model.Facilities.Count; i++)
{
#Html.HiddenFor(x => x.Facilities[i].name)
#Html.LabelFor(x => x.Facilities[i].IsChecked, Model.Facilities[i].name);
#Html.CheckBoxFor(
x => x.Facilities[i].IsChecked,
new {
id = item.facility_id,
#class = "RightSpacing",
description = item.description // <!-- HUH, description attribute????
}
)
}
Also you will undoubtedly notice from my answer that checkboxes work with boolean fields on your model, not integers, not decimals, not strings => BOOLEANS.
So make sure that you have a boolean field on your model which will hold the state of the checkbox. In my example this field is called IsChecked but obviously you could feel absolutely free to find it a better name.

MVC 3 Html.DropDownList error

I have that error
The ViewData item that has the key 'BookAttributesDDL' is of type 'System.String' but must be of type 'IEnumerable<SelectListItem>'.
in that code:
#Html.DropDownList("BookAttributesDDL", Model.BookAttributes_Items)
but the Model.BookAttributes_Items is type of the IEnumerable<SelectListItem> ! What's wrong ?
The ViewData.Keys property from the Immediate Window:
ViewData.Keys
Count = 2
[0]: "FCoookie"
[1]: "Title"
Try to avoid dynamic variables like ViewBag and ViewData. It will make your code unreadable and painful to maintain in future as it grows. ViewBag is like Magic strings !
Switch to the ViewModel approach.
Example, If you are creating a View to Create a Book, Create a Viewmodel (it is just a plain Class) for that like this
public class BookViewModel
{
public int BookId { set;get;}
public string BookName {set;get;}
public IEnumerable<SelectListItem> Attributes{ get; set; }
public int SelectedAttribute{ get; set; }
}
Now in your GET Action, Simply create an object of this class, Set the BookAttribbutes proeprties to your Dropdown items and pass this ViewModel object to the View
public ActionResult Create()
{
BookViewModel vm=new BookViewModel();
//The below code is hardcoded for demo. you mat replace with DB data.
vm.Attributes=new[]
{
new SelectListItem { Value = "1", Text = "F Cookie" },
new SelectListItem { Value = "2", Text = "Title" },
}
return View(vm);
}
Now in We will make our view strongly typed to this ViewModel class
#model BookViewModel
#using(Html.BeginForm())
{
#Html.TextBoxFor(x=>x.BookName)
#Html.DropDownListFor(x => x.SelectedAttribute,
new SelectList(Model.Attributes, "Value", "Text"), "Select Attribute")
<input type="submit" value="Save" />
}
Now you will get the Selected Dropdown value and the textbox value in your HttpPost action by accessing the corresponding properties of your ViewModel
[HttpPost]
public ActionResult Create(BookViewModel model)
{
if(ModelState.IsValid)
{
//check for model.BookName / model.SelectedAttribute
}
//validation failed.TO DO : reload dropdown here
return View(model);
}
Fully agree with View Model approach response to you (and its me who selected it as useful).
However, if you don't want to switch, but remain as is I bet your answer lays in this article.
Hope this help you.

How to create a DropDownList from a LINQ query in MVC3?

I need to know how I could create a drop down list to represent all the categories in my "Categories" table.
I have already extracted the names and the values of each category I need, using this LINQ query :
var dbcontext = new LNQ2SQLDataContext();
var Q = from P in dbcontext.Categories
where P.SUB_CAT == null
select P;
I can pass this "Q" to my view like this :
In Controller :
return View(Q);
And in the View :
#model IEnumerable<MyAppName.Models.Category>
But I have no idea how to use #html.DropDownListFor() to make a darn good drop down list out of the model. :|
PLUS:
I could make a SelectList from the query "Q" like this :
var category_list = new SelectList(Q, "CAT_ID", "CAT_Name");
BUT I don't know how to create a drop down list (without using ViewBag to pass the category_list to the view) from a simple SelectList, either :|
I searched through as many blogs and websites as I could. But they didn't have the solution for my problem. I only got more and more confused!
So can anybody help please ? :/
To use DropDownListFor you'll either have to have a model that has a SelectList or data to make a selectlist out of and a property to store the selected value of the dropdown OR use the ViewBag to pass the category_list. So you can go with...
Public Class MyViewModel
{
Public Integer SelectedCategory { get; set; }
Public SelectList Categories { get; set; }
}
Public Class ItemsController : Controller
{
Public ActionResult Index()
{
var dbcontext = new LNQ2SQLDataContext();
var Q = from P in dbcontext.Categories
where P.SUB_CAT == null
select P;
var vm = new MyViewModel();
vm.Categories = new SelectList(Q, "CategoryID", "Name");
return View(vm);
}
[HttpPost()]
Public ActionResult Index(MyViewModel vm)
{
var theSelectedCategory = vm.SelectedCategory;
}
}
The view would be...
#model MyViewModel
#Html.DropDownListFor(model => model.SelectedCategory, Model.Categories, "Select Category")
Note: I don't typically code in C# so I can't guarantee the syntax is exactly right.

ASP.NET MVC 3 Viewmodel Pattern

I am trying to work out the best way of using a viewmodel in the case of creating a new object.
I have a very simple view model that contains a contact object and a select list of companies.
private ICompanyService _Service;
public SelectList ContactCompanyList { get; private set; }
public Contact contact { get; private set; }
public ContactCompanyViewModel(Contact _Contact)
{
_Service = new CompanyService();
contact = _Contact;
ContactCompanyList = GetCompanyList();
}
private SelectList GetCompanyList()
{
IEnumerable<Company> _CompanyList = _Service.GetAll();
return new SelectList(_CompanyList, "id", "name");
}
I then have contact controller that uses this viewmodel and enable me to select a related company for my contact.
[Authorize]
public ActionResult Create()
{
return View(new ContactCompanyViewModel(new Contact()));
}
My issue is with the create method on the controller.
[Authorize]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(Contact _Contact)
{
try
{
_Service.Save(_Contact);
return RedirectToAction("Index");
}
catch
{
return View();
}
}
The problem is that the view returns an empty contact object, but! the company id is populated, this is because the dropdown list explicitly declares its field name.
#Html.DropDownList("parent_company_id",Model.ContactCompanyList)
The standard html form fields pass the objects values back in the format of contact.forename when using the HTML.EditorFor helper...
#Html.EditorFor(model => model.contact.forename)
I can access them if I use a FormCollection as my create action method paremeter and then explicitly search for contact.value but I cannot use a Contact object as a parameter to keep my code nice and clean and not have to build a new contact object each time.
I tried passing the actual view model object back as a parameter but that simply blows up with a constructor error (Which is confusing seeing as the view is bound to the view model not the contact object).
Is there a way that I can define the name of the Html.EditFor field so that the value maps correctly back to the contact object when passed back to the create action method on my controller? Or Have I made some FUBAR mistake somewhere (that is the most likely explanation seeing as this is a learning exercise!).
Your view model seems wrong. View models should not reference any services. View models should not reference any domain models. View models should have parameterless constructors so that they could be used as POST action parameters.
So here's a more realistic view model for your scenario:
public class ContactCompanyViewModel
{
public string SelectedCompanyId { get; set; }
public IEnumerable<SelectListItem> CompanyList { get; set; }
... other properties that the view requires
}
and then you could have a GET action that will prepare and populate this view model:
public ActionResult Create()
{
var model = new ContactCompanyViewModel();
model.CompanyList = _Service.GetAll().ToList().Select(x => new SelectListItem
{
Value = x.id.ToString(),
Text = x.name
});
return View(model);
}
and a POST action:
[HttpPost]
public ActionResult Create(ContactCompanyViewModel model)
{
try
{
// TODO: to avoid this manual mapping you could use a mapper tool
// such as AutoMapper
var contact = new Contact
{
... map the contact domain model properties from the view model
};
_Service.Save(contact);
return RedirectToAction("Index");
}
catch
{
model.CompanyList = _Service.GetAll().ToList().Select(x => new SelectListItem
{
Value = x.id.ToString(),
Text = x.name
});
return View(model);
}
}
and now in your view you work with your view model:
#model ContactCompanyViewModel
#using (Html.BeginForm())
{
#Html.DropDownListFor(x => x.SelectedCompanyId, Model.CompanyList)
... other input fields for other properties
<button type="submit">Create</button>
}

MVC3 Pass additional data from ctrl to view

If I were to pass additional data (other than the model) into my view, like say a list of files from a specific folder, whats the best way to do that?
I was thinking something like making a method and return a list into ViewData:
public List<string> GetFiles(int id, string cat)
{
var files = new List<string>();
var folder = "~/App_Data/uploads/" + cat + "/" + id.ToString();
foreach (var file in Directory.GetFiles(folder))
{
files.Add(file);
}
return files;
}
The controller:
ViewData["files"] = db.GetFiles(id, "random");
The view:
#foreach (var item in ViewData["files"]){ //markup }
First, ive heard that viewdata and viewbag shouldnt be used. Second, this code doesnt work. Something with Viewdata object is not enumerable. Should I make a model class for these files. If so, how? 2 models in one view? Im a bit confused how to do this proberly.
If I were to pass additional data (other than the model) into my view,
like say a list of files from a specific folder, whats the best way to
do that?
Write a view model which will contain all the properties your view needs and have your controller action pass this view model to the view. So:
public class MyViewModel
{
public List<string> Files { get; set; }
public string Foo { get; set; }
... some other properties that your view might need
}
then the controller:
public ActionResult Index(int id)
{
var model = new MyViewModel
{
Files = db.GetFiles(id, "random"),
Foo = "bar"
};
return View(model);
}
and finally in your strongly typed view:
#model MyViewModel
#foreach (var file in Model.Files)
{
<div>#file</div>
}
<div>#Html.DisplayFor(x => x.Foo)</div>

Resources