I'm developing in asp.net using Master/content pages. I'm using Bundling by adding it to the Master page. I think I have everything set up correctly; however, when I'm developing I can no longer see my list of available classes on the content pages if I'm trying to set up html......without bundling a list of classes used to pop up when I'd get to the class= part; but with bundling nothing appears.
My setup looks like this, in the Master page I'm referencing the bundles:
<head runat="server">
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title><%: Page.Title %> - JCPS DMC</title>
<link href="~/favicon.ico" rel="shortcut icon" />
<asp:PlaceHolder ID="PlaceHolder2" runat="server">
<%: Styles.Render("~/bundles/bootstrapCSS", "~/bundles/utilitiesCSS") %>
<%: Scripts.Render("~/bundles/utilitiesJs", "~/bundles/modernizr") %>
</asp:PlaceHolder>
<asp:ContentPlaceHolder ID="contentHeader" runat="server">
</asp:ContentPlaceHolder>
</head>
And my BundleConfig.cs file looks like this:
using System.Web.Optimization;
namespace DMC
{
public class BundleConfig
{
// For more information on Bundling, visit http://go.microsoft.com/fwlink/?LinkId=254726
public static void RegisterBundles(BundleCollection bundles)
{
bundles.Add(new ScriptBundle("~/bundles/WebFormsJs").Include(
"~/Scripts/WebForms/WebForms.js",
"~/Scripts/WebForms/WebUIValidation.js",
"~/Scripts/WebForms/MenuStandards.js",
"~/Scripts/WebForms/Focus.js",
"~/Scripts/WebForms/GridView.js",
"~/Scripts/WebForms/DetailsView.js",
"~/Scripts/WebForms/TreeView.js",
"~/Scripts/WebForms/WebParts.js"));
bundles.Add(new ScriptBundle("~/bundles/MsAjaxJs").Include(
"~/Scripts/WebForms/MsAjax/MicrosoftAjax.js",
"~/Scripts/WebForms/MsAjax/MicrosoftAjaxApplicationServices.js",
"~/Scripts/WebForms/MsAjax/MicrosoftAjaxTimer.js",
"~/Scripts/WebForms/MsAjax/MicrosoftAjaxWebForms.js"));
bundles.Add(new ScriptBundle("~/bundles/utilitiesJs").Include(
"~/Scripts/jquery-{version}.js",
"~/Scripts/bootstrap.js",
"~/Scripts/common.js",
"~/Scripts/analytics.js"));
bundles.Add(new ScriptBundle("~/bundles/highCharts").Include(
"~/Scripts/highcharts/4.2.0/highcharts.js",
"~/Scripts/highcharts/4.2.0/highcharts-more.js",
"~/Scripts/highcharts/4.2.0/highcharts-3d.js",
"~/Scripts/highcharts/4.2.0/modules/broken-axis.js",
"~/Scripts/highcharts/4.2.0/modules/data.js",
"~/Scripts/highcharts/4.2.0/modules/drilldown.js",
"~/Scripts/highcharts/4.2.0/modules/exporting.js",
"~/Scripts/highcharts/4.2.0/modules/funnel.js",
"~/Scripts/highcharts/4.2.0/modules/heatmap.js",
"~/Scripts/highcharts/4.2.0/modules/no-data-to-display.js",
"~/Scripts/highcharts/4.2.0/modules/offline-exporting.js",
"~/Scripts/highcharts/4.2.0/modules/solid-gauge.js",
"~/Scripts/highcharts/4.2.0/modules/treemap.js",
"~/Scripts/highcharts/4.2.0/adapters/standalone-framework.js"
));
// Use the Development version of Modernizr to develop with and learn from. Then, when you’re
// ready for production, use the build tool at http://modernizr.com to pick only the tests you need
bundles.Add(new ScriptBundle("~/bundles/modernizr").Include(
"~/Scripts/modernizr-*"));
bundles.Add(new StyleBundle("~/bundles/bootstrapCSS").Include(
"~/Content/bootstrap.css",
"~/Content/bootstrap-multiselect.css",
"~/Content/bootstrap-navbar.css",
"~/Content/bootstrap-overrides.css"));
bundles.Add(new StyleBundle("~/bundles/utilitiesCSS").Include(
"~/Content/print.css",
"~/Content/common.css",
"~/Content/site.css",
"~/Content/colorThemes.css"));
BundleTable.EnableOptimizations = false;
}
}
}
That last line in the BundleConfig.cs file I thought was the key to debugging:
BundleTable.EnableOptimizations = false
I thought that if it was set to false, it overrode the web.config file and allowed you to see classnames...is this incorrect? Am I missing something else?
Here is the documentation for the property: BundleTable.EnableOptimizations Property. Enabling it will bundle and minify your scripts/css files, see Bundling and Minification for complete details. This provides for faster load times for your clients as there is less that is transmitted over the wire (less white space, line breaks etc). It makes client side script debugging more difficult because your whole script could be on a single line and your variable names in javascript are usually also shortened. It has nothing to do with server side code or the web.config.
No. You are not missing anything. According to MSDN you can also use,
<system.web>
<compilation debug="true" />
<!-- Lines removed for clarity. -->
</system.web>
Documentation clearly says that,
Unless EnableOptimizations is true or the debug attribute in the
compilation Element in the Web.config file is set to false, files
will not be bundled or minified.
Link: http://www.asp.net/mvc/overview/performance/bundling-and-minification
Related
I have a c# .NET 6 app. Some of the web pages (razor pages) of the app are using dependency injection, injecting configuration into the razor pages (the .cshtml files) so that some config elements can be displayed in the UI.
My question is, is it possible to inject configuration in a similar way to a .css file? Is it possible to have something similar to this in a .css file:
/**mycssfile.css**/
#using Microsoft.Extensions.Configuration
#inject IConfiguration Configuration
.mystyle {
background-image: Configuration.image;
}
You might be looking for the #import keyword, if you are just looking to bring in rules from other style sheets.
The #import CSS at-rule is used to import style rules from other valid stylesheets. An #import rule must be defined at the top of stylesheet, before any other at-rule (except #charset and #layer) and style declaration, else it will be ignored.
https://developer.mozilla.org/en-US/docs/Web/CSS/#import
However, if you are trying to bring in values, strings, or any other kind of data from C# (which you appear to want in your example), you’re going to have to dynamically build the CSS file content in an endpoint on your server (where you have access to the C# values), then send that down to the browser where it acts like just another style sheet.
The issue is, once the browser parses CSS, it does not have access to your server’s code, because it’s already been downloaded.
Although, the functionality I just described may already be available to use as a built-in feature of the .NET framework, otherwise, you’ll have to code it yourself in .NET.
CSS files are plain text and are not processed by ASP.NET Core. Consequently, it is not possible to execute server-side code within them. However, you can easily use a Razor page to generate your CSS file, which will support DI etc (although you likely lose any CSS support offered by your IDE).
Here's the PageModel for an example. Note that the ContentType for the Response has been altered:
public class CssModel : PageModel
{
public string TextColor => "green";
public void OnGet()
{
Response.ContentType= "text/css; charset=utf-8";
}
}
Here's the .cshtml file content. The Layout has been set to null:
#page
#model WebApplication3.Pages.CssModel
#{
Layout = null;
}
body{
font-family:consolas;
color: #Model.TextColor;
}
Here's an example of a page that uses the generated CSS. Note here that the script is being output in a section:
#page
#model CssTestModel
#{
}
<h1>Hello World!</h1>
<p>This page tests Razor generated CSS</p>
#section styles{
<link rel="stylesheet" href="/css" />
}
And here's the layout page's head section that includes the necessary RenderSection call:
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>#ViewData["Title"]</title>
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
#RenderSection("styles", false)
</head>
I am using BundleTransformer to bundle and minify JS/CSS in a legacy ASP.NET WebForms app. However, I'd like to disable bundling if <compilation debug="true"> in Web.config.
Current Behavior: If debug=true, resources are still bundled together, but nothing is minified.
Desired Behavior: Bundling and minification only occur if debug=false. Otherwise, references to the individual scripts and stylesheets are rendered in the HTML.
Is this possible?
Below is an example of how I currently create one of the bundles:
BundleConfig.cs
var jsMinifier = new BundleTransformer.JsMin.Minifiers.CrockfordJsMinifier();
var customJsTransformer = new BundleTransformer.Core.Transformers.ScriptTransformer(jsMinifier);
var CustomJsBundle = new BundleTransformer.Core.Bundles.CustomScriptBundle("~/bundles/CustomJsBundle");
foreach (var item in jsFiles)
{
CustomJsBundle.Include(item);
}
CustomJsBundle.Transforms.Clear();
CustomJsBundle.Transforms.Add(customJsTransformer);
BundleTable.Bundles.Add(CustomJsBundle);
Navigation.master
<%# Master Language="C#" CodeFile="Navigation.master.cs" Inherits="Main_Nav" %>
<%# Import Namespace= "System.Web.Optimization" %>
<html>
<head runat="server">
<%= Scripts.Render("~/bundles/CustomJsBundle") %>
<!-- if debug=true, I'd like the server to just render references to the individual script files
instead of the bundle -->
</head>
<body>
<!-- other markup -->
</body>
</html>
The Scripts.Render call in Navigation.master always displays a bundle, even if debug is true. I'd like it to render the bundle only if debug is false. Otherwise, I'd like script tags to each individual script file rendered instead for debugging purposes.
I solved this for now by using the RenderFormat method when in DEBUG mode. Otherwise, I use the usual Render method.
For example, in my Navigation.master:
<% #if DEBUG %>
<%= Scripts.RenderFormat("<script src='{0}'/></script>", "~/bundles/CustomJsBundle") %>
<% #else %>
<%= Scripts.Render("~/bundles/CustomJsBundle") %>
<% #endif %>
This has the desired effect of:
In DEBUG mode, render script tags with the individual files.
Otherwise, a single script tag with the bundle is rendered.
I'm not sure if there is a better way to do this, but if so, please comment or submit an answer. I tried other potential solutions, such as setting EnableOptimizations to false, and clearing the BundleTable, but this is the only solution that has worked.
I'm trying to implement popouts but it seems like poppers.js doesn't want to load before bootstrap.js. I've tried to place the bundle before the bootstrap one but it doesn't want to load.
BundleConfig:
public static void RegisterBundles(BundleCollection bundles)
{
bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
"~/Scripts/jquery-{version}.js"));
bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
"~/Scripts/jquery.validate*"));
// Use the development version of Modernizr to develop with and learn from. Then, when you're
// ready for production, use the build tool at https://modernizr.com to pick only the tests you need.
bundles.Add(new ScriptBundle("~/bundles/modernizr").Include(
"~/Scripts/modernizr-*"));
bundles.Add(new ScriptBundle("~/bundles/popper").Include(
"~/Scripts/popper.js"
));
bundles.Add(new ScriptBundle("~/bundles/bootstrap").Include(
"~/Scripts/bootstrap.js"
));
bundles.Add(new StyleBundle("~/Content/css").Include(
"~/Content/bootstrap.css",
"~/Content/xbbcode.css",
"~/Content/site.css"));
}
_Layout:
#Scripts.Render("~/bundles/popper")
#Scripts.Render("~/bundles/jquery")
#Scripts.Render("~/bundles/bootstrap")
#RenderSection("scripts", required: false)
I still get this error:
jQuery.Deferred exception: Bootstrap's tooltips require Popper.js (https://popper.js.org/) Tooltip#http://localhost:56609/Scripts/bootstrap.js:2836:15
Popover#http://localhost:56609/Scripts/bootstrap.js:3509:14
_jQueryInterface/<#http://localhost:56609/Scripts/bootstrap.js:3569:18
each#http://localhost:56609/Scripts/jquery-3.3.1.js:354:10
each#http://localhost:56609/Scripts/jquery-3.3.1.js:189:10
_jQueryInterface#http://localhost:56609/Scripts/bootstrap.js:3559:14
load/http://localhost:56609/:299:46
mightThrow#http://localhost:56609/Scripts/jquery-3.3.1.js:3534:21
resolve/http://localhost:56609/Scripts/jquery-3.3.1.js:3602:12
undefined jquery-3.3.1.js:3818:3
TypeError: Bootstrap's tooltips require Popper.js (https://popper.js.org/)
This was doing my head in for about 30 mins but i managed to solve it. All i did was add the UMD version of popper.js and bundled it with the bootstrap.js.
What is UMD
The UMD pattern typically attempts to offer compatibility with
the most popular script loaders of the day (e.g RequireJS amongst
others). In many cases it uses AMD as a base, with special-casing
added to handle CommonJS compatibility.
So your BundleConfig.cs will look like this...
bundles.Add(new ScriptBundle("~/bundles/bootstrap").Include(
"~/Scripts/umd/popper.js",
"~/Scripts/bootstrap.js"));
I also tried bundling it on its own and it worked.
bundles.Add(new ScriptBundle("~/bundles/popper").Include(
"~/Scripts/umd/popper.js"));
Just remember to add it to your _Layout.cshtml. Also, i think order matters so try this order instead.
#Scripts.Render("~/bundles/jquery")
#Scripts.Render("~/bundles/popper")
#Scripts.Render("~/bundles/bootstrap")
#RenderSection("scripts", required: false)
So jQuery first, popper and then bootstrap. Hope this helps
I solved by adding the bootstrap.bundle.js which includes popper.js within.
If you are using a ScriptManager component and don't want to use bundles, you can reference the script js files directly with the Path attribute:
<asp:ScriptReference Name="jquery" />
<asp:ScriptReference Path="~/Scripts/umd/popper.min.js" />
<asp:ScriptReference Name="bootstrap" />
<asp:ScriptReference Path="~/Scripts/jquery-ui-1.12.1.js" />
I have the following situation on my new ASP.Net page:
I am using a master page
I am using themes
I have pages in separate folders
I need to reference a favicon from my master page based on the current theme.
Unfortunately the ~App_Themes/Basic/Images/favicon.ico path resolves to http://example.com/folder/App_Themes/Basic/Images/favicon.ico.
How can I uniformly refer to my favicon.ico located in the App_Themes/Basic/Images/favicon.ico path from master page used by the following differently located pages:
~/Home.aspx
~/Secure/Dashboard.aspx
~/Accounts/Login.aspx
Usually ASP.NET themes are limited to skin files and CSS files with all images referenced from the CSS file. In that scenario, the paths to images are relative from the CSS file.
If you need a path to a file inside the current theme's folder relative from a page, you can use the Page.Theme property combined with the Page.ResolveUrl() method:
<%= Page.ResolveUrl(String.Format("~/App_Themes/{0}/Images/favicon.ico", Page.Theme)) %>
If you want to use that in a <link rel="shortcut icon"> element you can just put the code above inside the href attribute. Unless you have a <head runat="server">, in which case ASP.NET may throw an HttpException:
The Controls collection cannot be modified because the control
contains code blocks (i.e. <% ... %>).
This can be fixed by putting the <link> element inside an <asp:PlaceHolder> control:
<head runat="server">
<asp:PlaceHolder runat="server">
<link rel="shortcut icon" href="<%= ... %>" />
</asp:PlaceHolder>
</head>
I'm attempting to precompile a few master pages (not update-able) to share them across multiple applications. The project I'm precompiling is a Web Site. The project that references precompiled assemblies is a Web Application. However, I'm getting a Could not load type 'ASP.xxx_master' every time i try to reference the master page from the client.
<%# Master Language="C#" Inherits="ASP.sitebase_master" %>
My precompiled master page looks like this.
<%# Master Language="C#" ClientIDMode="Static" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org /TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="AspNetHead" runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<!--[if IE]><meta http-equiv="X-UA-Compatible" content="IE=7" /><![endif]-->
<asp:ContentPlaceHolder ID="MetaContent" runat="server" />
<title>Web Portal</title>
<link href="/media/css/style.css" rel="stylesheet" type="text/css" />
<link href="/media/js/plugins/colorbox/colorbox.css" rel="stylesheet" type="text/css" />
<asp:ContentPlaceHolder ID="StyleContent" runat="server" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js" type="text/javascript" language="javascript"></script>
<script src="/media/js/plugins/colorbox/jquery.colorbox-min.js" type="text/javascript" language="javascript"></script>
<script src="/media/js/plugins/filestyle/jquery.filestyle.min.js" type="text/javascript" language="javascript"></script>
<script src="/media/js/portal.master.js" type="text/javascript" language="javascript"></script>
<script language="javascript" type="text/javascript">
PORTAL.debug.init();
PORTAL.init();
</script>
<asp:ContentPlaceHolder ID="ScriptContent" runat="server" />
</head>
<body>
<div id="hld">
<div class="wrapper">
<form id="AspNetForm" runat="server">
<asp:ContentPlaceHolder ID="BodyContent" runat="server" />
</form>
<asp:ContentPlaceHolder ID="FooterContent" runat="server" />
</div>
</div>
</body>
I'm stumped. No idea why the type isn't resolved. Anybody got suggestions? Both projects (precompiled web site and client web application) are built for ASP.NET 4.0.
EDIT: Here is the list of dependencies of the precompiled assembly. No 3rd party references.
mscorlib,
System,
System.Web
UDPATE 1
Well, the quick fix to this issue is to specify the full path to the master page.
<%# Master Language="C#" Inherits="ASP.sitebase_master, App_Web_sitebase.master.cdcab7d2" %>
After doing that, I'm receiving the following error:
An error occurred while try to load the string resources (FindResource failed with error -2147023083).
After doing some research, this appears to be related to the way HTML markup is parsed within the master page. Not entirely sure yet. I haven't dug much deeper into it. Overall, I can't believe this is the recommended way to share controls as it is absolutely, mindbogglingly idiotic.
UPDATE 2
I couldn't make anything of value out of this. It appears to be hating "script" tags in the head section, but I don't know why. The Master Page works great with a single script include. As soon as i start adding more I keep getting that error. After wasting a full day on this I ended up submitting a bug report to Microsoft. If anyone wants to bump it, please do.
UPDATE 3
I spent a few more days debugging this after no response from MS. Here is my findings. I initially thought that the code generated by CodeDOM provider is looking for a .NET resource that somehow did not get embedded in the assembly when it was published. I was wrong. After some investigation it appears that what's happening is after the Master Page reaches a certain size, a chunk of it is stored in the Resource Table in PE Data Directories section of the assembly. In fact, after looking at the generated assembly in PE resource viewer, i was able to confirm this by finding all my script includes in the Resource Table. Now, here is the actual problem. What's happening is that the CodeDOM provider generates a call to Win32 FindResource to pull that resource from the Resource Table. However, FindResource doesn't work on assemblies in memory, only on disk. So it fails with the above exception. I'm getting close, but still no workaround.
I finally have a workaround. It's not pretty, but it resolves the issue. Apparently using LoadControl to pre-load precompiled MasterPages loads all of the resources that FindResource cannot find otherwise. So, here is all i did to make this work.
In my client application I created a dummy master page (i.e. Dummy.Master) which references my precompiled master page like so:
<%# Master Language="C#" Inherits="ASP.sitebase_master, App_Web_sitebase.master.cdcab7d2" %>
Now, any .aspx page which references Dummy.Master needs to preload the precompiled MasterPage type like so:
protected override void OnPreInit(EventArgs e)
{
ASP.sitebase_master mp = (ASP.sitebase_master)Page.LoadControl(typeof(ASP.sitebase_master), null);
base.OnPreInit(e);
}
I don't know why this works, but it does. This code must run before the MasterPage is resolved, so PreInit worked great. After glancing at the .NET code for a few seconds in Reflector, it appears that LoadControl actually does some assembly compiling voodoo when it attempts to load a certain control type. So perhaps something in there loads that PE resource data section. The best place to put it would be in the base class I suppose which all pages could inherit from. Also, each loaded control (master page in this case) ought to be cached. Here is a good article explaining just that.
Hopefully this helps someone as much as it helped me. It was a pretty big show stopper for me.