0

I need to set a banner image in my layout page dynamically based on a selected value. I have been researching but it seems that i cannot have a controller for the layout page, so i have been looking at using a partial view but i am missing something apparently. How can i accomplish this?

Layout Page

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>@ViewBag.Title - My ASP.NET Application</title>
    @Styles.Render("~/Content/css")
    @Scripts.Render("~/bundles/modernizr")
</head>
<body>
    <div class="header">
        <div>
            @Html.Partial("_Header")
        </div>
    </div>
    <div class="container body-content">
        @RenderBody()
        <hr />
        <footer>
            <p>&copy; @DateTime.Now.Year - My ASP.NET Application</p>
        </footer>
    </div>

    @Scripts.Render("~/bundles/jquery")
    @Scripts.Render("~/bundles/bootstrap")
    @RenderSection("scripts", required: false)
</body>
</html>

Partial view

@model TicketPaymentsAzureMVC.Models.Client

<h1>Header</h1>
<img src="@Url.Content(Model.BannerUrl)" alt="Image" />
Rick james
  • 824
  • 1
  • 11
  • 30
  • Are you getting an error? what's the issue? are you setting the property BannerUrl in the model? – Francisco Goldenstein Feb 03 '17 at 20:41
  • No error, i guess im not sure how or where to go about setting the property. Does the partial view use its own controller? Where do i perform my logic and pass to it? – Rick james Feb 03 '17 at 20:44
  • I answered your question. I think doing an AJAX call in the layout is the best way to go. Partial views use the same model (or subset of the model) you have in the current view if you do @Html.Partial(...). You could generate the partial view using an AJAX call but in this case, I think it's better to make an AJAX call and get just the image url. – Francisco Goldenstein Feb 03 '17 at 20:51
  • What version of .net are you on? If you are using .net Core then take a look at view components. They are sort of like partials but they also have a controller! https://learn.microsoft.com/en-us/aspnet/core/mvc/views/view-components – nurdyguy Feb 03 '17 at 20:54
  • so no way to do this using a controller? I have other C# libraries and methods i need to consume to get the necessary information. – Rick james Feb 03 '17 at 20:54
  • Yes, as I was saying, you can do it but you are gonna have to share a base model across all the views that use that layout. I don't like that approach. Or you could just set the Property in a ViewBag if you prefer. I don't know your expertise in development so I recommended you the best way to go. Do the ViewBag.BannerUrl in every controller action and change @Url.Content(Model.BannerUrl) to @Url.Content(ViewBag.BannerUrl) – Francisco Goldenstein Feb 03 '17 at 20:56

2 Answers2

6

Using @Html.Partial() does not call a controller method and just renders the html defined in the partial. By default it passes the model from the main view to the partial (unless you specify it in the 2nd parameter), so unless the model in the main view is typeof TicketPaymentsAzureMVC.Models.Client your code will throw an exception.

If you use a view model that contains a property Client Client (and you populate that property in the GET method), then you could use

@Html.Partial("_Header", Model.Client)

to pass an instance of Client to the partial. That would mean that every view using that layout would need that property.

A better option is to use @Html.Action() or @{ Html.RenderAction(); } to call a server method that returns the partial

[ChildActionOnly]
public ActionResult Header()
{
    Client model = ... // initialize your model and set the `BannerUrl` property
    return PartialView(_"Header", model);
}

and in the layout

@{ Html.RenderAction("Header", yourControllerName); }
  • I got this error when trying this `The controller for path '/' was not found or does not implement IController.` – Matthew Aug 13 '18 at 04:58
  • 1
    @Matthew, That suggests there is an error in your code. I cannot guess what your code is so I suggest you ask a new question with all the relevant details –  Aug 13 '18 at 05:00
0

All the options I can think of:

1) Share a model across all the actions that use that layout.

2) Use ViewBag. Link: https://msdn.microsoft.com/en-us/library/system.web.mvc.controllerbase.viewbag(v=vs.118).aspx

3) Use RenderAction (the layout doesn't depend on the actions that are executed). Link: https://msdn.microsoft.com/en-us/library/ee839451(v=vs.98).aspx

4) Making an AJAX call from a JS file that is referenced in the layout (the layout doesn't depend on the actions that are executed). Link: http://www.aspsnippets.com/Articles/ASPNet-MVC-jQuery-AJAX-and-JSON-Example.aspx

The way you are doing it you need to define a base class for the model with property BannerUrl. You could use ViewBag to avoid that but I don't like that solution either.

I would do this:

1) Create a JS file called layout.js. That file would make an AJAX call to the get the banner URL and set it.

2) In the layout, reference the JS file created in (1).

3) Any page that uses the layout doesn't need to do anything. The layout is handling it by itself so no dependency.

Francisco Goldenstein
  • 13,299
  • 7
  • 58
  • 74