0

I have a predicament that I am not quite sure how to overcome. I do not know what is the right way. I am building a website and I was given a template to integrate with my server code. The problem lies in how the template is outlined. Let me show you an example.

<body>
    <div class="breakpoint active" id="bp_infinity" data-min-width="588">
        <div id="header">full page header content</div>
        <div id="body">some stuff</div>
        <div id="footer">some stuff</div>
    </div>
    <div class="breakpoint" id="bp_587" data-min-width="493" data-max-width="587">
        <div id="header">mobile header content</div>
        <div id="body">some stuff</div>
        <div id="footer">some stuff</div>
    </div>
    <div class="breakpoint" id="bp_492" data-max-width="492">
        <div id="header">mobile header content</div>
        <div id="body">some stuff</div>
        <div id="footer">some stuff</div>
    </div>
</body>

I am trying to setup my MVC5 Views in a way that does not repeats common code. The problem that I am facing is that the header and footer div are common code from page to page and the body changes. The second problem is that each page has different number of breakpoints. Here is a second page to show what I mean:

<body>
    <div class="breakpoint active" id="bp_infinity" data-min-width="588">
        <div id="header">full page header content</div>
        <div id="body">some stuff</div>
        <div id="footer">some stuff</div>
    </div>
    <div class="breakpoint" id="bp_587" data-max-width="587">
        <div id="header">mobile header content</div>
        <div id="body">some stuff</div>
        <div id="footer">some stuff</div>
    </div>
</body>

So the Layout page is now tricky to setup because I can't just say:

<body>
    @RenderBody
</body>

One of the solutions I thought of was to use Sections, something like this:

<body>
    @RenderBody
    @RenderSection("Breakpoint-1", false)
    @RenderSection("Breakpoint-2", false)
    @RenderSection("Breakpoint-3", false)
</body>

Now each page would be along the lines of:

@section Breakpoint-1
{
    <div class="breakpoint active" id="bp_infinity" data-min-width="588">
        @{ Html.RenderPartial("full-page-header"); }
        @{ Html.RenderPartial("full-page-body"); }
        @{ Html.RenderPartial("full-page-footer"); }
    </div>
}
@section Breakpoint-2
{
    <div class="breakpoint" id="bp_587" data-max-width="587">
        @{ Html.RenderPartial("mobile-page-header"); }
        @{ Html.RenderPartial("mobile-page-body"); }
        @{ Html.RenderPartial("mobile-page-footer"); }
    </div>
}

A problem that I see with above code is that if the header now needs to have 5 breakpoints instead of 2, I need to go and modify it everywhere.

Is there a better way to do this? Is what I thought of the best solution for my scenario?

EDIT: To clarify. There are multiple brakpoints in the HTML because only one of them is active at a time. When page hits a certain width, 1 the currenct active breakpoint gets hidden and the new one becomes visible.

Bagzli
  • 6,254
  • 17
  • 80
  • 163
  • From the last paragraph, it seems you displaying different data based on the device, so why are you not just detecting the device/screen size and rendering only the html you need (generating 2 - 5 times as much html is just degrading performance) –  Nov 03 '16 at 23:37
  • If you really do want to do this, then one option would be to create a `HtmlHelper` extension method which might be called using (say) `@Html.Breakpoint(header, body, footer, width)` where the first 3 parameters are the names of the partial views (typeof `string`). The method would output the `
    ` and the partials
    –  Nov 03 '16 at 23:44
  • @StephenMuecke It is a responsive website, so it needs to be able to switch sizes on desktops. As for your second post, that would mean that I am building my HTML in a string on the serverside. Somehow, it doesn't feel right. – Bagzli Nov 04 '16 at 01:08
  • Yes, I know - but you do not need to send 5 times as much html to the browser as necessary - refer [this article](https://www.asp.net/whitepapers/add-mobile-pages-to-your-aspnet-web-forms-mvc-application). Not sure what you mean by your 2nd comment - your already using `HtmlHelper` methods in your view (which do _build HTML in a string on the serverside_) –  Nov 04 '16 at 01:15
  • The extension method means all your main view needs to render the output is `@Html.Breakpoint("full-page-header", "full-page-body", "full-page-footer") @Html.Breakpoint("mobile-page-header", "mobile-page-body", "mobile-page-footer") ` - I omited the last parameter - but it probably should be an `enum` which then allows you to conditionally set attributes such as `id`, `min-width` etc –  Nov 04 '16 at 01:18
  • @StephenMuecke ok, but you are still printing the page multiple times like I am right? Also what would `@Html.Breakpoint` return? A String? Does it render a partial view and returns that? Could you post an answer and give example on how you would do it? – Bagzli Nov 04 '16 at 14:29
  • If you were to use an extension method (e.g. `@Html.Breakpoint(..)` then yous you would (which is why I did not recommend it - I just stated if you want a bad solution, then that's one way to solve it). And the method would return a `MvcHtmlString` - the same as all other `HtmlHelper` methods do. –  Nov 05 '16 at 06:10
  • I won't post a bad answer, but if you want to understand how to create extension methods, but you can refer [this answer](http://stackoverflow.com/questions/34913037/refactor-similar-chtml-that-renders-varying-properties-using-editorfor-and-label/34913933#34913933) fro an example of creating a method that combines the built-in helper methods plus additional html –  Nov 05 '16 at 06:13

1 Answers1

0

Assumptions

... are the mother of all....

  1. "some stuff" that goes in the body tag is HTML being fed from some data source, or is hard-coded
  2. "...the header and footer div are common code from page to page..." means that literally, you don't need to change the header/footer at all. (You still could, but I'm ignoring that for now)
  3. The div id's "header", "body", "footer" should be handled as dom classes rather than dom ids. That is another discussion, but ids should always be unique.

Solution

This is a basic example, there are plenty of other approaches to try and plenty of other tweaks you can make

Controller Let's call this BreakpointController

public ActionResult Index()
{
  var model = new List<BreakpointViewModel>();
  // populate model
  return View(model);
}

ViewModel

public class BreakpointViewModel
{
    public string BreakPointId { get; set; }
    public int? MinWidth { get; set; }
    public int? MaxWidth { get; set; }
    public string Body { get; set; }
    public bool IsActive { get; set; }
}

View This should be your index.cshtml (or whatever you want to call it)

@model IEnumerable<WebApplication1.Models.BreakpointViewModel>

<div>
    <h1>A header!</h1>
</div>

@Html.DisplayForModel()

<div>
    <h4>A footer!</h4>
</div>

DisplayTemplate * Thou shalt live in the folder containing views for the controller (or Shared) * Thou shalt live in a subfolder named 'DisplayTemplates' * Thou shalt be named {ModelName}.cshtml

in the end, the folder structure should look something like this:

Views
|-- Breakpoint
|   |-- DisplayTemplates
|   |   +-- BreakpointViewModel.cshtml
|   +-- Index.cshtml

And BreakpointViewModel.cshtml should look like this:

@model WebApplication1.Models.BreakpointViewModel

<div class="breakpoint @(Model.IsActive ? "active" : null)"
     id="@Model.BreakPointId"
     @(Model.MinWidth.HasValue ? "data-min-width='" + Model.MinWidth + "'" : null)
     @(Model.MaxWidth.HasValue ? "data-max-width='" + Model.MaxWidth + "'" : null)>
     @Html.Raw(Model.Body)
</div>

Note the minwidth/maxwidth lines in the div. Not required, just how I would personally deal with the widths.

Resulting HTML

<div>
    <h1>A header!</h1>
</div>

<div class="breakpoint active"
        id="bp_1"
        data-max-width=&#39;720&#39;>
    <div>Hello World!</div>
</div>

<div class="breakpoint"
        id="bp_2"
        data-max-width=&#39;720&#39;>
    <div>Another Breakpoint</div>
</div>

<div class="breakpoint"
        id="bp_3"
        data-max-width=&#39;720&#39;>
    <div>Third Breakpoint</div>
</div>

<div class="breakpoint"
        id="bp_4"
        data-max-width=&#39;720&#39;>
    <div>Fourth Breakpoint</div>
</div>

<div>
    <h4>A footer!</h4>
</div>

Original Answer

DisplayTemplates are your friend. If your sections are going to be the same, you can put the relevant information into a ViewModel, then pass the List<ViewModel> to the DisplayTemplate. The MVC engine will then use the DisplayTemplate for your ViewModel to fill out the needed code for each section.

You only need code your DisplayTemplate for your ViewModel once.

I don't have any sample code up at the moment, but if you need further help, comment on this and I'll break some out over the weekend.

Kevin R.
  • 3,571
  • 1
  • 19
  • 29
  • If you have time, please post an example of what you mean, I'm still a little lost. – Bagzli Nov 05 '16 at 13:38
  • I did a lot more digging and I found out about Editor Templates on top of Display Templates. I have a theory of making it work with Editor Templates which I am trying to implement now. When you said Display, did you mean Editor? – Bagzli Nov 05 '16 at 14:28
  • Sorry, I haven't had time to work up an example yet. There are EditorTemplates and DisplayTemplates. They work much the same way, but usually you'll use `@Html.DisplayFor` in your DisplayTemplates and `@Html.EditorFor` on your EditorTemplates. I should be able to work something up for you tomorrow and I'll edit my answers. – Kevin R. Nov 05 '16 at 17:30
  • If I understand this correctly, to populate the html content for the body, I have to do it in the controller or some service instead of the views? – Bagzli Nov 06 '16 at 03:48
  • It depends on what you're trying to do and what the content is. – Kevin R. Nov 06 '16 at 04:01