views:

293

answers:

3

All my controllers in my project inherit from a base controller, which has a property with my Entity Model.

Let say I have a view that shows cities in the world, and it has an option to filter by country. The country filter is a dropdown list of countries from the database. The Html helper for the dropdown list requests a IEnumerable<SelectItem>.

Now with that info, is it ok if I create a HtmlHelper that looks like this:

    public static IEnumerable<SelectListItem> GetCountries(HtmlHelper htmlHelper)
    {
        return (from c in ((BaseController) htmlHelper.ViewContext.Controller).Entities.Countries
                orderby c.Name
                select new SelectListItem() {Text = c.Name, Value = c.ID});


    }

The question is not if I it is possible, but if it is ok according to the MVC way of doing things. (Or should I put the collection of countries in the ViewData inside the controller?)

+3  A: 

I would pass the data as a parameter to the GetCountries method. The htmlHelper function really shouldn't know about the properties of your base controller - what if someone were to ever use it on a controller that doesn't inherit from your base? I know I know, you control the code, blah blah. If you're really interested in best practices, avoid the dependency.

public static IEnumerable<SelectListItem> GetCountries(this HtmlHelper html, Countries countries) {
    return from c in countries
           order by c.Name
           select new SelectListItem 
           {
               Text = c.Name,
               Value = c.ID
           };
}

then, in your View:

<%=Html.GetCountries(this.Entities.Countries)%>
Chris
But how do I get the Countries inside my ViewPage?The main item in my ViewPage is an IEnumerable<City>. So should I put the Countries collection inside the ViewData (ViewData["countries"]).You code suggests that you create a new ViewPage that inherits from the original ViewPage and which adds the Entities property (also seems like a lot of dependency).
Gidon
You can create a viewmodel that holds both cities and countries.
Morph
I feel that sometimes a ViewModel is good, but using it for every page (because many pages consists out of main data, with some extra data) just creates so many ViewModels that I wonder if it is still advantageous.
Gidon
There's no real downside to having a large number of ViewModel classes (if you need them). If you REALLY don't want that many ViewModels, then you can just put the Countries collection directly into ViewData. You may also be able to cut down on the total number of classes through inheritance.
AaronSieb
+1  A: 

Check out the ViewModel pattern, it's mentioned in the NerdDinner tutorial: http://nerddinnerbook.s3.amazonaws.com/Part6.htm

Basically you create a ViewModel class that encapsulates all the data you might need for your view. So you'd have a class that contains a list of all cities and/or countries, and whatever else, instantiated/populated in the controller action.

Then, you can strongly type your view to use that ViewModel class, and blammo: you've got all the data you need for your form.

mgroves
Yep I know the ViewModel pattern, and am also using it for some cases. But also find in some cases it is an overkill. I am going to use this list of countries in lots of places, which will mean I have to create a ton of ViewModels...
Gidon
Might be worth making a base class that contains countries, and just inheriting from that when necessary. But ultimately, yeah, if you have a lot of views, you'll need a lot of viewmodels. Also, I've not done this, but you could try passing anonymous types to your views, and then you can create ViewModels on the fly.
mgroves
A: 

I think html helper should return html. So you have two approaches:

first, if you want to do this as you started, from your htmlhelper prepare your list of elements and then call html.RenderDropDown with initialized list, selected element etc...

second, you can all prepare in your model, so controller will pass initiated object with all elements you need so in you view you can call renderdropdown directly

cheers

Marko