For a while now I’ve wanted the ability to generate javascript file like I generate html files. Take a little bit of the power of the WebForm view engine (just a little bit) and give that to me for JavaScript.
Why? I have a few reasons. Number one is I want to remove all of my JavaScript from the html, so the JavaScript is loaded via script tags. Once that happens, then I can have the JavaScript cached by the browser and speed up my application a bit. What I need the Asp.Net engine for was to give me accurate links to web site resources (images, web services, call, other javascript files, etc). These are thing that I don’t want to hard wire, mostly because I’ve bitten off the Asp.Net Routing engine bug. So, I want to use the Asp.Net Routing engine to tell me where to find stuff.
Not that I’ve committed to Asp.Net MVC I’ve come to a realization that making dynamic javascript files is well within my reach. Actually, it is almost there already; Asp.Net MVC ships with a JavaScriptResult. The downside of that result action is that the object expects you to hand it the JavaScript as a string, which it pushes to the browser as a file. I want to give a method a JavaScript View for that. The other good news about JavaScriptResult is that it doesn’t do much (just set the response.ContentType to application/x-javascript). So I could grab all of the View methods, turn them into extension methods, rename them, and then use them for my own underhanded affairs.
Luckily we can all grab the Asp.Net MVC source code and go to town. I didn’t modify the original source, but I defined a new class (JavaScriptFileResult) and a bunch of extension methods for the Asp.Net MVC Controller to give you JavaScriptView methods. You can see a usage in the first piece of code.
1: public ActionResult JsConstants( )
2: {
3: return this.JavaScriptView();
4: }
Now below is the actually extension methods and class needed. Put this in your project some place and go to down.
1: public class JavaScriptFileResult: ViewResult
2: {
3: public override void ExecuteResult(ControllerContext context)
4: {
5: base.ExecuteResult(context);
6: HttpResponseBase response = context.HttpContext.Response;
7: response.ContentType = "application/x-javascript";
8: }
9: }
10:
11: public static class JavaScriptControllerExtensions
12: {
13: public static ViewResult JavaScriptView(this Controller controller )
14: {
15: return JavaScriptView(controller, null /* viewName */, null /* masterName */, null /* model */);
16: }
17:
18: public static ViewResult JavaScriptView(this Controller controller, object model)
19: {
20: return JavaScriptView(controller, null /* viewName */, null /* masterName */, model);
21: }
22:
23: public static ViewResult JavaScriptView(this Controller controller, string viewName)
24: {
25: return JavaScriptView(controller, viewName, null /* masterName */, null /* model */);
26: }
27:
28: public static ViewResult JavaScriptView(this Controller controller, string viewName, string masterName)
29: {
30: return JavaScriptView(controller, viewName, masterName, null /* model */);
31: }
32:
33: public static ViewResult JavaScriptView(this Controller controller, string viewName, object model)
34: {
35: return JavaScriptView(controller, viewName, null /* masterName */, model);
36: }
37:
38: public static ViewResult JavaScriptView(this Controller controller, string viewName, string masterName, object model)
39: {
40: if( model != null )
41: {
42: controller.ViewData.Model = model;
43: }
44:
45: return new JavaScriptFileResult
46: {
47: ViewName = viewName,
48: MasterName = masterName,
49: ViewData = controller.ViewData,
50: TempData = controller.TempData
51: };
52: }
53:
54: public static ViewResult JavaScriptView(this Controller controller, IView view)
55: {
56: return JavaScriptView(controller, view, null /* model */);
57: }
58:
59: public static ViewResult JavaScriptView(this Controller controller, IView view, object model)
60: {
61: if( model != null )
62: {
63: controller.ViewData.Model = model;
64: }
65:
66: return new JavaScriptFileResult
67: {
68: View = view,
69: ViewData = controller.ViewData,
70: TempData = controller.TempData
71: };
72: }
Now the view, here is what it looks like:
1: <%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
2: var UrlList = {
3: SubscriptionList: '<%=Url.Action("SubscriptionList", "Channel") %>',
4: MoreSubscriptions: '<%=Url.Action("More", "Channel") %>',
5: WaitGif: '<%=Url.Content("~/Content/img/ajaxloader.gif") %>',
6: };
Like I stated before, I was looking to get uris for the most part. But this could also be a strongly typed view where you have to pass in real data.
Finally, loading this view into my page. You saw the controller action was “JsConstants” above, which was in my HomeController (not shown), so here is how I ask the routing engine for the JavaScript file.
1: <script type="text/javascript" src="<%=Url.Content("~/Home/JsConstants") %>"></script>
So far I am happy with this technique. But if you see something that can be improved, please let me know.
Really nice work. Congrats.
I think you could make one more improvement. You could create Html helper methods to write that script tag for you. This would also make it possible to write strongly type calls to your actions using lambda expressions.
Anyway, nice work. I’ll use it for sure.
Best regards,
Kelps
Great idea. Would you mind contributing that to mvccontrib?
Excellent. I am in need of this functionality on a current project.
You know, it would be very interesting to use technique to minify an entire directory of JavaScript files.
Trust me, that thought has crossed my mind as well.
@Jeffery Palermo: I know you are probably getting ready for PDC and your party, but I would be happy to contribute this code to MVCContrib. Plus that would give me an excuse to check out GIT.
Cracking idea! Well done.
So is this a part of MVCContrib now? or not yet?
Great Idea.
Regards,
Ajay
I will say I attempted to submit this to MVCContrib (did that back in November actually). But GIT is still a bit of a mystery to me, so I’m not sure if it is there or not.
This is a great idea.
I did not see that it was part of MVCContrib. If you still out of time, perhaps I can assist. I think this is a vastly helpful.
Great,
I used this for localize javascript messages
Thanks for the code here. Great update!