Including scripts in sections in MVC EditorTemplate [duplicate] - c#

So I'm trying to create a custom editor so that for a DataType of "Duration" a textbox appears with a masked format of HH:MM:SS.
I've created a very simple piece of code so far
#Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue, new { #class = "text-box single-line", type = "duration" })
<script>
$(document).ready(function () {
$("##Html.NameFor(c => c)").mask("00:00:00");
});
</script>
This is in my ~/Views/Shared/EditorTemplates/Duration.cshtml file. However it requires an additional javascript to be loaded (maskedInput.js).
Is there any razor includes I can use here so that I can include the maskedInput.js file once and only once in a page load. I realise I could add it to the parent page the editor will be on (but that would require knowing every page where this editor is used). I could add it to the master layout view but this would mean overhead for the pages that don't use this editor.
So I suppose in summary all I'm asking is :- "Is there a way to include a javascript file once and only once from a EditorTemplate".

I wrote a nuget package exactly for this purpose and wrote the blog post that YD1m has referred to.
To use the package, first thing you need to do is add a call to Html.RenderScripts() somewhere in your layout so that all of the script file references and blocks added using the helpers during the rendering of a Razor view are outputted in the response. The typical place to put this call is after the core scripts in your top level layout. Here's an example layout:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>#ViewBag.Title</title>
<meta name="viewport" content="width=device-width">
</head>
<body>
#RenderBody()
<script src="~/Scripts/jquery-2.0.2.js"></script>
#* Call just after the core scripts in the top level layout *#
#Html.RenderScripts()
</body>
</html>
If you're using the Microsoft ASP.NET Web Optimization framework, you can use the overload of Html.RenderScripts() to use the Scripts.Render() method as the function for generating scripts:
#Html.RenderScripts(Scripts.Render)
With that done, now all you need to do in your editor template is use the helpers in the nuget package to add a reference to the script and add your code block
#Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue, new { #class = "text-box single-line", type = "duration" })
#using (Html.BeginScriptContext())
{
Html.AddScriptFile("~/Scripts/maskedInput.js");
Html.AddScriptBlock(
#<script>
$(document).ready(function () {
$("##Html.NameFor(c => c)").mask("00:00:00");
});
</script>);
}
The referenced script file will only be added once to the page and all of the script blocks will be written out at the end of Html.RenderScripts() call.
Using the helpers, you can add script files and script blocks in Views, Partial Views and Editor/Display Templates. Note that the current version (1.1.0.0) will not render out scripts using the helpers when called via AJAX but this is something that I'm looking to add in the next version.

Well, you can do following:
Add #RenderSection("MaskedInput", false) to your master layout. That'l render
#section MaskedInput{}
on each page that has that section;
On a page you need to add maskedInput.js you put:
#section MaskedInput
{
#*Include scripts, styles or whatever you need here*#
}

You can create singleton class with Dictionary property whic will store scripts from your custom helpers/templates.
In your templates you can call method, that put script in singleton dictionary property with some string key. In that method you can prevent adding scripts with same keys.
Finally you should to write a render action for rendering scripts from dictionary and call this action from your master layout.
Here you can find solution similar to mine:
Managing Scripts for Razor Partial Views and Templates in ASP.NET MVC

Related

Passing args from Razor Page to a Razor Component

The scenario
I've created a Razor Pages project and added server side blazor rendering so that I can add Razor Components. I am having an issue where _Layout.cshtml renders a razor component on the page and passes an argument to it, yet at runtime, the razor component appears to not receive an argument for this parameter.
Example with actual output
_Layout.cshtml
#using Microsoft.AspNetCore.Http
#inject IHttpContextAccessor HttpContextAccessor
<!DOCTYPE html>
<html lang="en">
<body>
IP ON GET WAS: #HttpContextAccessor.HttpContext.Connection.RemoteIpAddress
<component type="typeof(MyFormComponent)" IPAddress="#HttpContextAccessor.HttpContext.Connection.RemoteIpAddress" render-mode="ServerPrerendered"/>
</body>
</html>
When the website renders, the visitor's IP address is displayed directly above the component. (I'm doing this purely to prove to myself that _Layout.cshtml, the page rendering the razor component, does indeed have access to a concrete working child of IHttpContextAccessor.)
MyFormComponent.razor
#using System
<button type="button" #onclick="doStuff">
Click Me
</button>
#code
{
[Parameter]
public string IPAddress { get; set; }
private void doStuff()
{
Console.WriteLine("We've got the IP! -> " + this.IPAddress);
// prints: "We've got the IP! -> "
}
}
When the above button is clicked on the component, this.IPAddress is null. I've checked this value on a breakpoint while debugging and I've observed that the IP address is not printed to the console.
The expected output
I expected that my razor component would receive the argument value passed to it and print it to the console whenever the button is clicked. I expected that _Layout.cshtml would be able to pass this value to the razor component because it has proven that it does indeed have access to the desired value I want to pass by displaying the value on the page.
Closing notes
This was initially a Razor Pages project, created via dotnet new webapp.
Server side blazor was introduced to the project and has been working with great success, with this issue being the only exception so far. So, the project does have a _Host.cshtml and _Imports.razor file, along with the AddServerSideBlazor() call in Startup.ConfigureServices(), and the MapBlazorHub() endpoint added in Startup.Configure(). I didn't think it would be necessary to share the contents of these files for this specific problem. If I'm wrong about this, please, let me know!
You might notice that this issue specifically involves a reference to IHttpContextAccessor. If you were not aware, this specific class is NOT to be accessed directly from within a razor component for the reasons described here.
It appears that the solution to this problem is simply an alternate syntax being used to render the razor component in the _Layout.cshtml file.
By replacing (the current code)
<component type="typeof(MyFormComponent)" IPAddress="#HttpContextAccessor.HttpContext.Connection.RemoteIpAddress" render-mode="ServerPrerendered"/>
with the following (a new snippet):
#(await Html.RenderComponentAsync<MyFormComponent>(RenderMode.ServerPrerendered, new { IPAddress=HttpContextAccessor.HttpContext.Connection.RemoteIpAddress.ToString()}))
I was able to resolve this issue.

Rendering JavaScript file in _Layout.cshtml

I have a problem with rendering JavaScript file in _Layout.cshtml.
#section Scripts {
<script src="#Url.Content("~/Scripts/Custom/productsSuggests.js")"></script>
}
When I paste it to Index.cshtml (Home) it works, but only on this page. I need this script to work globally. I have partial view SearchBox in HomeViews catalog, and Controller Action in HomeController.
Because you are in the _Layout.cshtml view, it is likely the top level view. A section is a placeholder in a parent view.
Instead of your current code, try
#Scripts.Render("~/Scripts/Custom/productsSuggests.js")
In the Layout.cshtml you can use: #Scripts.Render("YOUR BUNDLES")
When will be add layout to another page this bundle will be global work.

Razor tag inside Html.Raw(...)

Is it possible to render Razor in Html.Raw()? I have a dynamic page being generated that uses the Html.Raw() method to render the page that is created in the controller. My raw html has razor tags in it. That is, I am trying to generate a link by
#Html.Raw(Model.myRawHtmlContainingRazorTags);
where Model.myRawHtmlContainingRazorTags contains
<html>
...
...
#Model.TheLink
...
...
<html>
I need the value of Model.TheLinkto be rendered when #Html.Raw(Model.myRawHtmlContainingRazorTags) is called within the cshtml.
You may have some architectural issues that lead you to have some razor code on your Model property.
Anyway, you can do it by using some external libs such RazorTemplates.
Here is a sample :
var template = Template.Compile(Model.myRawHtmlContainingRazorTags);
#Html.Raw(template.Render(Model));

MVC 4 _Layout.cshtml dynamic CSS

I have a problem where I am trying to have a default CSS page, but depending on certain aspects can have their CSS changed. Groups of people can have their CSS changed to their own custom version via database entries, they post a long string that has what the CSS needs to be set to. But, they can also do something simple and simply want to overwrite maybe just the background or the entire site.
<head>
<link href="/static/css/styles.css" rel="stylesheet" />
<style type="text/css">
#{
WebExtensionHelper.CustomCSS();
}
</style>
</head>
WebExtensionHelper.CustomCSS() returns a string with all the CSS in it, as stated previously. I need this to affect every page so I need this on the _Layout.cshtml page. Another thing to consider if it's helpful, I will have about 200 people who would like customcss.
One solution is to generate a custom CSS file at run time. In your layout you can point the stylesheet link to a controller action instead of an actual file:
<link href="#Url.Action("Index", "Style", new { whichStyle = myValue })" rel="stylesheet" type="text/css" />
In the Style controller, create an Index method:
public ActionResult Index(string whichStyle)
{
MyStyleViewModel model = new MyStyleViewModel();
// -- (Load relevant style settings here) --
Response.ContentType = "text/css";
return View(model);
}
And then in the Index view, write out css as normal:
#model MyStyleViewModel
#{ Layout = null; }
body
{
color:#333;
#if (Model.Font == "Lucida")
{
#:font-family:"Lucida Grande", sans-serif;
}
else
{
#:font-family:Georgia, Serif;
}
}
I haven't run that exact view code, so adjust as needed for your situation.
You could simply use LESS. It is supported in Visual Studio 2012 (and 2010) and MVC 4, so it is easy enough to use, and will do exactly what you want. It is perfect for your scenario.
Setting up LESS requires more work than I can put into an answer here, so as much as I hate to do this, here are two links:
http://geekswithblogs.net/ToStringTheory/archive/2012/11/30/who-could-ask-for-more-with-less-css-part-2.aspx
http://www.hanselman.com/blog/VisualStudio2012RCIsReleasedTheBigWebRollup.aspx
http://richardneililagan.com/2012/08/automatic-dotless-compile-less-css/
Once set up you can either compile on the server side with Dot LESS, as mentioned in the last link above, or send it to be processed on client side with javascript.
It should be
#WebExtensionHelper.CustomCSS();
not
#{
WebExtensionHelper.CustomCSS();
}
Sorry for those who tried to help me earlier, I was unable to copy and paste my code at the time, and noticed in my question that I answered my problem.

Why does my .cshtml page need to define content?

Let's say I have the following structure in my ASP.NET MVC 3 application.
Items
Index.cshtml
Categories
Shared
_Index.cshtml
_Site.cshtml
Index.cshtml
Both Index.cshtml files use _Index.cshtml as the layout page and _Index is nested within the _Site layout.
Items/Index implements the optional sections defined in _Index. Shared/Index is empty.
The Items/Index view works fine. Since Categories doesn't have an Index, it uses the one in the Shared folder. This does not work.
It throws the error
The "RenderBody" method has not been called for layout page "~/Views/Shared/_Index.cshtml".
If _Site calls RenderBody, and _Index inherits from _Site, doesn't the content in _Index satisfy the required RenderBody call and Shared/Index.cshtml can be blank?
The reason I ask is because I have an ASP.NET MVC 1 application that implemented this structure using Master pages and it worked fine, but converting it to MVC 3 with Razor is causing this issue.
Here is the basic outline of what I'm describing:
_Site.cshtml
<!DOCTYPE html>
// head
<body>
#RenderBody()
</body>
_Index.cshtml
#{
Layout = "~/Views/Shared/_Site.cshtml";
}
<div id="sub-menu">
// Markup
</div>
// More markup
#RenderSection("SectionOne", required: false)
#RenderSection("SectionTwo", required: false)
Items/Index.cshtml (Working)
#{
Layout = "~/Views/Shared/_Index.cshtml";
}
#section SectionOne {
// Markup
}
Shared/Index.cshtml (RenderBody error)
#{
Layout = "~/Views/Shared/_Index.cshtml";
}
// Rest of this file is empty
I'm not sure i follow you completely, but ALL layout pages have to have a RenderBody(), even if they're nested. RenderBody() renders the content for the "child". When you have nested layout pages the nested layout is the child of the parent, and it's output must be rendered in the RenderBody. Likewise, the child of the child has to render it's body into the middle page.
To put it another way, anything that's not in a #section is considered the "body". So, _Index.cshtml needs to render it's body (Index.cshtml) and _Site.html has to render it's body (_Index.cshtml). It goes up the chain.
EDIT:
It appears that a layout has to render at least one section, be it with a RenderBody() or a RenderSection(). While it may be true that the sections are optional, rendering at least one section is not. Either add an empty section to your Index.cshtml or add a RenderBody() to your _Index.cshtml.

Categories