ASP.net MVC static resource bundles and Scripts.Render() - asp.net-mvc

I'm trying to implement some static resource improvements into my ASP.net MVC 4 project (VB.net) by changing how static resources such as javascript and css files are retrieved.
I've been following this link (ASP.NET & MVC 4: Cookieless domain for bundling and static resources ) to help accomplish this but I've come across an issue whereby unbundled javascript and css files are not rendered.
Normally when rendering .js or .css bundles you use the following:
#Scripts.Render("~/bundles/jquery")
This will then render each script tag separately in the ~/bundles/jquery bundle when in development mode, and render a single script tag pointing to the minified bundle when in production.
According to the link above, when the scripts are bundled into a single file, you can use the following line:
<script src="#Url.StaticContent("~/bundles/jquery")" type="text/javascript"></script>
This works fine for me for bundled files since the src property is valid and the StaticContent function is able to change the src URL. But when in development mode, the bundled file does not exist as no bundling takes place and all scripts are rendered seperately to the browser by #Scripts.Render and so this method does not work.
Does anyone know if it is possible to create an extension method for the Scripts helper that will accomplish what I need, or am I going to have to do something like this?
#If Misc.IsLocalDev Then
#Scripts.Render("~/bundles/jquery")
Else
#<script src="#Url.StaticContent("~/bundles/jquery")" type="text/javascript"></script>
End If

I managed to find a solution to this problem, so hopefully by putting it up here for all to see this will help others with a similar problem that I had.
Working off the same idea as the workaround I posted in my original question, I created 2 new helper functions to help generate the necessary Script and Style references in my views...
Scripts
<ExtensionAttribute()> _
Public Function RenderScripts(helper As HtmlHelper, async As Boolean, ParamArray Paths() As String) As IHtmlString
If Misc.IsLocalDev Then
Return Optimization.Scripts.Render(Paths)
Else
Dim url As UrlHelper = New UrlHelper(HttpContext.Current.Request.RequestContext, RouteTable.Routes)
Dim html As String = ""
For Each Path In Paths
If async = True Then
html = html & "<script async src=""" & url.StaticContent(Path) & GetAppVersionSuffix() & """ type=""text/javascript""></script>"
Else
html = html & "<script src=""" & url.StaticContent(Path) & GetAppVersionSuffix() & """ type=""text/javascript""></script>"
End If
Next
Return New HtmlString(html)
End If
End Function
So instead of using:
#Scripts.Render("~/bundles/jquery")
I replaced the calls with:
#Html.RenderScripts(False, "~/bundles/jquery")
A few notes on the above method...
I added an async parameter to the function call to allow me to utilise modern browser aynsc scripts.
The GetAppVersionSuffix() function call returns the assembly version which is appended to the end of the scripts source as ?v=1.2.3.4 for example. This ensures that the browser gets a new copy of the scripts and style-sheets when a new version is released.
The Misc.IsLocalDev function is a special function I use to change the way certain parts of the web application behave when I'm developing on my local machine. In this case, it ensures that unbundled scripts and styles are rendered instead of minified/bundled ones to ease debugging.
Styles
<ExtensionAttribute()> _
Public Function RenderStyles(helper As HtmlHelper, ParamArray Paths() As String) As IHtmlString
If Misc.IsLocalDev Then
Return Optimization.Styles.Render(Paths)
Else
Dim url As UrlHelper = New UrlHelper(HttpContext.Current.Request.RequestContext, RouteTable.Routes)
Dim html As String = ""
For Each Path In Paths
html = html & "<link href=""" & url.StaticContent(Path) & GetAppVersionSuffix() & """ rel=""Stylesheet"" />"
Next
Return New HtmlString(html)
End If
End Function
So again, instead of using:
#Styles.Render("~/Content/Style")
I replaced the calls with:
#Html.RenderStyles("~/Content/Style")
I hope this is useful to someone!

Related

vue.js how to pass data to root element

I'm currently building an application using a dotnet core backend and some Vue.js elements in the front. I was able to build the application using regular Vue (non template, plain non es6 javascript syntax) in a cshtml file, but I've been trying to move towards a more modular structure using vue components in .vue files.
The problem I'm running into is that in the original version, I would have access to Json objects when instantiating the root element:
var jsonRenderedWithHtml = #Html.Raw(Json.Serialize(Model.SomeJsonObject));
vm = new Vue({
el: "#root-element",
data: {
vueData: jsonRenderedFromHtml;
}, ...
When I switch over to the component version, from what I've found, I need to render the root element from a javascript file, doing something like this:
import RootElement from "../Vue/RootElement.vue";
import Vue from 'vue';
let vm = new Vue({
el: '#root-element',
render: h => h(RootElement)
});
Then, I would import this script in the #section Scripts part of the cshtml file. Unfortunately, from within the javascript file, there doesn't seem to be a way to pass in data from outside (from the cshtml file). It seems that if I were to instead write an AJAX request inside the vue root instance, it would need to do two server requests to do the same job.
It also seems that I can't use import statements within cshtml (don't seem to be any webpack loaders for cshtml?). Otherwise, I would skip rendering the element in a separate javascript file.
What we did in our application was to output the data added to the ViewData into the view through as json into a script tag and added an object to the window in the script with the previously rendered json. In the beforeCreate we then read the data from the object we added to the window and commit it to our store. Something like:
#{
IHtmlString myObj = null;
if(ViewData["SomeObject"] is ContentResult)
myObj = Html.Raw(((ContentResult)ViewData["SomeObject"]).Content);
<script type="text/javascript">
(function(){
window.obj = JSON.parse(myObj);
});
</script>
You could however add a prop to your App and pass the object in through the prop just create your vue instance in a script tag.

Bundling, but per user

We have javascript files which get bundled and compressed using the normal asp.net mvc mechanism.
We also have some javascript files which get transformed via httphandlers to deal with phrases, colour schemes, etc. At present these are simply linked in, could these be compressed and bundled but at the user level?
Unfortunately we can't group these easily, but even if we could we couldn't do it within a global.ascx file without a lot of rejigging. I mention this as it's not simply a case of having bundle1 = french, bundle2=german, etc
Compression I'm assuming could be done via IIS and static compression, but bundling?
thanks
There is no easy way to do this.
The easiest I can see is to skip the whole Bundling and Minification that is shipped with MVC 5.
Handle it yourself. Generate CSS for your user and have it go through this piece of code:
public static string RemoveWhiteSpaceFromStylesheets(string body)
{
body = Regex.Replace(body, #"[a-zA-Z]+#", "#");
body = Regex.Replace(body, #"[\n\r]+\s*", string.Empty);
body = Regex.Replace(body, #"\s+", " ");
body = Regex.Replace(body, #"\s?([:,;{}])\s?", "$1");
body = body.Replace(";}", "}");
body = Regex.Replace(body, #"([\s:]0)(px|pt|%|em)", "$1");
// Remove comments from CSS
body = Regex.Replace(body, #"/\*[\d\D]*?\*/", string.Empty);
return body;
}
Or any CSS minifier for that matter. Just make sure to include proper caching tag for your user and you won't even have to regenerate it too much.
Code taken from Mads Kristensen

How to manually minify a JavascriptResult content before returning it

I have a controller that has a special method to return me a js content uppon some requests params.
Everything is working greatly, I'm simply using a method that returns a JavaScriptResult and I'm rendering a view (that has the whole js content) to a string and returning it.
Now I want to take a step further on that solution. I want that js content to be minified. I've found (on NuGet) a CrockfordJsMinifier class (part of Web Markup Minifier package) that seemed to do the job.
Problem is: It's not a complete minifier, it's only "eating" the extra spaces, line breaks, etc... It is not compressing variable or function names and stuff like that.
Hence my question: Is it possible to use the minification from the "Bundling and Minification" solution from MVC5 on a string? If so, how should I do it?
Here's a brief example of my method just to illustrate:
[AllowAnonymous]
[HttpGet]
public JavaScriptResult GetAnonymousJS(string JSName)
{
//"PartialToString" just renders the view on a string.
string result = PartialToString(PartialView("PublicViewScripts/" + JSName));
var minifier = new CrockfordJsMinifier();
result = minifier.Minify(result, false).MinifiedContent;
//This result is not "really" minified (is just roughly minified)
return JavaScript(result);
}
How about Microsoft Ajax Minifier. It can compress variables and function names as you want. After added the Ajaxmin.dll to your project, you can call the minifier:
public JavaScriptResult GetAnonymousJS(string JSName)
{
//"PartialToString" just renders the view on a string.
string result = PartialToString(PartialView("PublicViewScripts/" + JSName));
var minifier = new Minifier();
result = minifier.MinifyJavaScript(result);
//This result is not "really" minified (is just roughly minified)
return JavaScript(result);
}
http://ajaxmin.codeplex.com/wikipage?title=AjaxMin%20DLL

How can I load static content into a gsp page?

We have a grails project that is templated to produce two different sites. The sites have two different Frequently Asked Question pages but we would like to keep the template the same. We were thinking about including two different *.groovy files that have variables in them with the questions and then map those variables to a gsp page. Or maybe two different *.gsp files and the right one gets included at startup.
What is the best way to include the static content into the gsp page while reusing as much code as possible and how would I go about doing it?
Let me know if you need more information.
Grails have the concept of templates to reuse your view code. For example:
*grails-app/views/book/_bookTemplate.gsp*
<div class="book" id="${book?.id}">
<div>Title: ${book?.title}</div>
<div>Author: ${book?.author?.name}</div>
</div>
grails-app/views/book/someView.gsp
<g:render template="bookTemplate" model="[book: myBook]" />
grails-app/views/book/anotherView.gsp
<g:render template="bookTemplate" model="[book: myBook]" />
So you can use the render tag in any GSP that needs to use your template.
It is kind of late but I was looking for the answer myself. While there is no direct way to have insert static file in Grails, there are various ways you could accomplish so. From controller to custom tag. There is a code for custom tag:
import org.slf4j.Logger
import org.slf4j.LoggerFactory
class HtmlTagLib {
// to be used in gsp like <html:render file="WEB-INF/some-static-file.txt"/>
// file param is relative to web-app folder
static namespace = 'html'
private static final Logger log = LoggerFactory.getLogger(HtmlTagLib .class)
def render = { attrs ->
String filePath = attrs.file
if (!filePath) {
log.error("'file' attribute must be provided")
return
}
String applicationPath = request.getSession().getServletContext().getRealPath( filePath )
def htmlContent = new File(applicationPath).text
out << htmlContent
}
}
Credits to Dónal on Rendering HTML files in Grails

Way to organize client-side templates into individual files?

I'm using Handlebars.js, and currently all my templates live inside script tags which live inside .html files housing dozens of other templates, also inside script tags.
<script type="text/template" id="template-1">
<div>{{variable}}</div>
</script>
<script type="text/template" id="template-2">
<div>{{variable}}</div>
</script>
<script type="text/template" id="template-3">
<div>{{variable}}</div>
</script>
...
Then I include this file on the server-side as a partial.
This has the following disadvantages:
A bunch of templates are crammed into HTML files.
Finding a given template is tedious.
I'm looking for a better way to organize my templates. I'd like each each template to live in its own file. For example:
/public/views/my_controller/my_action/some_template.html
/public/views/my_controller/my_action/some_other_template.html
/public/views/my_controller/my_other_action/another_template.html
/public/views/my_controller/my_other_action/yet_another_template.html
/public/views/shared/my_shared_template.html
Then at the top of my view, in the backend code, I can include these templates when the page loads, like this:
SomeTemplateLibrary.require(
"/public/views/my_controller/my_action/*",
"/public/views/shared/my_shared_template.html"
)
This would include all templates in /public/views/my_controller/my_action/ and also include /public/views/shared/my_shared_template.html.
My question: Are there any libraries out there that provide this or similar functionality? Or, does anyone have any alternative organizational suggestions?
RequireJS is really good library for AMD style dependency management. You can actually use the 'text' plugin of requireJS to load the template file in to your UI component. Once the template is attached to the DOM, you may use any MVVM, MVC library for bindings OR just use jQuery events for your logic.
I'm the author of BoilerplateJS. BoilerplateJS reference architecture uses requireJS for dependency management. It also provides a reference implementations to show how a self contained UI Components should be created. Self contained in the sense to handle its own view template, code behind, css, localization files, etc.
There is some more information available on the boilerplateJS homepage, under "UI components".
http://boilerplatejs.org/
I ended up using RequireJS, which pretty much let me do this. See http://aaronhardy.com/javascript/javascript-architecture-requirejs-dependency-management/.
I use a template loader that loads the template using ajax the first time it is needed, and caches it locally for future requests. I also use a debug variable to make sure the template is not cached when I am in development:
var template_loader = {
templates_cache : {},
load_template : function load_template (params, callback) {
var template;
if (this.templates_cache[params.url]){
callback(this.templates_cache[params.url]);
}
else{
if (debug){
params.url = params.url + '?t=' + new Date().getTime(), //add timestamp for dev (avoid caching)
console.log('avoid caching url in template loader...');
}
$.ajax({
url: params.url,
success: function(data) {
template = Handlebars.compile(data);
if (params.cache){
this.templates_cache[params.url] = template;
}
callback(template);
}
});
}
}
};
The template is loaded like this:
template_loader.load_template({url: '/templates/mytemplate.handlebars'}, function (template){
var template_data = {}; //get your data
$('#holder').html(template(template_data)); //render
})
there's this handy little jquery plugin I wrote for exactly this purpose.
https://github.com/cultofmetatron/handlebar-helper

Resources