Asp.Net MVC layout and partial views
Asked Answered
P

3

7

let's consider two views that use the same layout composed of:

  • A left column containing a "body" (which is filled differently by both views)
  • A right column that displays general information (passed via the model)

Instead of defining the right part twice, I wondered if I could create a PartialView to link directly from the layout page.

The problem is that the partial views implicitely inherit their models from the view that is being rendered. And since each view has its own model, I end up with a model type mismatch in the partial view.

From here I see two solutions:

  • I could insert the common part of the view model in the ViewBag. Unfortunately this means that each view that uses this layout has to implement this "convention" but nothing warns the developer about it at compile time...
  • I could use polymorphism to make each view model inherit from the same base class (edit: or interface) that the Partial Views uses. This would work up to a certain extend but would potentially exponentially increase in complexity as soon as I have a second partial view in the same layout.

So here are the questions:

  • Am I right with the assumptions above?
  • Do you see any other possibility?
  • Any return on experience on this?

Thanks a lot, TB.

Piggott answered 26/6, 2012 at 11:51 Comment(1)
Circa 2017, you could also make the common portion a property of each model, and then pass that as the model to @Html.RenderPartialAscus
P
7

Use an Interface and implement it on the two models, this is exactly the kind of thing they're used for.

Here is an example of two different Views using two different Models that both implement an interface. This is subtyping instead of ad-hoc polymorphism.

public class ViewModelOne : IReusableView
{
    public string Name { get; set; }
    public string Something { get; set; }
    public int ANumber { get; set; }
}

public class ViewModelTwo : IReusableView
{
    public string Name { get; set; }
    public string Thing { get; set; }
    public string SomethingElse { get; set; }
    public int ANumber2 { get; set; }
}

public interface IReusableView
{
    string Name { get; }
}

So we have the really simple partial view here that is 'InnerPartialView':

@model TestIntegration.Models.IReusableView
<div>
    @Model.Name
</div>

Which is used in the home and about pages of this example controller:

    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            ViewBag.Message = "Welcome to ASP.NET MVC!";

            return View(new ViewModelOne() { Name = "hello", Something="sdfsdfs", ANumber = 1 });
        }

        public ActionResult About()
        {
            return View(new ViewModelTwo() { Name = "hello 2", SomethingElse = "aaaddd", ANumber2 = 10, Thing="rand" });
        }
    }

The home view:

@model TestIntegration.Models.ViewModelOne
@{
    ViewBag.Title = "Home Page";
}

<h2>@ViewBag.Message</h2>
<p>
    To learn more about ASP.NET MVC visit <a href="http://asp.net/mvc" title="ASP.NET MVC Website">http://asp.net/mvc</a>.
    @Html.Partial("InnerPartialView")
</p>

The about view:

@model TestIntegration.Models.ViewModelTwo
@{
    ViewBag.Title = "About Us";
}

<h2>About</h2>
<p>
     Put content here.
         @Html.Partial("InnerPartialView")
</p>
Philip answered 26/6, 2012 at 11:55 Comment(3)
This is what I meant with "baseclass". I only fear that once I get multiple partial views, my view models grow quite fast in complexity, implementing this or that interface depending on the context. I guess that's better than the alternative I mentionned, but I'm surprised not to find any best practices here...Claudiaclaudian
A base class and interface are very different. I have updated with a full example as there's nothing complex about this. Don't go overboard with this, if you are adding a menu, for example, you wouldn't put that in the ViewModel, you'd pass that in the controller using the viewbag and handle that in the layout page. This method should be used for functionality that you want to reuse on objects, not for things like footers, menus or sidebars.Philip
Since you said the right pane was to display "general" information, this implies that it's showing fairly generic information, so it wouldn't depend too much on the detailed subclass. You haven't said much about your application domain, so it's hard to tell. For example, if it's a site for food recipes, you'd expect every recipe (no matter what subclass) to have a few common properties (title, yield, cooking time) and these could be expressed in an Interface. If your site is handling widely diverse types with nothing in common, then there may not be any meaningul "general" info to showDeaden
C
3

When you render the partial view, you can send it a model:

@Html.RenderPartial(MVC.Partials.Views.Sidebar, Model.SideBarModel);

So you could send down data as part of the parent model that is the model for the partial sidebar.

Contraband answered 26/6, 2012 at 12:59 Comment(1)
That would work if I knew what the model was. Since I am performing this RenderPartial in the layout, my model can be anything. Unless I have a common baseclass or interface for all my models I cannot assume anything about them.Claudiaclaudian
D
-1

In partial views, models are of type dynamic so you don't need to know what type they are. However, you just need to make sure the model has the property you need. In other words you can use Model.MyProperty or Model.MyProperty as MyPropertyType when using Html.Partial.

Deraign answered 25/3, 2013 at 22:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.