MVC3 nested layouts RenderSection fails - asp.net-mvc-3

I have the following setup
LayoutBasic.cshtml defining my head and body section + a #RenderSection("Extra", required: false);
LayoutTabPage.cshtml using LayoutBasic.cshtml defining the layout of a tab page
SpecificTabPageIndex.cshtml using LayoutTabPage.cshtml defining a #section Extra{} with some link and style tags
Sometimes I have a partial that contains the #section Extra{} rendered from within the SpecificTabPageIndex.cshtml
The problem is that it that the "Extra" section is not rendered. Sometimes the code runs fine but the section is not rendered and sometimes I get the "The following sections have been defined but have not been rendered for the layout page" error.
Can anyone give me a hint as to what I'm doing wrong? Is RenderSection unusable when nesting layouts in as many levels as I do?
TIA
./kroogar

Sometimes I have a partial that contains the #section Extra{}
Partials cannot have sections. That is a non supported scenario.

Related

How to programmatically add script or stylesheet on a per page basis in Docpad

How can I programmatically add script or stylesheet tag to a page specified in page's YAML front matter (meta)?
Assuming there is src/documents/posts/a.html.eco with following contents:
---
layout: default
scripts: ['a.js']
---
Blog post that requires a special javascript
and layout src/layouts/default.html.eco with following contents:
...
#getBlock('scripts').toHTML()
</body>
...
The final result for posts/a.html should be:
...
<!-- some extra stuff that was added when processing script tag -->
<script scr="/scripts/a.js"></script>
</body>
...
..while other pages shouldn't have a reference to /scripts/a.js
The comment above tag is just to show that there may be some processing envolved before injecting the tag.
I tried many approaches using different events in docpad.coffee file (including approach taken from docpad-plugin-livereload plugin) but every time I was facing the same problem - script tag was applied to all pages instead of being applied to a.html only. Here is one of my tries:
renderDocument: (opts) ->
{extension,templateData,file,content} = opts
if extension == 'html' and scripts = file.get('scripts')
if typeof scripts != 'undefined'
scripts.forEach (scriptName) ->
#docpad.getBlock('scripts').add('<!-- custom script tag here -->')
I've also tried render event, populateCollections (which is not documented however I found it in docpad-plugin-livereload plugin) and even extendTemplateData events and no luck so far.
I know there is a method of doing this right inside a layout:
#getBlock('scripts').add(#document.scripts or [])
..which is totally fine and it really works as expected however it doesn't seem to provide enough freedom for me to manipulate the content before it's injected to a page.. And even if it's possible I won't like the idea of having some heavy logic inside layout template, I want it to be in a plugin/docpad.coffee
Hopefully that makes sense
Try templateData.getBlock('scripts').add instead of docpad.getBlock('scripts').add

MVC 3 HTML 5 and HTML 4

I have a MVC 3 project and I enable the HTML 5 markup. For the HTML 5 part, it is running fine.
Now I created a partial view and copy and paste some code into it
The first line is
<div id="Header">
I want to use this partial view in my view which is html 4 markup.
The problem comes, the razor engine automatically convert my
<div id="Header">
into
<Header>
I don't want to use HTML 5 for this view but only HTML 4. What should I do?
If you change the div as follows, it won't get swapped out:
<div id="strap">
Which may solve your immediate problem.
why do you want to covert back to:
<div id="Header">
where is the way to go in the future?
If you worried about the compatibility with the older browser, you can simply implement modernizr-2.6.2.js. (which should come with the Visual Studio web projects)
This will make all the basic HTML5 works in older browser.
(tho haven't tested with browser older than IE6)
you could always use a class instead of an id
<div class="header">
this has the added advantage of not making your css (if any) for this element very specific, and also means that the ID cannot be reused.
you can still select your element using jQuery selector using it's class as the selector
$('.header')
question for you: Did you created the Html 4 partial view in Html 5 view? ; If your answer is yes, the problem you mentioned is occurring naturally. When MVC renders your page, he first renders the Html 5 part(the layout master page). As a result, Html5 type doc type will be chosen by default. After that when the Html 4 partial view is rendered, the engine renders it under a mark up support for Html 5.
if i want to summarize what i wanted to say:
<html>
//Html 4 partial view.
</html>
This will make a Html 5 markup view no matter what Html4 code you wrote in that partial view. If i am wrong, let me know. I will learn from my mistakes. thanks.
Can you please use Transitional doctype on the top of your partial view page. Because HTML engine understand the page according to doctype.

Declarative AJAX "Controls" in MVC

I want to create a reusable ajax control in MVC .NET using RAZOR.
my example is a simple ajax text box and list where the user filters the list by typing in the text box. on the first call i would render both the text box and the list using my razor view. on subsequent AJAX calls i would want to ONLY render the (now filtered) list.
idea 1: use #if statement to conditionally render code.
problem: razor does not seem to like conditionally written html. for example it errors when a <div> tag is not followed by a closing </div>.
idea 2: use #section tokens to create portions of my control and then call RenderSection within the same file as needed.
problem: razor does not allow RenderSection to call sections in the same page
i know i can conditionally render html as strings, but i wanted to take advantage of the legibility of the razor markup and keep with development protocols.
You should be able to output <div> tags in a Razor block without the corresponding </div> tag by surrounding it with <text>. The reason is that Razor uses the closing tag to know when to drag back into code-parsing mode:
#if (myCondition)
{
<text>
<div>
</text>
}
As for the Section stuff, you might be able to achieve what you want using Templated Razor Delegates, like this:
#{
Func<dynamic, object> b = #<strong>#item</strong>;
}
// ...
<span>This sentence is #b("In Bold").</span>
See Phil Haack's blog for a little more on this.

How to modify body class in MVC3 view page

Just wanted to add a class="myClass" in body tag. Is there any html helper or something else can do this in MVC3 view page? Please advise, thanks.
This is very similar to Aaron's solution, but doesn't have the weight of a section (which at least in my mind, are for larger blocks of content than a single string). The simplest way is to pass a variable with the ViewBag.
In your layout, just print out the class for the body tag, plus any other page specific variables (page title, extra css/js scripts, etc...)
_Layout.cshtml:
<html>
<title>#ViewBag.Title</title>#* Can easily add in per page titles too *#
<body class="#ViewBag.BodyClass">
#RenderBody()
</body>
</html>
Then, variables set in your view get passed upwards to the layout:
Index.cshtml:
#model MyViewModel
#{
ViewBag.Title = "This page title!";
ViewBag.BodyClass = "...";
}
While you may have full control of the HTML a solution was what was needed so here is one ;-)
In the _layout.cshtml page
<body class="#RenderSection("BodyClass", false)">
This will look for a section in all the child pages but says don't worry if it can't find one
Then in your child views just do this
#section BodyClass {productList}
Keep it on one line and then the outputted HTML will look fine, also you can then build up the class names as well.
#section BodyClass {productList generic}
This idea goes perfect with DOM-Ready page specific code, why not checkout
http://paulirish.com/2009/markup-based-unobtrusive-comprehensive-dom-ready-execution/
Or my extended version here
https://github.com/AaronLayton/H5BP-Core
My way lets you do page specific code, but allows you to keep all of the Javascript in separate pages so each page becomes easily manageable. The final step would be to concatenate and minify all the JS into 1 file ;-)

Razor Nested Layouts with Cascading Sections

I have an MVC3 site using Razor as its view engine. I want my site to be skinnable. Most of the possible skins are similar enough that they can derive from a shared master layout.
Therefore, I am considering this design:
However, I would like to be able to call RenderSection in the bottom layer, _Common.cshtml, and have it render a section that is defined in the top layer, Detail.cshtml. This doesn't work: RenderSection apparently only renders sections that are defined the next layer up.
Of course, I can define each section in each skin. For instance, if _Common needs to call RenderSection("hd") for a section defined in Detail, I just place this in each _Skin and it works:
#section hd {
#RenderSection("hd")
}
This results in some duplication of code (since each skin must now have this same section) and generally feels messy. I'm still new to Razor, and it seems like I might be missing something obvious.
When debugging, I can see the complete list of defined sections in WebViewPage.SectionWritersStack. If I could just tell RenderSection to look through the entire list before giving up, it would find the section I need. Alas, SectionWritersStack is non-public.
Alternatively, if I could access the hierarchy of layout pages and attempt execution of RenderSection in each different context, I could locate the section I need. I'm probably missing something, but I don't see any way to do this.
Is there some way to accomplish this goal, other than the method I've already outlined?
This is in fact not possible today using the public API (other than using the section redefinition approach). You might have some luck using private reflection but that of course is a fragile approach. We will look into making this scenario easier in the next version of Razor.
In the meantime, here's a couple of blog posts I've written on the subject:
http://blogs.msdn.com/b/marcinon/archive/2010/12/08/optional-razor-sections-with-default-content.aspx
http://blogs.msdn.com/b/marcinon/archive/2010/12/15/razor-nested-layouts-and-redefined-sections.aspx
#helper ForwardSection( string section )
{
if (IsSectionDefined(section))
{
DefineSection(section, () => Write(RenderSection(section)));
}
}
Would this do the job ?
I'm not sure if this is possible in MVC 3 but in MVC 5 I am able to successfully do this using the following trick:
In ~/Views/Shared/_Common.cshtml write your common HTML code like:
<!DOCTYPE html>
<html lang="fa">
<head>
<title>Skinnable - #ViewBag.Title</title>
</head>
<body>
#RenderBody()
</body>
</html>
In ~/Views/_ViewStart.cshtml:
#{
Layout = "~/Views/Shared/_Common.cshtml";
}
Now all you have to do is to use the _Common.cshtml as the Layout for all the skins. For instance, in ~/Views/Shared/Skin1.cshtml:
#{
Layout = "~/Views/Shared/_Common.cshtml";
}
<p>Something specific to Skin1</p>
#RenderBody()
Now you can set the skin as your layout in controller or view based on your criteria. For example:
public ActionResult Index()
{
//....
if (user.SelectedSkin == Skins.Skin1)
return View("ViewName", "Skin1", model);
}
If you run the code above you should get a HTML page with both the content of Skin1.cshtml and _Common.cshtml
In short, you'll set the layout for the (skin) layout page.
Not sure if this will help you, but I wrote some extension methods to help "bubble up" sections from within partials, which should work for nested layouts as well.
Injecting content into specific sections from a partial view ASP.NET MVC 3 with Razor View Engine
Declare in child layout/view/partial
#using (Html.Delayed()) {
<b>show me multiple times, #Model.Whatever</b>
}
Render in any parent
#Html.RenderDelayed();
See the answer link for more use-cases, like only rendering one delayed block even if declared in a repeating view, rendering specific delayed blocks, etc.

Resources