asp.net mvc 5 - StyleBundle CdnFallbackExpression questions? - css

I've added bootstrap CSS files via a StyleBundle to my asp.net mvc 5 project.
(It uses as Cdn: https://www.asp.net/ajax/cdn#Bootstrap_Releases_on_the_CDN_14 )
var bootstrapCssCdnPath = "http://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/css/bootstrap.min.css";
var bootstrapCssBundle = new StyleBundle("~/bundles/css/bootstrap", bootstrapCssCdnPath).Include("~/Content/bootstrap.css");
//bootstrapCssBundle.CdnFallbackExpression // ?
bundles.Add(bootstrapCssBundle);
var bootstrapThemeCssCdnPath = "http://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/css/bootstrap-theme.min.css";
var bootstrapThemeCssBundle = new StyleBundle("~/bundles/css/bootstraptheme", bootstrapThemeCssCdnPath).Include("~/Content/bootstrap-theme.css");
//bootstrapCssBundle.CdnFallbackExpression // ?
bundles.Add(bootstrapThemeCssBundle);
However there is a problem: when I add an incorrect url as CDN (e.g. adding 'ahttp' instead of 'http') my custom "fallback" css file is not used, instead it shows 'ahttp://' in my html source.
Same when I'm running my site on Debug or Release.
Why is my fallback not being used?
What is the CdnFallbackExpression for a StyleBundle? (and in particular for a bootstrap.css and bootstrap-theme.css)
Should the .Include be the .min.css file or does it automatically search for the .min. version first?
Is there a way to .Include multiple css files, using a Cdn with fallback, so that I don't have to create a new StyleBundle everytime per css file that uses a Cdn?

1) This is a bug in the Microsoft ASP.NET Optimization Framework, documented here.
2) The solution is to modify the CdnFallbackExpression to be a javascript function that both checks for the stylesheet and loads the fallback, thus ignoring the bad script from the Optimization Framework.
Here is solution which provides a StyleBundle extension method to solve the problem: Style Bundle Fallback.
3) There should be unminified version like bootstrap.css (not bootstrap.min.css). When you build your web application for release it uses .min version. More here: Bundler not including .min files.
4) No, you can't use multiple CSS files with CDN (each of them must have its own bundle). Here is an article that explains when to use a CDN (or not) and why: Know When To CDN.

Related

Add PureCSS to MVC 4.5 bundle

I'm currently learning ASP.NET 4.5 of the MVC flavour, and I've decided to remove bootstrap completely and go with PureCSS (http://www.purecss.io).
This is largely due to the fact that my web application requires almost no scripting other than on the code-behind, and some light JS for data validation and the like.
Currently I'm linking to the combined PureCSS style sheet from the Yahoo! CDN:
<link rel="stylesheet" href="http://yui.yahooapis.com/pure/0.6.0/pure-min.css">
in my _Layout.cshtml file. This is obviously functional, however I have 2 concerns:
If the CVN (for whatever reason) fails/goes down/changes, all of the styling disappears and I'll have to solve that on the fly (or implement some time of failsafe switchover to another CDN)
I really like the concept of bundling and I'd like to have the local PureCSS library bundled, to prevent the aforementioned problem as well as for the sake of modularization/compartmentalization.
Is generating this bundle a simple matter of:
bundles.Add(new StyleBundle("~/Content/css").Include(
"~/Content/purecss_release_1_6/some.css",
"~/Content/purecss_release_1_6/other.css",
"~/Content/purecss_release_1_6/neat.css",
"~/Content/purecss_release_1_6/etc.css",
...
"~/Content/site.css"));
If so, that's fine and dandy, but there are DOZENS of css files in the release. Is there a cleaner way to bundle them?
Thank you!
You can use IncludeDirectory to reference the whole directory containing all your CSS files.
Example specific to your case:
bundles.Add(new StyleBundle("~/Content/css")
.IncludeDirectory("~/Content/purcss_release", "*.css"));
New in .NET 4.5 is an integrated system for falling back from a failed CDN to local material. Tutorial/information: http://www.hanselman.com/blog/CDNsFailButYourScriptsDontHaveToFallbackFromCDNToLocalJQuery.aspx
Usable information from the link above:
The basic idea for CDN fallback is to check for a type or variable
that should be present after a script load, and if it's not there, try
getting that script locally. Note the important escape characters
within the document.write. Here's jQuery:
<script src="http://ajax.aspnetcdn.com/ajax/jquery/jquery-2.0.0.min.js"></script>
<script>
if (typeof jQuery == 'undefined') {
document.write(unescape("%3Cscript src='/js/jquery-2.0.0.min.js' type='text/javascript'%3E%3C/script%3E"));
}
</script>

ASP.NET MVC - weird style being generated in release mode

I have this:
bundles.Add(new StyleBundle("~/Content/Styles/Default").Include("~/Content/Styles/Default/Site.css"));
On my sites i have this:
#section Styles
{
#Styles.Render("~/Content/Styles/Default"))
}
My _Layout.cshtml looks like this:
#RenderSection("Styles", true)
Everything looks good, eh? Well, not really. When i compiled my application in release mode, decided to publish it, this is what it renders:
<link href="/Content/Styles/Default?v=78dkNySP_xsiuzsgxCx_GGnnHzYS-B8nNdnXqcl47XI1" rel="stylesheet">
Instead of generating href to a file, it generates some kind of id? Guid? Why? O.o
This is how bundles work. It's main purpose is for you to combine multiple CSS (and JS files for that matter) files into one package. e.g. you no longer have to put all your css (and js) into one huge file. Just split it up into sections, then add it into your bundles, and it packages it up into one item. Less web requests, the faster your page load time.
e.g. Lets say you had 2 css files. One's the main, but you had one for your menu system.
bundles.Add(new StyleBundle("~/Content/Styles/Default").Include(
"~/Content/Styles/Default/Site.css",
"~/Content/Styles/Default/Menu.css"));
This would show up as a single call with the GUID type code (to prevent caching on file changes) on the URL. This URL will link to a minified and bundled css.
But my browser cannot read that! There is no physical path to a file!
It's a sort of virtual file. MVC's bundling uses the routing engine to point it to a combined and minified version of a particle bundle.

Should not there be vendor directory under css in H5BP template?

That will be useful in keeping the vendor specific CSS files e.g. bootstrap files or jQuery UI CSS files.
The HTML5 Boilerplate is meant to be a good starting point for any new web project.
The JavaScript files in /js/vendor are appropriate starting tools for use on anything new (jQuery and Modernizr).
If the Bootstrap CSS files were added to /css/vendor that would be inappropriately suggesting Bootstrap is appropriate for any new project (it's not).
So only thing we could do is add an empty /css/vendor folder, which would cause confusion. Unless you can think of some CSS that is appropriate for any new project (like normalize.css which is already included).

Style bundles in .NET 4.5 and icons in CSS

I'm beginning to use .NET 4.5's built in minification and bundling to minify & bundle my CSS and JavaScript. JavaScript minification works great, however, I have run into trouble with CSS minification. I create a style bundle using the below code -
var myCss = new string[]
{
"~/Content/jquery.css",
"~/Content/app.css",
};
bundles.Add(new StyleBundle("~/bundles/MySiteCss/").Include(myCss ));
and then I reference them in .cshtml (razor file) as below -
#Styles.Render("~/bundles/MySiteCss/")
It minifies the CSS file. However, if the CSS files contain styles that have background-image references, such as background-image: url('img/icon.png'), it attempts the load this icon file from a new location (derived from the bundle name) = /bundles/MySiteCss/img/icon.png
Since the icon does not exist in the location, it doesn't get loaded and displayed on the page.
You need to have your bundles and CSS served from the same place for this to work easily. For example, change your bundle line to be:
bundles.Add(new StyleBundle("~/Content/MySiteCss/").Include(myCss));
And update your reference as well:
#Styles.Render("~/Content/MySiteCss/")
This has been fixed in version 1.1.0-alpha1 of the Microsoft ASP.NET Web Optimization Framework.
You can get the update via NuGet (https://nuget.org/packages/Microsoft.AspNet.Web.Optimization) if you include Prerelease.

Minify inline javascript during build for ASP.net?

We have a handful of ASP.net pages that include more than 50+ lines of javascript specific to that page. We'd like to minify that javascript during our CruiseControl build process.
We already use the YUI Compressor to compress our full javascript and css files. But we can't figure out how to do the Inline javascript.
Is there an MSBuild task to spin through asp.net pages and minify the javascript?
There is an interesting blog and NuGet package called undleMinifyInlineJsCss to handle this
http://weblogs.asp.net/imranbaloch/archive/2012/07/25/bundling-and-minifying-inline-css-and-js.aspx
I would extract javascript into methods and move them into .js files. and call the functions instead with the relevant parameters from the pages. Not a complicated procedure and much easier to maintain (less code). You can also benefit from client side content caching.
Also: Not sure if it helps but Google's Closure looks really good.
http://code.google.com/closure/
Compression options: http://code.google.com/closure/compiler/docs/api-tutorial3.html
Available as Java executable or web service.
You won't be able to do this without custom coding. Easiest way would probably to create a PreBuild step in the msbuild file which spits through all the .aspx files and regexes all the javascript out. Then use YUI to minify the content and replace the original by the minified version.
You might also want to check MbCompression which compresses alot including your asp.net pages, although I don't believe it also minifies the inline javascript.
It is possible to bundle and minify inline javascript. With templated Razor helpers you could create an extension method like the one below:
public static MvcHtmlString AddScriptSource(this HtmlHelper helper, Func<dynamic, HelperResult> source, string key)
{
string scriptSource = source(null).ToHtmlString();
// Cache scriptSource here
return MvcHtmlString.Empty;
}
Which you would use like this:
#Html.AddScriptSource(#<text>$(document).ready(function() { $('h1').text('The current controller is #ViewContext.RouteData.Values["controller"].ToString()'); });</text>, "test")
I created a bundler and minifier around this a few weeks ago at:
https://github.com/philpeace/CodePeace.StrawberryJam
ASP.NET now has bundling and minification built in as of MVC 4 (it is also available for web forms and web pages as well)
http://www.asp.net/mvc/tutorials/mvc-4/bundling-and-minification

Resources