1

To give you an example that you all should be familiar with, imagine that you are building a Facebook wall page in Asp.Net MVC. On the wall, there are different kinds of posts (ie status updates, photos, videos, links, whatever. They all display down the wall and they all render differently, depending on what kind of post they are. I am building something similar to this, and it seems to me that the most elegant way to do this is with polymorphism. I foreach through the Post types and call a rendering method that each subtype implements. I did something like this in the code-behind of Web Forms, but I cannot figure out how to do this in MVC without mixing concerns, short of having a giant list of if else blocks.

@foreach(Post post in Model.Posts)
{
    if(post is A)
    {
        <div>Different Content</div>
    }
    else if(post is B)
    {
        <div>Different Content</div>
    }
    else if(post is C)
    {
        <div>Different Content</div>
    }
}

instead of just

@foreach(Post post in Model.Posts)
{
    post.render();
}

How do I get something more maintanable like the second part?

4 Answers 4

6

Give the parent object a property "PartialViewPath", and in each child class, have the PartialViewPath property return a string representing the path to the view for that type of post. Then, in your main view, it's as simple as this:

@foreach (var post in Model.Posts)
{
    Html.RenderPartial(post.PartialViewPath)
}
Sign up to request clarification or add additional context in comments.

3 Comments

Ok. That makes sense. It smears the lines a little, but it's a lot better than storing the actual output in the object. But I read somewhere that rendering a partial in a foreach loop is really inneficient. Is this of concern? I find that I have situations like this quite often
Not really - enumerating a list with a foreach is a very normal way to achieve this behaviour; Perhaps it might be efficient if you're using Html.RenderPartial when your model item is only a string or something very simple. but as you need to encapsulate actual decisions, this approach is fine.
Ok. Thanks. I'm going to use this as a backup answer if I can't get Russ's to work. I'd mark up your answer but apparently I don't have enough reputation to do so. I just joined, so I apologize if I screw up etiquette here
3

You can use a partial view that itself is based on a Model which in an interface that exposes a Render method.

In your ActionResult, you return a concrete instance of this Interface that is the correct type for the content you want to display.

the problem is, of course, you need to generate the content in the Render method using Html.Raw or something like that as the actual html inside the View proper is static.

But then the view itself would just look something like

@Html.Raw(Model.Render()) 

without any supporting HTML, or just the plain HTML that you deem to be consistent over all content types.

Update: So you'd have an interface

public interface ContentView
{
    public string Render();
}

And you would have 2 classes for example that extend this Interface:

public class TextView : ContentView
{
    public string Render()
    {
        return "TextView!";
    }
}

And

public class HtmlView : ContentView
{
    public string Render()
    {
        return "<strong>HtmlView!</strong>";
    }
}

And your Partial view called ContentView

@model ContentView
@Html.Raw(Model.Render());

Then your ActionResult in your controller:

public ActionResult ShowPosts()
{

   List<ContentView> posts = PostRepository.GetPosts();

   return posts;
}

And your main view:

@model List<ContentView>

@foreach(var contentView in Model) {

    @Html.PartialView("ContentView", contentView);

}

I hope this clarifies it a bit; you've obviously need to adapt these concepts to your post; but what it allows you to do is to mask all your posts in a list of the type of the Interface, then the actual conversion on content type doesn't need to be switched or iterated, you will be using polymorphism.

2 Comments

Thanks for responding. I'm having trouble understanding your first two points. Could you reword that? Wouldn't that just display one post and not a collection?
Yeah that helped and I really like this solution in theory. I'll have to see how it works in my project. Thanks a lot
3

You could use templated HTML helpers for this:

  1. Create a DisplayTemplates directory under Views/YourView.
  2. Add a strongly-typed view whose name is <model class name>.cshtml (if you use Razor). This view will be invoked to render your model later.
    For your example you need 3 templates: A.cshtml, B.cshtml, C.cshtml.
  3. To render model with the above-defined view, use Html.DisplayFor() inside a parent view.

Comments

0

I did something similar, it requires the if/else statements, but uses RenderAction to avoid having the HTML embeded for each if-statement.

So I have a model defined:

@model SearchResultViewModel

Which contains a list of IEnumerable<ContentViewModel> then loop through using the if-statement like you have:

    @foreach (var result in Model.Results)
     {
        if (result is VideoViewModel)
        { Html.RenderAction("Item", "Video", new { item = result }); }
        else if (result is PodcastViewModel)
        { Html.RenderAction("Item", "Podcast", new { item = result }); }
        else
        { Html.RenderAction("Item", "Article", new { item = result }); }
    }

Then I can modify the HTML in each of those partial views since they all differ in terms of content being displayed.

In my scenario tho, the content is entirely different.

If you're are all similar you could output different Views in your action and use a single RenderAction call.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.