ASP.NET MVC 3 send <head> before <body> renders - asp.net

I have asp.net mvc application, that uses razor view engine.
I want to send head to browser, before body renders, to start parallel loading css, js and images, linked in css. (You can see, how this technique works on SO in chrome developer tools - network for example)
I found question about it in asp.net web forms: Send head before body to load CSS and JS asap
I tried to use this solution, but it don't work.
For razor engine next sequense of steps is actual:
action returns view result
_ViewStart.cshtml executes (set ViewBag.Layout)
view executes from first line to last (with code inclusions and sections)
view engine checks ViewBag.Layout and if it found - executes Layout (with renderind body and sections)
I think that good solution is to divide step 3 into 3 parts:
generating content for Head and Css section
send to browser
generating other part of view
Other solution is to static include all.css and basic .js files in (without sections content, generated from view), send head, and then generate view (with generation FooterScript section).
In both ways I need to start execution from Layout page, not from view. For first: Layout (head) - view (sections) - layout (flush) - view (other) - layout (body). For second: Layout (head + flush) - view (all) + Layout (body).
My _Layout.cshtml file:
<html #Html.Raw(ViewBag.xmlns)>
<head>
<title>#ViewBag.Title</title>
#Html.Partial("_MetaTags")
<link href="#Url.ThemeCss("jquery-ui-1.8.18.custom.css")" rel="stylesheet" type="text/css" />
<link href="#Url.Css("masterPage.css")" rel="stylesheet" type="text/css" />
<link rel="shortcut icon" href="/favicon.ico"/>
#RenderSection("Css", required: false)
<script src="#Url.CommonScript("jquery.common.min.js")" type="text/javascript"></script>
<script src="#Url.Script("Master.js")" type="text/javascript"></script>
#RenderSection("Head", required: false)
</head>
<body>
<div id="pageWhithoutFooter">
<div id="main">
#RenderBody()
</div>
</div>
#RenderSection("FooterScript", required: false)
</body>
</html>
Howto?

Try putting your head section into a Partial view then call this in your controller:
PartialView("_PageHeader").ExecuteResult(ControllerContext);
Response.Flush();
// Generate model
return View(model);
Not tested this but I can't see why it wouldn't work.

Related

Cannot load jquery before other script

I need to load JQuery before anything else, so I created this code inside _Layout:
<!DOCTYPE html>
<html>
<body class="sidebar-enable" data-keep-enlarged="true">
<div class="wrapper">
#RenderBody()
</div>
<script src="~/js/jquery.min.js"></script>
#RenderSection("DataTableScript", required: false)
</body>
</html>
I have a ViewComponent called ProductsViewComponent, I load it inside a View called Home in this way:
#await Component.InvokeAsync("Products", new { date = "2018-09-05" })
Inside the ViewComponent I need to load the DataTableScript (which requires JQuery for works properly), so I created this logic inside the Default.cshtml (which is the html of ProductsViewComponent):
#{
Layout = "/Views/Shared/_LayoutViewComponent.cshtml";
}
#section DataTableScript{
<script src="~/js/vendor/jquery.dataTables.js"></script>
<script src="~/js/vendor/dataTables.bootstrap4.js"></script>
<script src="~/js/vendor/dataTables.responsive.min.js"></script>
<script src="~/js/vendor/responsive.bootstrap4.min.js"></script>
<script src="~/js/vendor/dataTables.buttons.min.js"></script>
<script src="~/js/vendor/buttons.bootstrap4.min.js"></script>
<script src="~/js/vendor/buttons.html5.min.js"></script>
<script src="~/js/vendor/buttons.print.min.js"></script>
<script src="~/js/vendor/dataTables.keyTable.min.js"></script>
<script src="~/js/vendor/dataTables.select.min.js"></script>
<script src="~/js/dataTable.js"></script>
}
I have specified another Layout because the ViewComponent is not able to render a section (see this question for further information)
inside the _LayoutViewComponent I placed this code:
#RenderBody()
#RenderSection("DataTableScript")
Essentially RenderBody calls _Layout and then the DataTableScript are loaded. But when I start the application I get the DataTableScript loaded before of JQuery and this is really weird because in the _Layout I specified to load the DataTableScript after JQuery.
You can use a partial to render your script tags.
Views/Shared/_DataTableScriptsPartial.cshtml
<script src="~/js/vendor/jquery.dataTables.js"></script>
<script src="~/js/vendor/dataTables.bootstrap4.js"></script>
<script src="~/js/vendor/dataTables.responsive.min.js"></script>
<script src="~/js/vendor/responsive.bootstrap4.min.js"></script>
<script src="~/js/vendor/dataTables.buttons.min.js"></script>
<script src="~/js/vendor/buttons.bootstrap4.min.js"></script>
<script src="~/js/vendor/buttons.html5.min.js"></script>
<script src="~/js/vendor/buttons.print.min.js"></script>
<script src="~/js/vendor/dataTables.keyTable.min.js"></script>
<script src="~/js/vendor/dataTables.select.min.js"></script>
<script src="~/js/dataTable.js"></script>
In your _Layout.cshtml declare a scripts section.
<!DOCTYPE html>
<html>
<head>
#RenderSection("head", required: false)
</head>
<body class="sidebar-enable" data-keep-enlarged="true">
<div class="wrapper">
#RenderBody()
</div>
<script src="~/js/jquery.min.js"></script>
#RenderSection("scripts", required: false)
</body>
</html>
In the page that uses your ViewComponent render the partial in the scripts section.
Products.cshtml
#{
Layout = "_Layout";
}
#await Component.InvokeAsync("Products", new { date = "2018-09-05" })
#section scripts {
#await Html.PartialAsync("_DataTableScriptsPartial")
}
You cannot specify a layout for a view component view. It's essentially a partial view. By the time the view component is processed the main layout is already set in stone. You should use a script loading library to conditionally include your additional scripts, and then externalize the JavaScript that serves your view component and have it run after the associated scripts are finished loading.
There's various different libraries/techniques you can use for this: CommonJS, AMD, RequireJS, etc. You'll need to do some research and figure out which is the best fit for you. However, this is a transformative thing. It will change the entire way you handle scripts with your application and might require some major restructuring of anything existing, as a result.
The easier approach is to simply just include the script in your main layout. If your layout includes this view component, then it's always going to need the scripts anyways, so just go ahead make it static. It doesn't have a the warm fuzzies of a completely self-contained unit of functionality that you're wanting your view component to be, but to get there is going to likely require far more effort that is warranted or reasonable for this one particular scenario.
For what it's worth, you can optimize things a bit by front-loading the scripts using the <link rel="preload" href="..." as="script" /> tag. This would go in your head, and prompt supporting browsers to go ahead and load in the script, without actually running it (which is the part that blocks rendering). Then, by the time you actually include the script before the closing body tag, it's likely already good to go, and the script tag will simply prompt the browser to run it.
UPDATE
One further options is to use a client-side library that supports "components". This would be a replacement for your view component, not something you'd add in addition to. However, the benefit of a client-side component is that you can contain all the JavaScript functionality in that and then the library simply runs over your document at the end and wires everything up. I personally like Vue, but there's other choices like React, Ember, Angular, etc. Consult with the documentation for each to evaluate if you might prefer this approach instead and which particular library you prefer. They all essentially do the same thing, but they each have their own unique ways of getting there.
I like Vue personally because it's light-weight and largely unobtrusive. It doesn't necessarily force you to do things in a certain way, so you can have a bit more freedom in that respect. Libraries like React and Angular tend to be more opinionated, and since they're geared more towards creating single page applications, can sometimes make it difficult to split responsibilities with server-side rendering such as Razor views. Just my opinion though.

Servicestack include _Layout.cshtml in Razor Content Page

How can I include _Layout.cshtml in Razor Content Page ?
For example I created two cshtml files in root of my project.
First file is _Layout.cshtml
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
<h1>Main Layout</h1>
<br>
<br>
#RenderBody();
</body>
</html>
Second File is Product.cshtml
#inherits ServiceStack.Razor.ViewPage
#{
Layout = "~/_Layout.cshtml";
}
<h1>Product Page</h1>
When I call http://localhost:6000/product
The result is in browser is
Product Page
but it should be
Main Layout
Product Page
Why ?
What's the problem ?
Layout names should be the name of the file not a path and you should never need to reference _Layout as it's the default.
Also if you want your Views and Content Pages to share the same _Layout.cshtml pages add them once to /Views/_Layout.cshtml or /Views/Shared/_Layout.cshtml instead.
If this is Self hosting HttpListener project you need to ensure all *.cshtml are set to Copy to Output Directory or the WebHostPhysicalPath references your project path.

Browser tab information for image created by servlet

I'm using a servlet to show images on page. The links looks like
http://webappname.com/getImage?p=imagename.jpg
But the resulting image in the browser tab shows
getImage (100x200)
I would like to output there more useful information like string, so how can I do it?
As the response is an image, it cannot contain additional information (such as the page title) as an HTML response could. One possible workaround to this would be to return a simple HMTL page with your image in it.
You could trigger then trigger the regular download (the existing behaviour) with an additional URL parameter. So for example:
http://webappname.com/getImage?p=imagename.jpg
Would return this HTML:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" type="text/css" src="img-dl-style.css">
<script type="application/javascript">
// JavaScript to trigger image download when #dlButton is clicked
</script>
<title>Full Image Name</title>
</head>
<body>
<div class="container">
<h1>Full Image Name</h1>
<img src="http://webappname.com/getImage?p=imagename.jpg&dl=1>
<button id="dlButton" type="button">Download this image</button>
</div>
</body>
</html>
Any direct access to this URL:
http://webappname.com/getImage?p=imagename.jpg&dl=1
Would return the image directly, complete with the default browser title.
To see this in action, take a look at Dropbox's shared file viewer. A link such as:
https://www.dropbox.com/s/qmocfrco2t0d28o/Fluffbeast.docx
Returns a full webpage with a preview of the shared file and a download button. Clicking the button triggers the download with this URL:
https://www.dropbox.com/s/qmocfrco2t0d28o/Fluffbeast.docx?dl=1

Best Approach: Mutliple content place holders in MVC

I am converting an ASP.Net web forms project to MVC3. The master page contains multiple content place holders at different locations.
I replaced the first content place holder with #RenderBody() but I am confused with what to do of the second one.
One approach might be to separate views and place a #Html.RenderAction() for each content place holder method. Is there a better way to do it?
Razor has got sections understanding in place of asp.net webforms ContentPlaceHolders. Take a look at this introductionary link.
You can use sections. For example, to have a section for scripts, in the head tag of the layout.cshtml, you can specifiy
<head>
<title>#ViewBag.Title</title>
<link href="#Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
<script src="#Url.Content("~/Scripts/jquery-1.4.4.min.js")" type="text/javascript"></script>
#RenderSection("scripts", false);
</head>
Inside of any view, you can now add a scripts section to inject your scripts:
#section scripts{
<script src="#Url.Content("~/Scripts/myscript.js")" type="text/javascript"></script>
}
the "false" param tells MVC to render the section if exists on the child page or do nothing if no call

ASP.NET MVC 3: ViewModel for master template?

When I created my first empty MVC 3 project, this is the /Views/Shared/_Layout.cshtml file's contents:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>#ViewBag.Title</title>
<link href="#Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
<script src="#Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/modernizr-1.7.min.js")" type="text/javascript"></script>
</head>
<body>
#RenderBody()
</body>
</html>
The first thing I notice is that the <title>#ViewBag.Title</title>. The last thing I want to do is be using a dynamic "ViewBag" instead of a strongly-typed ViewModel.
How do I change my _Layout.cshtml so that the master template uses a strongly-typed ViewModel instead?
You can easily use #model to define the model type, but who will set the master pages's model or create it? The controller handles its own view and the model for the master page will be different based on the model created in each controller.
So, you may want to make all the models you pass to the views inherit from a base model class that has the properties required for master page and make the master page have the base type as the model type, but it'll be too ugly.
I'd suggest live with it for the simple cases like title here, if you find that you do a lot of stuff in there, have your own controller, action and view that perform the shared parts, and call Html.RenderAction() in the master page to get that executed.
http://gurustop.net

Resources