11

I have a layout page that has variables that need to be filled. Example:

@ModelType KarateAqua.schoolModel

<html>
    <body>

        @RenderBody()

        <div id="footer">
            <div class="content">
                <div class="bottom_logo">
                    <a href="/"><span class="inv">@Model.schoolName</span></a>
                </div>
            </div>
        </div>
    </body>
</html>

I don't want to populate this in every ActionResult. Is there a way to pass data to a layout page once and do it for all instances?

Rey
  • 3,663
  • 3
  • 32
  • 55
Rupert
  • 4,209
  • 7
  • 31
  • 36
  • The best answer is: http://stackoverflow.com/questions/13225315/pass-data-to-layout-that-are-common-to-all-pages – DeeArgee Feb 13 '15 at 20:01

7 Answers7

16

Create an action filter and decorate your controller classes. Inside the action filter you have access to put values in the viewbag which are available to your layout.

This will run on each request and you will not have to set the values in each action. You can look for and ignore things like a child request and ajax requests which typically do not use the layout anyways and not set your viewbag values for those.

Below is a sample of an attribute i created to copy an object from the session and make it available to the layout via the ViewBag

public class CurrentUserAttribute : ActionFilterAttribute
{
    public override void OnResultExecuting(ResultExecutingContext filterContext)
    {
        // Don't bother running this for child action or ajax requests
        if (!filterContext.IsChildAction && !filterContext.RequestContext.HttpContext.Request.IsAjaxRequest())
        {

            if (filterContext.HttpContext.Session != null)
            {
                var currentUser = filterContext.HttpContext.Session["CurrentUser"] as CurrentUser;
                if (currentUser != null)
                {
                    filterContext.Controller.ViewBag.CurrentUser = currentUser;
                }
            }
        }
    }


}
JBeckton
  • 7,095
  • 13
  • 51
  • 71
9

OK since you want this to be set once you can make use of a partial view. However depending on your needs you will need to have several partial views (may be not ideal if sections are going to be scattered across the _layout page)

your partial view will look like

@model KarateAqua.schoolModel

<div class="bottom_logo">
<a href="/"><span class="inv">@Model.schoolName</span>
</div>

Controller

public class SchoolController : Controller
{
     public ActionResult Index()
     {
          //get schoolModel  
          return PartialView(schoolModel);
     }
}

in your _layout.cshtml place this line where you want to have the partial view to be inserted

@Html.Action("Index","School")
Prashanth Thurairatnam
  • 4,353
  • 2
  • 14
  • 17
4

You can open a code block on the layout page and fill the object there. This will execute every time the layout page is being used. The benefit is that you do not have to alter anything on your controller:

@{
    KarateAqua.schoolModel data = YourBusinessLayer.Method();
}

<html>
<body>

    @RenderBody()

    <div id="footer">
        <div class="content">
            <div class="bottom_logo">
                <a href="/"><span class="inv">@data.schoolName</span></a>
            </div>
        </div>
    </div>
</body>
</html>
Rey
  • 3,663
  • 3
  • 32
  • 55
Joe Eveleigh
  • 89
  • 1
  • 4
  • 2
    Code blocks in views should only be used when no other option is available imo. You would make the view too smart. Its a view and it should present data based on a modal thats good enough to work without code blocks. – Kees Apr 11 '18 at 09:27
1

You could use ViewBag or ViewData to pass data to your Layout pages.

Layout

<html>
<body>
@RenderBody()

<div id="footer">
<div class="content">
<div class="bottom_logo">
<a href="/"><span class="inv">@ViewBag.schoolName</span>
</div></div></div>
</body>
</html>

Controller

public ActionResult Index(){
   ViewBag.schoolName = "Bayside Tigers";
   return View();
}
Gabe
  • 49,577
  • 28
  • 142
  • 181
  • 2
    But would I have to do that for each controller action? – Rupert May 11 '12 at 13:47
  • Yes, just like you would have to return a model for each action. – Gabe May 11 '12 at 13:58
  • 5
    Poor software design. – mcserep Apr 26 '14 at 17:08
  • 8
    @CMate - Poor comment – Gabe Apr 28 '14 at 15:44
  • 6
    @Gabe, please a learn a little about reusability, which is a key aspect when creating a software with sustainable maintainability. It shows a very poor software design, when a code segment must be repeated at the end of every action method, making it redundant and hard to maintain. It is especially bad, when the ASP.NET MVC framework offers base classes to avoid such a bad programming scheme. For more information on the topic, see the ActionFilterAttribute class on MSDN. It was also presented in JBeckton's answer. – mcserep Apr 28 '14 at 22:36
  • What if you want to get a result from the database? For example, ViewBag.htmlraw = db.Entries.Find(3); then how would you call it in the view?
    @ViewBag.htmlraw
    – JoshYates1980 Sep 10 '14 at 21:11
  • The previous comment can't be edited, but this is how I did it: var result = new HelpEntry(); result = db.HelpEntries.Find(id); ViewBag.htmlraw = result.Description; – JoshYates1980 Sep 10 '14 at 21:38
  • He doesn't want to populate this in every ActionResult. downvote. – RayLoveless Feb 04 '15 at 05:24
1

You could always create action that returns a partial view of your header.

Just add this to your layout page:

<html>
    <head> 
    </head>
        <body>
            @{ Html.RenderAction("header", "MyController", new { area = "" }); }

            @RenderBody()
//...
Rey
  • 3,663
  • 3
  • 32
  • 55
RayLoveless
  • 19,880
  • 21
  • 76
  • 94
1

Your Layout Page:

@ViewBag.LayoutVar

Your HomeController:

public class HomeController : BaseController
{
   //Here some logic...
}

Your BaseController

namespace ProjectName.Controllers
{
    public class BaseController : Controller
    {

        public YetkiController()
        {
            //This parameter is accessible from layout
            ViewBag.LayoutVar = "Suat";
        }
    }
}

Logic is easy: You create BaseController which includes every global parameters you will use in layout. (Like username or other data based parameters)

You inherit (call) BaseController to get all parameters into your current controller.

Rey
  • 3,663
  • 3
  • 32
  • 55
Suat Atan PhD
  • 1,152
  • 13
  • 27
1

I used HTTP Session to persist the data between different pages -

//Opening page controller
public ActionResult Index()
{    
    Session["something"]="xxxx";
    return View();
}

In the shared _layout page;

//persistent data   
<p>Hello, @Session["something"]!</p>

Hope this helps but it won't work if you start from a different page from the default one that has been set.

Rey
  • 3,663
  • 3
  • 32
  • 55
Jon649
  • 279
  • 3
  • 7