[asp.net-mvc-4] Pass data to layout that are common to all pages


Answers

I used RenderAction html helper for razor in layout.

@{
   Html.RenderAction("Action", "Controller");
 }

I needed it for simple string. So my action returns string and writes it down easy in view. But if you need complex data you can return PartialViewResult and model.

 public PartialViewResult Action()
    {
        var model = someList;
        return PartialView("~/Views/Shared/_maPartialView.cshtml", model);
    }

You just need to put your model begining of the partial view '_maPartialView.cshtml' that you created

@model List<WhatEverYourObjeIs>

Then you can use data in the model in that partial view with html.

Question

I have a website which have a layout page. However this layout page have data which all pages model must provide such page title, page name and the location where we actually are for an HTML helper I did which perform some action. Also each page have their own view models properties.

How can I do this? It seems that its a bad idea to type a layout but how do I pass theses infos?




if you want to pass an entire model go like so in the layout:

@model ViewAsModelBase
<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta charset="utf-8"/>
    <link href="/img/phytech_icon.ico" rel="shortcut icon" type="image/x-icon" />
    <title>@ViewBag.Title</title>
    @RenderSection("styles", required: false)    
    <script type="text/javascript" src="http://code.jquery.com/jquery-1.8.3.min.js"></script>
    @RenderSection("scripts", required: false)
    @RenderSection("head", required: false)
</head>
<body>
    @Html.Action("_Header","Controller", new {model = Model})
    <section id="content">
        @RenderBody()
    </section>      
    @RenderSection("footer", required: false)
</body>
</html>

and add this in the controller:

public ActionResult _Header(ViewAsModelBase model)



Creating a base view which represents the Layout view model is a terrible approach. Imagine that you want to have a model which represents the navigation defined in the layout. Would you do CustomersViewModel : LayoutNavigationViewModel? Why? Why should you pass the navigation model data through every single view model that you have in the solution?

The Layout view model should be dedicated, on its own and should not force the rest of the view models to depend on it.

Instead, you can do this, in your _Layout.cshtml file:

@{ var model = DependencyResolver.Current.GetService<MyNamespace.LayoutViewModel>(); }

Most importantly, we don't need to new LayoutViewModel() and we will get all the dependencies that LayoutViewModel has, resolved for us.

e.g.

public class LayoutViewModel
{
    private readonly DataContext dataContext;
    private readonly ApplicationUserManager userManager;

    public LayoutViewModel(DataContext dataContext, ApplicationUserManager userManager)
    {
    }
}



Presumably, the primary use case for this is to get a base model to the view for all (or the majority of) controller actions.

Given that, I've used a combination of several of these answers, primary piggy backing on Colin Bacon's answer.

It is correct that this is still controller logic because we are populating a viewmodel to return to a view. Thus the correct place to put this is in the controller.

We want this to happen on all controllers because we use this for the layout page. I am using it for partial views that are rendered in the layout page.

We also still want the added benefit of a strongly typed ViewModel

Thus, I have created a BaseViewModel and BaseController. All ViewModels Controllers will inherit from BaseViewModel and BaseController respectively.

The code:

BaseController

public class BaseController : Controller
{
    protected override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        base.OnActionExecuted(filterContext);

        var model = filterContext.Controller.ViewData.Model as BaseViewModel;

        model.AwesomeModelProperty = "Awesome Property Value";
        model.FooterModel = this.getFooterModel();
    }

    protected FooterModel getFooterModel()
    {
        FooterModel model = new FooterModel();
        model.FooterModelProperty = "OMG Becky!!! Another Awesome Property!";
    }
}

Note the use of OnActionExecuted as taken from this SO post

HomeController

public class HomeController : BaseController
{
    public ActionResult Index(string id)
    {
        HomeIndexModel model = new HomeIndexModel();

        // populate HomeIndexModel ...

        return View(model);
    }
}

BaseViewModel

public class BaseViewModel
{
    public string AwesomeModelProperty { get; set; }
    public FooterModel FooterModel { get; set; }
}

HomeViewModel

public class HomeIndexModel : BaseViewModel
{

    public string FirstName { get; set; }

    // other awesome properties
}

FooterModel

public class FooterModel
{
    public string FooterModelProperty { get; set; }
}

Layout.cshtml

@model WebSite.Models.BaseViewModel
<!DOCTYPE html>
<html>
<head>
    < ... meta tags and styles and whatnot ... >
</head>
<body>
    <header>
        @{ Html.RenderPartial("_Nav", Model.FooterModel.FooterModelProperty);}
    </header>

    <main>
        <div class="container">
            @RenderBody()
        </div>

        @{ Html.RenderPartial("_AnotherPartial", Model); }
        @{ Html.RenderPartial("_Contact"); }
    </main>

    <footer>
        @{ Html.RenderPartial("_Footer", Model.FooterModel); }
    </footer>

    < ... render scripts ... >

    @RenderSection("scripts", required: false)
</body>
</html>

_Nav.cshtml

@model string
<nav>
    <ul>
        <li>
            <a href="@Model" target="_blank">Mind Blown!</a>
        </li>
    </ul>
</nav>

Hopefully this helps.




instead of going through this you can always use another approach which is also fast

create a new partial view in the Shared Directory and call your partial view in your layout as

@Html.Partial("MyPartialView")

in your partial view you can call your db and perform what ever you want to do

@{
    IEnumerable<HOXAT.Models.CourseCategory> categories = new HOXAT.Models.HOXATEntities().CourseCategories;
}

<div>
//do what ever here
</div>

assuming you have added your Entity Framework Database




Related