How to make your views DRY with MVC5 and multiple page breakpoints? - asp.net

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.

Assumptions
... are the mother of all....
"some stuff" that goes in the body tag is HTML being fed from some data source, or is hard-coded
"...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)
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='720'>
<div>Hello World!</div>
</div>
<div class="breakpoint"
id="bp_2"
data-max-width='720'>
<div>Another Breakpoint</div>
</div>
<div class="breakpoint"
id="bp_3"
data-max-width='720'>
<div>Third Breakpoint</div>
</div>
<div class="breakpoint"
id="bp_4"
data-max-width='720'>
<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.

Related

How can I change the attribute in header section of cshtml files?

I have a problem about removing attributes when I navigate pages.
I used PartialView in HomeController to define index.cshtml page.
The line which I pass the data to the destination is showed orderly.
HomeController -> Contract.cshtml -> _Layout.cshtml -> NavbarPartial.cshtml
Here is HomeController.cs file shown below.
public ActionResult ContractUs()
{
ViewBag.Attribute = ""; // header-transparent
return View();
}
Here is my Contract.cshtml shown below.
#{
ViewBag.Title = "Contract Us";
Layout = "~/Views/Shared/_Layout.cshtml";
}
Here is my _Layout.cshtml shown below.
Html.RenderAction("NavbarPartial", "Home");
Here is my NavbarPartial.cshtml shown below.
<header id="header" class="fixed-top d-flex align-items-center header-transparent">
</header>
What I want to do is to show this header code snippet in the index page and show another one on another page like this shown as below without changing NavbarPartial.cshtml.
Index.cshtml
<header id="header" class="fixed-top d-flex align-items-center header-transparent">
</header>
Contract.cshtml
<header id="header" class="fixed-top d-flex align-items-center">
</header>
How can I do that?
If you render your partial view from another view, then you can send some parameters to decide what class should be applied. So let's look at the example.
We call partial view and send additional data:
#{Html.RenderPartial("_Header", new ViewDataDictionary { { "ApplyStyle", true } });}
and then in partial view we can apply style conditionally:
#{
var yourClasses = "";
if ((bool)ViewData["ApplyStyle"])
{
yourClasses = "fixed-top d-flex align-items-center header-transparent";
}
else
{
yourClasses = "fixed-top d-flex align-items-center";
}
}
<header id="header" class="#yourClasses">
This is header!
</header>
UPDATE:
This is a fiddle with complete example. However, this fiddle does not support PartialView, but I believe basic idea is shown.
Try using conditions in class value.
For example:
<header id="header" class="fixed-top d-flex align-items-center #(#Model.name === 'index' ? 'header-transparent' : '')">
If you dont have any kind of variable name for current component, you can use Javascript's location object.
window.location.pathname will provide you the pathname of current page.
Using this value you can add conditional class values

Unable to load the data from other controller

I have a controller, which is composed of many other partial views. I wanted to use a particular section of that controller in another controller. I am able to see the design but unable to load items in it.
Let say I have one controller named "Products" within Product view folder I have _items.cshtml. I wanted to use this _items.cshtml in another controller called "placeTheOrder".
In particular section of div in placeTheOrder view I referred to #Html.Partial("~/Views/Products/_items.cshtml"). Even after doing so it is unable to load the content from _items.cshtml into placeTheOrder.
What am I doing wrong.
_items.cshtml view
<div id="accordionProduct" class="span-6 last prod-acc">
#Html.Partial("~/Views/product/_ezCpSearchBar.cshtml")
<div id="filterPanel" class="span-6 last filter-panel">
<div class="span-6 last">
<div class="filter-panel-head">
<h1>Filter</h1>
</div>
</div>
<div class="span-6 last">
<div class="filter-panel-body">
<div class="filter-panel-prop"></div>
</div>
</div>
</div>
<div id="accordionProductInner" class="span-6 last prod-acc-body">
</div>
</div>
// this is the script are where the content gets loaded into the view
<div id="TemplateFilterItem" class="hidden"></div>
<div id="TemplateLastViewItem" class="hidden"></div>
This is the place where I have refered to it in another controller
<div class="span-6" style="background-color:#d4d4d4;padding:20px;">
#Html.Partial("~/Views/product/_items.cshtml")
</div>
You can use Html.Renderpartial in your view instead
Html.RenderPartial("~/Views/ControllerName/ViewName.cshtml", ModelData);
If you are using different model for both the views then you need to make ViewModel which will include required properties.
Hope this Helps

Partial view for container

Say, I'm using often the following HTML in ASP.NET MVC application:
<div class="a">
<div class="b">
(varying content)
</div>
</div>
I'd like to put the repeating div part to a separate file and reuse it (notice, that the content may, and will change). How can I do that?
Keep it in a partialView, eg: _CommonDiv
call Partial view from your view
#Html.Partial("_CommonDiv", null, new ViewDataDictionary {{ "yourvaringcontentKey", yourcontent}})
get content in partialView
string yourcontent= (string)this.ViewData["yourvaringcontentKey"];
And use the same where you want.
In your web application add App_Code\Helpers.cshtml
Paste the code below inside Helpers.cshtml
Helper code:
#helper DivHelper(string text)
{
<div class="a">
<div class="b">
#text
</div>
</div>
}
Now you can call this helper in any of your views using #Helpers.DivHelper("Some text...") and because the logic is inside App_Code no using statements are required!

Control Layout Rendering from Child Pages

I have a layout page and, for the moment, one page that uses that layout. I would like to have a portion of the page that can be shown or hidden at the child pages discretion. If it is hidden, then other style changes, like the width of the main content div, need to be adjusted. Below is my non-working attempt at this:
Layout page
#{
Layout = "~/Views/Shared/_Layout.cshtml";
ViewBag.ShowTree = true;
}
<div class="row">
#if(ViewBag.ShowTree){
<div class="col-md-2">
#Html.Action("ItemTree")
</div>
}
<div class="#(ViewBag.ShowTree ? "col-md-10" : "col-md-12")">
#RenderBody()
</div>
</div>
Child Page
#{
ViewBag.Title = "Edit";
Layout = "~/Views/Shared/_NestedLayout.cshtml";
ViewBag.ShowTree = false;
}
In this instance the value for ShowTree is set to true, the layout page renders, and then the child sets it to false and the content renders. This makes perfect sense based on the page life cycle for MVC. But of course is not how I want it to work.
I know I could set this value in the controller, and not set it in the layout/web page at all, but I'd like to have this be purely a design thing if I can and keep it out of the controller.
My preference would be for it to not call the action at all, and not just hide the data with JavaScript.
I like your approach, but if that doesn't work, the only other mechanism I can think where the child view can control the layout would be using #section. Not ideal, but should work.
Layout page:
<div class="row">
#RenderSection("ItemTreeSection", required: false)
<div class="#(ViewBag.ShowTree ? "col-md-10" : "col-md-12")">
#RenderBody()
</div>
</div>
Child view:
#section ItemTreeSection {
<div class="col-md-2">
#Html.Action("ItemTree")
</div>
}

Asp.Net MVC - Open HTML Controls

I'm new to asp.net mvc. I'm looking to create some control for reusing html. I have a complex HTML box for example:
<div class="Box">
<div class="Top"></div>
<div class="Content">
<div style="padding:10px;">
[CONTENT GOES HERE]
</div>
</div>
<div class="Bottom"></div>
</div>
Previously using webforms I've been able to reuse this by inheriting from WebControl and override Render. But how can I implement this in MVC?
The Content of the box could of course be what ever. Other boxes for example.
Easiest yet: using a UserControl
<%# Control Language="VB" Inherits="System.Web.Mvc.ViewUserControl" %>
<div class="Box">
<div class="Top"></div>
<div class="Content">
<div style="padding:10px;">
<%= Model.Content %>
</div>
</div>
<div class="Bottom"></div>
</div>
And then calling the user control with
<% Html.RenderPartial("NiceBox", New with {.Content = "The real content goes here"})%>
Here's an example of one way to build it as an HtmlHelper extension method:
public static string Box(this HtmlHelper helper, string content)
{
var builder = new StringBuilder();
builder.Append("<div class=\"Box\" .......... );
if (!String.IsNullOrEmpty(content))
{
builder.Append(content)
}
return builder.ToString();
}
In your view:
<%= Html.Box(contentString) %>
Your other options are to create it as a partial view (.ascx) and pass it a ViewModel class that contained the content that you wanted to render. Judging by the requirement of "the content could be anything", an HtmlHelper extension might be more flexible for you.

Resources