I use a .cshtml file to generate a dynamic .js file in an ASP.NET MVC controller. So the .js file never exists on disk, but only in memory at runtime. It works like this.
The browsers loads a javascript...
<script async src='http://example.com/script.js'></script>
...that is fetched from and generated by my MVC Controller endpoint:
[HttpGet]
[Route("script.js")]
public ActionResult GetScript()
{
// This takes a .cshtml template-file and returns a generated .js file
return new JavascriptViewResult(viewName, viewModel);
}
Is there a way to minify the resulting javascript file that is returned in runtime? I had a look at System.Web.Optimization.JsMinify but I'm not sure how I would get that to work. Can I write some kind of HttpHandler? Is there maybe a better approach to solve this problem?
I had a similar problem and after some searching some I came across this question. All you need to do is:
using Microsoft.Ajax.Utilities;
//...
string minifiedScript = new Minifier().MinifyJavaScript(yourJs);
I can't believe it was this simple, yet so hard to find.
P.S. It seems to be part of Microsoft Ajax Minifier library, but I don't remember ever installing it manually, so it's either part of MVC or comes with System.Web.Optimization.
I didn't notice the part about the JS being generated from a view. In that case using bundles might be a solution or rendering the view to a string (but I'm unfamiliar with how it's done in Spark view engine) before passing it to Minifier.MinifyJavaScript.
Edit: Another solution (albite somewhat convoluted) would be calling Minifier.MinifyJavaScript in the view and cramming the actual JS in a partial view. I don't know about Spark, but in Razor it's fairly easy to render a view to string from another view.
Related
Currently, I've been trying to put Cassette into our Sitecore project and it's just fallen flat on it's face. I've had dotLess in the project already, but would like a more solid solution for cache-busting while bundling.
I can use Cassette in a normal .Net4 project, but just can't get it to work in our Sitecore project. (Ed) We were unable to get it to actually build any sort of bundles (checked /_cassette) and it wasn't outputting anything to the page. For us, the project isn't built but is instead using CodeFile and I'm not sure if that was part of the problem. In general, we kept getting the No bundle with path 'xxx' exception no matter what we tried. It's a bummer to because I would really wouldn't mind having CoffeeScript weaved into the solution.
Is there a solution that compresses/minifies javascript and can render dotLess files with Sitecore?
The empty response streams from Cassette when working in any of the Sitecore client interfaces are being caused by the rewriteHtml functionality that it implements.
By default Cassette will buffer and rewrite page HTML output. This allows partial views to insert <link> tags referencing stylesheets after the <head> tag has been rendered. The rewrite functionality gets invoked as a PostRequestHandlerExecute event handler.
The empty response streams that you are seeing are as a result of the output stream that has been rewritten not being flushed. A fix for this issue is to flush the output stream when Close is called on the Cassette.AspNet.PlaceholderReplacingResponseFilter class, this is shown below:
void WriteUncompressedOutput()
{
var output = GetOutputWithPlaceholdersReplaced(bufferStream);
var outputBytes = outputEncoding.GetBytes(output);
if (outputBytes.Length > 0)
{
outputStream.Write(outputBytes, 0, outputBytes.Length);
outputStream.Flush();
}
}
If you do not require the rewriting functionality a workaround is available now. Just disable the Cassette HTML rewriting feature, either in the web.config:
<configuration>
<configSections>
....
<section name="cassette" type="Cassette.CassetteConfigurationSection, Cassette"/>
</configSections>
<cassette rewriteHtml="false"/>
or in code:
public class CassetteSettingsConfiguration : IConfiguration<CassetteSettings>
{
public void Configure(CassetteSettings configurable)
{
configurable.IsHtmlRewritingEnabled = false;
}
}
This information is included in my blog post about using Cassette with Sitecore.
We ended up getting SquishIt to work rather easily and without much hassle besides getting the JavaScript files to play nicely together.
I was under the impression that you could put javascript in a view template in Rails 3. For example, if I had this html in views/public/home.html.erb
<div id="block">click</div>
then in views/public/home.js.erb, I thought I could put the following javascript, and then click on html to trigger the javascript. However, when I tested it, I got no results. But if I put the javascript in assets/javascript/application.js, then everything worked fine...Shouldn't it also work if it was in a js template with the same name as the html view?
$(document).ready(function(){
test();
});
function test() {
$("#block").click(function() {
$('#block').hide();
});
}
Ummm. no. It just doesn't work that way. Only one of views/public/home.* will be rendered, depending on the responds type.
Javascript shouldn't be added as a view file (bla.js.erb). They must be put in assets/javascripts or at least in lib or vendor directory.
You must also require them in your application.js file, if you already don't use require_tree.
In this way you won't need to reference the javascript in any way in your view (the application.js will include it for you). Otherwise, you need to specify a layout to insert javascript files in block, because views are rendered after tag.
There are a lot of reason not to put javascript directly in html (except for tests obviusly), read the rails asset pipeline for more information.
When you create a view with a different extension from html.erb that will be used only if your url specify a format with that extension, for example mywebsite/users.json will return eventually a index.json.erb.
For AJAX you would like to return a JSON object, not javascript which is definitely not a correct approach. Remember that you are using a framework and you should follow it's guidelines. If you want to live it's rails, it will be hard to work with it.
You can use javascript_include_tag
If you have the js files source1.js, source2.js in public/javascript then you can include them using
javascript_include_tag 'source1', 'source2'
I am using OpenRasta 2.0, and am hosting within a console application.
I would like to be able to return some static HTML pages and JavaScript files from this setup, for example:
/index.html
/jquery.js
The files are entirely static, i.e. no Handler or Resource is required.
I have added the appropriate files to the project, and initially tried the following syntax:
ResourceSpace.Has
.ResourcesOfType<object>()
.AtUri("/")
.HandledBy<HtmlHandler>()
.RenderedByAspx("~/Views/IndexView.aspx");
The .aspx file is added to the project under a folder 'Views', and is set the build action to 'Embedded Resource'. This results in a NullReferenceException at runtime when attempting to resolve the virtual path. If I set the build action of the file to 'Compile', then it will not compile, I'm guessing because the console project does not understand ASPX.
I have also tried the following shorthand syntax for this available if referencing the WebForms codec:
ResourceSpace.Has
.TheUri("/jquery.js")
.ForThePage("~/Views/jquery.js");
But this suffers from the same issues as my initial approach, although does remove the need for a dummy Handler. So as far as I can tell, the WebForms codec cannot be used within a console application because the ASPX files cannot be compiled.
I was able to return HTML using the Razor codec as this expects the view templates to be embedded. However - I was not able to return a JavaScript file with the appropriate media type using the same technique, and I had to turn my otherwise static files into .cshtml files with a #resource defined.
I can't find any examples online of returning static HTML and/or JavaScript using OpenRasta. I would expect to find a dedicated configuration API for this like the "TheUri" syntax but independent of the WebForms codec.
I could create my own 'EmbeddedFileHandler' to return the content of a static embedded file, but I feel like I'm missing something since this is such a simple use case...
Anything that depends on the asp.net pipeline being initialized (such as aspx webforms pages) cannot compile because the BuildProvider is not there to do it, mostly because webforms is too tightly coupled to the asp.net pipeline.
OR 2 was not really designed to be used as a full web stack outside of asp.net for serving static content, as usually the host environment is better suited at doing it, but that's definitly something we're going to address in 3.0.
What I'd suggest is something along the lines of registering FileInfo as a resource, create a handler that can scan the file system for the files you want, and provide your own codec that either stream the data itself or call the API for the host http listener. It should be about 20 lines of code tops and would make a great blog post. :)
So, continuing in climbing my MVC learning curve, I would like to know how to effectivelly handle javascript in partial views. I mean coding a script in a partial view and then rendering the partial view twice in a view gives duplicate code, including variables and is generally conflictive.
How pros handle JS concurrency in ASP.NET MVC so each partial view see only its own JS code?
When you render a partial view within a view, you can still reference all of the HTML elements in that partial view from Javascript on the holding view.
This will not only help with duplication, but will add to the principle that code should be maintainable within one place in the codebase.
If I were you, I would put all Javascript that your partial views need in the main view, or better yet, in a Javascript file that is separate and reference it in a master page which your main view inherits from :)
This video gave me a great inside look into jquery with mvc it share also some best-practise...
mvcConf 2 - Eric Sowell: Evolving Practices in Using jQuery and Ajax in ASP.NET MVC Applications
http://channel9.msdn.com/Series/mvcConf/mvcConf-2-Eric-Sowell-Evolving-Practices-in-Using-jQuery-and-Ajax-in-ASPNET-MVC-Applications
def. worth a look...
Use external javascript file and reference it only in the full view. Put this line in the partial view so it does not load the stuff from the _Layout.cshtml
#{ this.Layout = null; }
you need to add the script at the bottom of the page before close the body tag place all required js their and place a rendersection('yoursection',false) so when you need you can define your section and just use the script their who would work in the page you need to define.
If you want javascript need to maintained effectively. Write that javascript in seperate js file. create another partial view and give the link to the js file. while rendering partial views, render the partial view which contains the link for that js file
I am not sure if it is the best way of doing this, but I certainly got it working for me.
So you write you javascript in an external file, and reference it in a partial view, then you could nest your partial view inside this partial view, so each of those nested partial view will be able to use the java script file on their parent level. So this way, you only reference the javascript once, but it can be used with all nested partial views.
good luck mate.
I think Dan Finch and user2745484 have answered the question. But as you sad scripts in partial views may override each other. For solving these problem you need to use some kind of object oriented programming to have encapsulation. Note that you may encounter these kind of problem everywhere you write JavaScript code.
There are essentially 2 places to define JavaScript functions in Grails, directly in a element on the GSP, and within a separate javascript source file under /web-app/js (for example, application.js). We have defined a commonly reused javascript function within application.js, but we also need to be able to generate parts of the function dynamically using groovy code. Unfortunately, ${some groovy code} does not appear to be processed within separate javascript source files.
Is the only way to do this by defining the javascript function within a script tag on a GSP page, or is there a more general solution? Obviously we could define the javascript function in a script tag within a template GSP file which would be reused, but there is a lot of push to keep our javascript functions defined all together in one place (i.e. the external javascript source file). This has performance benefits as well (the javascript source files are usually just downloaded once by each client's browser, instead of reloading the same javascript functions within the source of every html page they visit). I have toyed around with the idea of breaking the function up into static and dynamic pieces, putting the static ones in the external source and putting the dynamic ones in the template GSP, then gluing them together, but this seems like an unnecessary hack.
Any ideas?
(edit: It may sound like the idea of dynamically generating parts of a JavaScript function, which is then downloaded once and used over and over again by the client, would be a bad idea. However, the piece which is "dynamic" only changes perhaps once a week or month, and then only very slightly. Mostly we just want this piece generated off the database, even if only once, instead of hard coded.)
An easy solution to keep your JavaScript unobtrusive is to create a JavaScriptController and map its actions "/js/*" by adding this to your UrlMappings.groovy file:
"/js/$action"{
controller = "javascript"
}
then just create an action for each dynamic JS file you want, include in in your layout <HEAD>, and presto, you've got a JS file that you can insert Grails snippets into! :)
Note: I've found that there's currently a bug in Grails that doesn't map file extensions to content-types properly, so you'll need to include <%# page contentType="text/javascript; UTF-8" %> at the top of your view files.
This is a great solution. I would like to offer a suggestion to use somthing other then a mapping of "/js/$action" because this is no longer going to allow you to access you javascript files in /web-app/js/. All your javascript files would have to be moved to a the directory your controller would point to.
I would use something like
"/dynjs/$action"
This way you still can point to files in the /web-app/js/ files with out conflict and enjoy the benifits of gsp tags in javascript files
Please correct me if I'm wrong.
Or this... have a tag/service/dynamic method that lets tags write out their JS+CSS+whatever else, to a "cache" which is used to build the JS+CSS resources by a different controller.
Full concept here: [http://www.anyware.co.uk/2005/2009/01/19/an-idea-to-give-grails-tags-esp/][1]
If you want to use models created by the controller (that rendered HTML page which reference the Javascript in which you intend to use groovy code) in the Javascript code, then you can use this technique:
This technique does not need to change URL mappings and does not require you to create extra controller.
In your view GSP add javascript as follows:
<script type="text/javascript">
<g:render template="/javascript/yourJavascriptFile"/>
</script>
In views folder create a "javascript" folder. And create a file named:
_yourJavascriptFile.gsp
You can not only use all the GSP code in your _yourJavascriptFile.gsp file, but you can also use all the models created in your controller (that is rendering the view).
NOTE: There is nothing special about javascript folder. You can name it anything you want OR use an existing view folder. This is just a matter of organizing and identifying your HTML spitting GSP from Javascript spitting GSPs. Alternatively, you can use some naming conventions like: _something.js.gsp etc.
Name your scripts like this
/wherever/the/js/files/are/thescript.js.gsp
The gsp code inside will be rendered correctly by grails. This works, but I have no idea if it's considered a Good Idea or not.
There is another way - pass in the generated code into a function that expects closures. Those closures is generated by the program of course. The generated code is of course inlined/script-tagged in the gsp page.
it may or may not work depending on the nature of the code being generated. But i suspect it will work, and if it doesnt, minor tweaking to the coding style of your javascript will definitely make it work. Though, if these 'generated' code doesnt change much, this quite overkill imo.