Conditional section in MVC - c#

I have a twin Layout setup that renders either the entire page (with navigation) or just the contents depending on whether it was an Ajax request or not. I achieve this like so:
_LayoutRightPanel.cshtml
#{
Layout = !Request.IsAjaxRequest() ? "~/Views/Shared/_Layout.cshtml" : null;
}
This works fine for most pages, but pages that are requested via Ajax that contains #section{}s dont work (they do when loaded normally). I get the following error:
The following sections have been defined but have not been rendered
for the layout page "~/Views/Shared/_LayoutRightPanel.cshtml":
"scripts".
At the bottom of my _LayoutRightPanel I do the following to render scripts, either forcing the section to render on the main layout or rendering it on the current page if its an ajax request
#if (Request.IsAjaxRequest())
{
RenderSection("scripts", false);
}
else{
#section scripts{
#RenderSection("scripts", false)
}
}

#section scripts{
#RenderSection("scripts", false)
}
Why are you rendering the scripts section inside of the scripts section? I don't see how that can end in any way but errors or infinite recursion (which ends in an error).
Edit This is incorrect. See my update below.
RenderSection() should be used on the _Layout page (or any page that is wrapped around the current view). This is the target location (where you want the section to end up)
#section scripts{} should be used on your (Partial)View. This is the source location (where you define the section's contents).
Having both references to the same section on the same page defeats the purpose of why you should be using sections in the first place (to pass certain HTML parts to other, wrapping views (e.g. _Layout.cshtml))
Slight update
To directly answer your question, I think the error is thrown because the RenderSection call that's inside of the #section cannot find any section called "scripts" because it has not yet been defined (you're in the process of defining it).
Update
According to this SO question, duplicate names should not be an issue (so you can disregard my comment below).
However, the root cause of the issue (in that question) was that the script sections weren't chained (i.e. you can't render the section 2 layout pages up, it has to be in the specific Layout page of your view (and then keep chaining it upwards to the next Layout page).
Can you confirm that to be set up as intended?
Maybe offtopic, but is there any added value to passing the scripts section to the upper Layout page? Since you're going to render the section either way, should it not work if you always place it in the _LayoutRightPanel page?

Related

How to pass a section to a partial in layout?

In ASP.net MVC 5 We can pass section to layout using
#section AnySection{
//section code here
}
and render it in Layout by
#RenderSection("AnySection", required: false)
But how can we pass that section again to a partial inside that layout? See the image below for reference
subheader-v1 is a partial view inside my Layout I have made my Layout with many partial Views.
When I try this as mentioned in Image above it gives me this error
The file "~/Views/Shared/partials/_subheader/subheader-v1.cshtml" cannot be requested directly because it calls the "RenderSection" method.'
You don't pass a section to the layout. It is the layout that determines which sections should (or could) be rendered in the view... it also determines where in the view the section should be rendered.
From MS Documentation:
A layout can optionally reference one or more sections, by calling
RenderSection. Sections provide a way to organize where certain page
elements should be placed.
Sections don't work in partial views and that is by design. You would need to move RenderSection to your layout and the section body to your view. See this question for more information.
Inside the subheader-v1.cshtml (since the objective is to bring the "CoordinatesSelection" partial into subheader-v1.cshtml) substitute
#RenderSection("Coordinates", required: false)
FOR
#Html.Partial("CoordinatesSelection", Model)
The code #RenderSection("Coordinates", required: false) was designed to be written directly in the layout to avoid duplicate calls to it!!
Two ways:
1.
You can migrate from subheader-v1 to layout
layout:
// your subheader-v1 code
#RenderBody()
And you can use RenderSection in the layout
2.
You can pass a model to Partial subheader-v1
*layout:
#Html.Partial("partials/_subheader/subheader-v1",RenderSection("Coordinates",false))
#RenderBody()
*subheader-v1:
#model object
.
.
.
<div class="kt-subheader__wrapper">
#Html.Raw(Model)
*SelectClosetStore
#section Coordinates{
//your partial code
}
I think the first way is better than the second way.

What is the execution order of an MVC Razor view/layout

I have a razor layout like:
#using (var context = SetUpSomeContext()) {
<div>
Some content here
#RenderBody();
</div>
}
And a view like:
#{
Layout = "MyLayout.cshtml";
}
<div>#SomethingThatDependsOnContextBeingSetUp()</div>
When the view renders, SomethingThatDependsOnContextBeingSetUp executes before SetUpSomeContext and fails. This seems weird, because I would expect that not to execute until RenderBody is called in the layout. When I switch this to use a "PageContent" section instead of RenderBody, everything works as expected. Can anyone explain this behavior?
The Razor pipeline is:
First, Razor evaluates, if present, _ViewStart.cshtml that contains only Razor statements (C# or VB) for assign Layout or other initialization, it should never have html tags inside.
Then, it parse and evaluates the "View" cshtml file.
Then, it parse and evaluates, if present, the Layout, and when evaluates the #RenderBody method of the cshtml layout file, replaces it with the html script resulting from evaluation of "View" cshtml file.
Finally, it builds the html control graph objects of layout and view html files.
So, you cannot do depend any "Razor" objects of a view from layout operations, but rather you may put in _ViewStart.cshtml your initialization of objects visible to your view.
You may imagine cs(vb)html views as a static content loaded when Controller.View method is called.
At that point, the cshtml loaded content is parsed by Razor that evaluates the expressions (assign properties(as Layout), branchs, loops) and build a sort of binary tree or graph of "HtmlControls" objects into the ActionResult object returned by View method.
Next, ActionResult is rendered as html from Asp.Net and returned to the client as http response.
To do that, Razor parses cshtml files and carries out their code inside parts starting first from the "_ViewStart.cshtml" (also more of one if present in the sub folders chain related to the origin controller), then follows cshtml file loaded by conventions (name of view equals to the name of action in the path Views/[ControllerName]/), or by expressed view's name as parameter when calling View method, and finally, the eventual layout file linked to the view by Layout property.
Let me clarify this by investigating a situation,Assume that you have view like;
#renderSection("Header")
#using (var context = SetUpSomeContext()) {
<div>
Some content here
#RenderBody();
</div>
}
#renderSection("Footer")
And we are assuming that razor executes the page in the order you expect, what would happen if we declared our view like?
#{
Layout = null;
}
<div>#SomethingThatDependsOnContextBeingSetUp()</div>
Razor would not have any idea whether that view needs a layout page until executing #RenderBody().Also it would deduce that it rendered layout page for nothing and this would not be reasonable.So this is not what actually happens.
When request made it is so natural that Razor first executes body of your view.
If your view not specified layout like in my demo Razor only renders output of that page and stops there.If view has a layout specified like in your code
after executing the view , it passes control to layout page.(Layout page starts to render from top to bottom)So what is remaining for Layout page is only content placement.When it sees #RenderBody() it only places output of your already executed view.
For sections; they are not executed when your view body executed, after your view passes control to layout page,layout page explicitly invokes the execution of your sections in the order they are declared.
Also notice that you are specifying your page title in your view body and it is rendered in your layout title tag (ViewBag.Title).After executing view body all the variables which is declared in view body are available in layout page.
Sum:Rendering order is from top to bottom but execution order is different.
For your situation: "SomethingThatDependsOnContextBeingSetUp executes before SetUpSomeContext and fails". Like i said it is natural behaviour of Razor execution cycle, view body executed before layout page executed.When you make it section ; view body executed first but sections are not executed before layout page.View body passes control to layout page and Layout page starts to render from top to bottom and if it sees #RenderSection then invokes the execution of section.So in this case SetUpSomeContext is executed before SomethingThatDependsOnContextBeingSetUp executed.
The execution order is from innermost to outermost.
I would argue that using a 'context' the way you use it is not the best design - you should consider moving the setup to the controller / action filter and pass the data to the views in model.
If you require some logic on all your views, create a ViewModelBase that all your ViewModels inherit from.
Then in your Controller(Base) you can initialize the ViewModel.SharedContext and other properties.

HTML helpers in ASP.NET MVC 3 with Javascsript action

I have many HTML helper in Helpers.cshtml file, but some of the helper (html) need some jquery action, so how do i can call jquery inside helpers.cshtml, is that possible?
i know we can keep the js file in header or particular page, but i do not want to do like that, i want to use jquery or javascript only on the page which loaded particular helper.
anyone have idea on this?
My scenario is, i have list box control, that is properly loading from helper, but i need to apply custom theme to the list box.
Little more Clarity
//in index.cshtml
#Helpers.testListBox("mylist" "1,2,3,4,5,6,7")
//in Helpers.cshtml
#helper testListBox(string listName, string listData){
//...... HTML code .........
//Javascript here?
}
With Web Forms, the framework could automatically include Javascript (once) when certain server controls were used on a page; ASP.Net MVC has no such facility. It sounds like this is what you're missing.
The way to do it is on the client. Look at RequireJS at http://requirejs.org/. This is a client-side library for managing Javascript dependencies. This does what Web Forms did, but better, and it does more. Your master layout will have a script tag like this:
<script src="/Scripts/require.js" type="text/javascript" data-main="/Scripts/main"></script>
This can be the only script tag you include on every page. Everything else can be dynamically loaded only as needed by RequireJS. It's true that you load this on every page, but it's smaller than jQuery, and it earns its place because it does so much for you.
Using your example, let's say you have this markup:
#Helpers.testListBox("mylist" "1,2,3,4,5,6,7")
and it renders HTML and needs jQuery scripting. You would render this:
// HTML for list box here
<script type="text/javascript>
require(['jquery'], function($) {
// Do your jQuery coding here:
$("myList").doSomething().whatever();
});
</script>
The require function will load jQuery, unless it has already been loaded, and then execute your code. It's true that your jQuery snippet is repeated once per use of the HTML helper, but that's not a big deal; that code should be short.
RequireJS manages dependencies effectively; you can have module A, and module B which dependes on A, and module C which depends on B. When your client code asks for module C, A and B will be loaded along with C, and in the correct order, and only once each. Furthermore, except for the initial load of require.js, scripts are loaded asynchronously, so your page rendering is not delayed by script loading.
When it's time to deploy your site on the web server, there's a tool that will examine the dependencies among the Javascript files and combine them into one or a small number of files, and then minimize them. None of your markup has to change at all. While in development, you can work with lots of small, modular Javascript files for easy debugging, and when you deploy, they are combined and minimized for efficiency.
This is much better than what the web forms framework did, and entirely client-side, which in my opinion is where it belongs.
You can put a <script> tag in the helper body.
How about this for an example of a partial view:
#model Member.CurrentMemberModel
#{
var title = "Test View";
}
<script type="text/javascript">
// Javascript goes in here, you can even add properties using "#" symbol
$(document).ready(function () {
//Do Jquery stuff here
});
</script>
#if (currentMember != null)
{
<div>Hello Member</div>
}
else
{
<div>You are not logged in</div>
}

How do I add custom tags in the head of my razor views

I want to add custom javascript in the head of certain views. I have the normal structuring, with a shared folder that has a view called _Layout.cshtml. This file has the head tag in it and is the basic "master page" for all my views. I want a certain view to have certain javascript in the head tag, but have everything else the same, how do I accomplish this?
check out the #RenderSection method. you pass it a name and whether the section is required (yours would be false), and then in your views you set that section up using #Section.
to sum up, put something like this in your layout:
#RenderSection("OptionalContent", required: false)
and something like this in your view:
#section ExtraContent {
<div>Some extra content</div>
}

MVC3 Layout Page, View, RenderPartial and getting script files into the Header (from the partial view)

So I have a Layout page
<head>
#RenderSection("HeaderLast", required: false)
</head>
A view
#section HeaderLast
{
<script src="#Url.Content("~/Scripts/knockout-1.2.0.js")"
type="text/javascript"></script>
}
<div id="profile-tab">
#{ Html.RenderPartial("_userProfile"); }
</div>
And a Partial view
#section HeaderLast
{
<script type="text/javascript">
alert('test');
</script>
}
<div......
I figured it couldn't be that simple. Is there a proper way to do this out of box or will this always require some kind of mediator and passing stuff around ViewData to manually make the content bubble up to the layout page?
Bounty started: The bounty will be rewarded to the best solution provided for this short coming. Should no answers be provided I will award it to #SLaks for originally answering this question.
You cannot define sections in partial views.
Instead, you can put the Javascript in ViewBag, then emit any Javascript found in ViewBag in the layout page.
#JasCav: If a partial needs its own CSS, it has no good way to get it rendered.
If that's the reason for its use, it could very well be by design.
You don't want to have a separate CSS file x partial/helper. Remember, each separate CSS file means a separate request to get it from the server, thus an additional round-trip that affects time to render your page.
Also you don't want to emit direct CSS to the HTML from the partial/helper. Instead you want it to have appropriate hooks you can use to define all the look in your site's CSS file.
You can use the same hooks you have available for CSS to activate custom JavaScript behaviors for the elements involved When JavaScript is enabled.
Finally it may be the case what you need is not a Partial View, but an extra Layout you use for some pages. With that approach you would have:
A master Layout that gets set automatically on _ViewStart like you probably has now. This defines the sections like in your sample.
A children Layout page. Here you have both the extra html, css, js you need to have for these views. This uses both #RenderBody() and #section SomeSection { } to structure your common extra layout.
Some views that point to the children layout, and others that use the default master layout.
How to get extra data to the children Layout is out of the scope of the question, but you have several options. Like having a common base for your entities; using ViewBag or calling Html.RenderAction to get that shared logic related to shared dynamic elements in the layout.
It looks like there was a similar question on SO - How to render JavaScript into MasterLayout section from partial view?.
Unfortunately, there is no possibility of declaring sections inside Partial Views. That is because RenderPartial ends up rendering totally separate view page. There is a workaround to this, though a bit ugly. But it can look better if using strongly-typed model instead of ViewData.
Basically, you need to keep track of the reference to the view which called RenderPartial and use the DefineSection method on the object passed to push data to that view.
UPDATE: There is also a blog post about dealing with RenderSection you may find useful.
Here is another approach using helper methods and templated delegate
http://blogs.msdn.com/b/marcinon/archive/2010/12/15/razor-nested-layouts-and-redefined-sections.aspx
As a follow up to my question, the JavaScript/CSS combiner/minifier tool Cassette supports this functionality to allow you to compartmentalize your JavaScript and other assets that are required for partials.
I purchased a site license and use this in all of my MVC applications now.

Categories

Resources