5

I have the following model:

public class Filter
{
    public string Field { get; set; }

    public string Operator { get; set; }

    public string Value { get; set; }
}

And the following controller:

public class FilterController
{
    public ActionResult Index()
    {
        IList<Filter> model = new List<Filter>() {
            new Filter(), 
            new Filter()
        };

        return View(model);
    }
}

And the following view:

@model IEnumerable<Filter>
@Html.EditorForModel()

This should look for my EditorTemplate Filter.cshtml, and render the template for each element in the list, right?

Using Glimpse, I notice that MVC is looking for IEnumerable`1.cshtml instead of Filter.cshtml

The same thing happens when I use

@Html.EditorFor(model => model)

When I do this:

@Html.EditorFor(model => model, "Filter")

I get an error saying that Filter.cshtml is expecting a model of type Filter but it received a model of type IEnumerable<Filter>

Am I doing this correctly? Do I need to do anything else to get the list of models to render correctly, using the correct editor template?

Kyle Trauberman
  • 25,414
  • 13
  • 85
  • 121
  • Please post the markup for Filter.cshtml – Maess Apr 19 '12 at 18:25
  • @Maess The problem is that MVC isn't finding Filter.cshtml at all, so how is that relevant? – Kyle Trauberman Apr 19 '12 at 18:26
  • You've shown enough of the view. I can't find a good answer, but can't you wrap the collection in a class and just use Model.MyCollection in the view? – David Fox Apr 19 '12 at 18:35
  • @David that worked. If you move your comment to an answer, I'll accept it. Thanks! – Kyle Trauberman Apr 19 '12 at 18:41
  • 2
    I'd love to know why this is the case. The Display/EditorTemplates don't seem to be all that consistent. – John H Apr 19 '12 at 18:46
  • 1
    @JohnH - Sometimes MVC's conventions do confusing things. This could be because the controler is called FilterController, or it could be because it conflicts with another class named Filter (which shouldn't happen because of namespaces, but one never knows). What would happen if you changed the name to FilterClass instead of Filter? – Erik Funkenbusch Apr 19 '12 at 20:34
  • @MystereMan Filter is a changed name that I used for this post. The actual class name shouldn't conflict with anything. – Kyle Trauberman Apr 19 '12 at 20:45
  • @MystereMan Yeah unfortunately they do. I've run into the class conflict before, which actually I think is completely fine albeit a little misleading at the time, but generally, I just don't like the way the templates seemingly behave very differently under similar circumstances. [Here's another example](http://stackoverflow.com/questions/8678802/) that I came across recently. – John H Apr 19 '12 at 22:28

2 Answers2

3

I've definitely had issues in the past with EditorTemplates, but I think it was mostly user error.

One possible workaround is to encapsulate your collection in a single, view model class and pass that to the view

public class MySweetFilterViewModel
{
    public IEnumerable<Filter> Filters { get; set; }
}

Then you could use a single view to pick apart the collection

@model Project.Models.MySweetFilterViewModel
@Html.EditorFor(x => x.Filters)

Just make sure your controller encapsulates

public ActionResult Index()
{
    //...
    return View(new MySweetFilterViewModel() { Filters = model });
}
Kyle Trauberman
  • 25,414
  • 13
  • 85
  • 121
David Fox
  • 10,603
  • 9
  • 50
  • 80
1

To answer your question regarding why... let's experiment with a few things.

What happens if you wrote your code this way:

return View(new List<Filter>{ new Filter(), new Filter() });

It could be that since you are using an intermediate IList rather than a List, that something is getting confused. What would be happening (in my theory) is that passing an IList<Filter> is causing a standard IEnumerable rather than an IEnumerable<Filter> to be passed to the view.

You could also try model.AsEnumerable<Filter>()

Erik Funkenbusch
  • 92,674
  • 28
  • 195
  • 291