7

Lets say I am writing a blog web application using MVC pattern. Typical layout for the main page of the blog application is - some kind of a post index in the main part, and aside there are some addition parts, like timeline, tag navigation panel, subscribing panel, etc. Those controls also appear on a single post view, and may appear on other views I have.

My question is - how should I handle those panels aside in my views and controllers. I see three approaches here:

  1. Create a big viewmodel class that contains all the information required for rendering the view s (either index or single post). That case I could make these aside panels a partial views, and calling them from the view being rendered passing part of that big viewmodel. The drawback is - I will have the code filling viewmodel spread amongst various controller method, meaning code is duplicated. Which is quite bad.

  2. Create another layer of view rendering. Say, the topmost layer of rendering recives a alreay rendered parts of html, or functions which when called outputs an html. The layer under this "combining partials layer" would give just partial views for each panel I want, include main content. The drawback here - memory usage. Most modern frameworks renders htmls directly to output stream, but in this approach partial views would be rendered into string objects first - which leads to memory overhead.

  3. Use something like "RenderAction" from asp.net mvc, which calls a controller method from a view. I think this is a worst solution of 3 given, because it drops an MVC approach.

The question is not tied to any specific framework, I want to understand the general way of doing things like that.

UPDATE

After an answer given, I've found out that post is not clear. So reasonable update here:

Under viewmodel term I understand an object that contains all the data required to render a particular view.

All of three approaches involve building partial views with their own viewmodel. For example (using C# syntax):

class SinglePostViewModel {
  string Text {get;set;}
  string Title {get;set;}
  string Slug {get;set;}
  DateTime PublishedDate {get;set;}
  ...
}

class TagNavigationPanelViewModel {
  string TagText {get;set;}
  int PostsCount {get;set;}
}

class CalendarNavigationPanelViewModel {
  DateTime YearAndMonth {get;set;}
  int PostsCount {get;set;} 
}

My question is - how to nicely combine those partial views together.

3 Answers 3

1

I see another method that, unless I've misunderstood your post, hasn't been mentioned:

The main view, post, would have its model. This model would consist of ONLY the properties necessary to display this post (author,title,body, etc). Then, every piece of the post view that you can think of (timeline, tag navigation panel, subscribing panel, etc), would be split into their own views and each one would have its own model. This way, you can build up those models in your controller when you need them.

It might sound like an unnecessary amount of extra work to split these out like this, but it lends itself to the single responsibility principle. Keep each "view/model" focused on itself so that it can be re-used anywhere it is needed.

If you feel that your code is beginning to duplicate itself, which it might depending on your situation, you should consider writing some sort of "helper class." This helper class would handle all of the model build-up in one place, and all other duplicate code would be stripped down to a helper class call.

2
  • This is a general approach I try to follow. My question was about how to combine these partial views with own models together. Still I would update the question to make it more clear.
    – Hedin
    Aug 8, 2013 at 5:13
  • 1
    I see...sorry for the initial confusion. I'd go with the container view that houses all of the necessary partial views, personally.
    – ethorn10
    Aug 8, 2013 at 12:26
1

What I am doing is a variation/combination of your points 1 and 3. I combine the viewmodels in a container class, and use that as a "main" viewmodel. The pass the parts as models to the partials.

Following your example, I would create this viewmodel for the default page:

class DefaultViewModel {
  public List<SinglePostViewModel> Posts ...
  public TagNavigationPanelViewModel Tags ...
  public CalendarNavigationPanelViewModel Calendar ...
  ...
}

In Default.cshtml

@model DefaultViewModel
...html for rendering the default page...


@Html.Partial("_TagNavigationPanel", Model.Tags)
...etc...

In _TagNavigationPanel.cshtml

@model TagNavigationPanelViewModel
...html+razor for rendering the tags...

Then, follow the same approach for a single post page:

class SinglePostPageViewModel {
  public SinglePostViewModel Post ...
  // no tags in single view, for example..
  // public TagNavigationPanelViewModel Tags ...
  public CalendarNavigationPanelViewModel Calendar ...
  // But a list of related, perhaps?
  public List<RelatedPosts> RelatedPosts
  ...
}

And build your views (cshtml(s)) accordingly

3
  • This is what 1 approach is about. 3rd is only about calling RenderAction or @Html.Action, which involves calling the controller method. Thanks for reply!
    – Hedin
    Aug 8, 2013 at 8:15
  • 1
    @Hedin oh, ok... I thought that your approach #1 was more "monolithic", with one big (different) viewmodel. So yes, I use 1 and find it good. I also like the third one less but it is good wrt caching. If you have performance issues, or need to cache parts of the view, it may be worth trying it. Aug 8, 2013 at 13:16
  • What about skipping a partial view? e.g. based on a user role?
    – LifeH2O
    May 17, 2015 at 10:42
0

You may create a single "container" view with all three subviews - SinglePostView, TagNavigationPanelView and CalendarNavigationPanelView. You can also replace the SinglePostView with any other view a page requires. This way you need only replace the "central" (post) view for different pages with another type of view (for example list of posts) and the other views just need to be updated with the corresponding data, not replaced.

Your container view (for example MyView) should hold references to all subviews. It may have different update methods that are used to update different subview. For example - updateCalendar(CalendarNavigationPanelViewModel calendarData) or updatePost(SinglePostViewModel postData). Or even a single update(IViewModel modelData) - then you'll need to check the type of (and cast) the modelData to determine which view to update

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.