34

Trying to improve my coding styles I've tried different solutions but I can't figure out what is the best.
I've started putting JavaScript inside my views but I don't particularly like this solution.
It's hard to debug with Visual Studio, and it kinds of "pollutes" the page.
My new "trend" is to put the scripts for the page in a separate file.
The only problem I am facing is with the code.
To solve the problem I've defined JavaScript variables like this:

<script type="text/javascript">
    var PriceListFetchAction = '<%=Url.Action("Fetch", "PriceList")%>';
    var UploaderAction = '<%=Url.Action("UploadExcelPriceList", "PriceList")%>';
    var ModelId = '<%=Model.id%>';
    var ImportType = '<%=Model.Type%>';
    var customerCodeFetchAction = '<%=Url.Action("FetchByCustomerCode", "Customers")%>';
    var customerNameFetchAction = '<%=Url.Action("FetchByCustomerName", "Customers")%>';
    var ImportErpAction = '<%=Url.Action("ImportPriceListErp", "PriceList")%>';
    var imageCalendar = '<%=Url.Content("~/Content/Images/calendar.png")%>';
</script>  

and then I use the variables in my JavaScript file. What is the best in terms of performance, debugging, style for you?

LeftyX
  • 35,328
  • 21
  • 132
  • 193
  • vandalo - i've updated my answer with an example of the custom action result serving the context aware cached javascript file. let me know how you get on – jim tollan Jan 07 '11 at 12:59
  • You can make all that a one liner by just json encoding a anonymous object you generate in the controller, and I would use some namespacing as well: ´´ – Guillaume86 Jun 26 '12 at 21:34

6 Answers6

40

I follow a handful of rules:

  1. Don't attach a variable directly to the DOM unless absolutely necessary.
  2. Place as much information in js files as possible. The fewer js files, the better.
  3. Version your js files. When publishing, minify and mash via Chirpy or SquishIt
  4. In js, minimize your dependency on dynamic server-side values (generated ids, etc.) when you can.

So, here's an example:

I'll add jQuery and jQuery metadata to my project: http://plugins.jquery.com/project/metadata

Then, in my master js file, I'll extend jQuery with my own namespace:

$.extend({
   fatDish : {
     url : {},
     urls : function(a) {
        $.extend($.fatDish.url, a);
     }
   }
});

Almost all of my customized js logic will live in the $.fatDish namespace.

Now, let's say I want to pass a MVC route to $.fatDish. In my aspx page, I'd write the following:

<script src="@Url.Content("~/path/master.js")" type="text/javascript"></script>
<script type="text/javascript">
   $.fatDish.urls({
      path1 : '@Url.Action("Index", "Home")'
   });
</script>

In a js file, I can now write:

window.location = $.fatDish.url.path1;

A second approach is to use jQuery metadata (which I mentioned above). On your aspx page, you could write something like:

<div class="faux-link {act:'@Url.Action("Index", "Home")'}">Go Somewhere</div>

Then, in your js file, you can grab the route value like so:

$('.faux-link').click(function() {
   var $this = $(this);
   var href = $this.metadata().act;
   window.location = href;
});
Evan Nagle
  • 5,133
  • 1
  • 26
  • 24
  • 1
    weirdlover, this is great stuff. I guess I have to study all these things. Do you have any links to samples or material of any sort about this topic? Thanks for your help. Much appreciated. – LeftyX Jan 07 '11 at 14:41
  • weirdlover, +1 from me too. this is a nice simple approach to the problem that can be used in a wide varietyclient-side implementations, independent of mvc etc. – jim tollan Jan 07 '11 at 14:47
  • I've used the jQuery extend solution. It is elegant and simple. Great! Thanks – LeftyX Jan 07 '11 at 17:36
  • @vandalo -- no links. But since people seem to like it so much, I suppose I should blog about it. I'll keep you posted! – Evan Nagle Jan 07 '11 at 17:53
  • +1 `A benefit of this approach is that you can easily move your scripts to a separate static-file server, if the need arises, because they are all under the same root. If you write your own replacement for Url.Content, you can even do this with only a code change to one function.` as mentioned by [Gabe Moothart](http://stackoverflow.com/q/3576713/148271#3576758) – IsmailS Apr 19 '11 at 09:14
  • I've been looking for a solution to this inconvenience for a while, thanks! – zaq Feb 17 '12 at 01:15
  • 1
    I use something like the second approach (no script in view, all in js file), but I prefer data-attribute and $('elem').data('attribute') to set/get parameters, need no plugin. You can also store a object like that: ´
    ´ and just get with ´$('div').data('options')´
    – Guillaume86 Jun 26 '12 at 21:21
  • @weirdlover When publishing, minify and mash via http://www.asp.net/mvc/tutorials/mvc-4/bundling-and-minification – RickAndMSFT Jun 27 '12 at 00:08
5

I create separate javascripts for calculations / logic, but call them from my view. In this way I do not have to create global variables and its easier to re-use the javascripts.

example javascript:

function doSomeCoolProcessing(modelId, fetchAction)
{
    //some stuff
}

and in the view

<script type="text/javascript">
    $('document').ready(function() {
        doSomeCoolProcessing('<%=Model.id%>', '<%=Url.Action("Fetch", "PriceList")%>');
    )};
</script>

It also makes it a lot clearer what's happening (and debugging when you return to your project after six months), since nothing happens unless you explicitly tell it to do so.

jgauffin
  • 99,844
  • 45
  • 235
  • 372
4

Personally I always put javascript in separate files. Global variables which depend on routing or some server side information inside the view. Exactly as you do. As an alternative to storing global variables you could use anchor or form elements which already contain the url and then ajaxify them. As far as model values are concerned they could be stored also in DOM elements such as hidden fields, CSS classes, HTML5 data-* attributes, ... but this might not be applicable to all situations.

Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • Thanks for your reply, Darin. I don't like hidden fields anymore but I like the HTML5 data-* attributes. I'll try to find more infos about that. Thanks. – LeftyX Jan 07 '11 at 11:10
1

vandalo,

I've borrowed a situation whereby the javascript is all in seperate file. however, using a custom action result on the controller, the js is invoked as an htmlhelper function and has a context with the page. the htmlhelper actually serves the code as a file and it's therefore cached, thus speeding up excecution/delivery.

I know you'll be curious to see how this works, so will update the answer to show the mechanics of this a little later.

til then... (here's a link to where i got my inspiration. i then tweaked this to my own needs)

ASP.NET MVC routing and paths is js files

ok, here's my worked example (a simple mvc 2 project that contains required helpers and classes) - enjoy:

http://www.gatehousemusic.com/downloads/ServeJsExample.zip

Community
  • 1
  • 1
jim tollan
  • 22,305
  • 4
  • 49
  • 63
  • vandalo - updated with link to project that demonstrates the custom action result serving of context specific js – jim tollan Jan 07 '11 at 12:59
  • Thanks Jim.I've downloaded your sample project. It is cool stuff, I must admit, but I don't know if I am going to go for this solution. I prefer to have js files ... personal choice. Thanks for your great support, though. +1 – LeftyX Jan 07 '11 at 14:38
  • vandalo - no problem. if anything, there's a few little extension methods in that solution that you can use for other purposes. glad to have been of assistance. cheers.... – jim tollan Jan 07 '11 at 14:41
  • Yes, I was just looking at that. I might consider to use those extension methods for other purposes. Thanks again. – LeftyX Jan 07 '11 at 15:03
  • +1 @jim, I first visited to your solution and I thought that is the best way. Actually it is nice thing you came up with. Later I came to this question and I must admit that @wierdlover makes lot of sense and is the best way to go with. – IsmailS Apr 19 '11 at 09:09
1

A separate file for tidiness at the least. The less languages you put into each file, the easier it will be to read what it does at a glance.

Kaido
  • 3,383
  • 24
  • 34
0

Here's my methods on ASP.NET MVC3 to complete the @weirdlover answer, one thing he's missing is the JSON encoding which is quite important when you want to safely inject data in js.

If the data is not too big and can be logically attached to a DOM element, I use one (or several) data-attribute (don't require a jQuery plugin and leave the css class pretty) and a css class to find the elements from jQuery.

Stupid example:

HTML:

<div class="text-widget" 
     data-options="@Json.Encode(new { url = Url.Action("Update", "Text", new { id = 3 }), uselessParam = true })">
  <input type="text" />
</div>

COFFEESCRIPT:

class TextWidget
  constructor: (@element) ->
    @el = $ @element
    @options = @el.data 'options'
    @input = @el.find 'input'
    @input.change @update

  update: =>
     value = @input.val()
     $.post @options.url, 
       value: value
       , -> alert 'text saved'

$ ->
  new TextWidget element for element in $('.text-widget').get()

A little gotcha with Json.Encode and jQuery.data: if you use it on a simple string, you will get a quoted string in javascript: $().data('strattribute') == ' "hello" '. Just use Html.Encode in that case.

Anyway I prefer to use a single attibute with an anonymous object I build in the controller, it's easier to maintain: data-options="@Json.Encode(Model.Options)".

If there is a lot of data, like a list of objects I will turn to ViewModels for knockoutjs, I use a <script> and var data = @(Html.Raw(Json.Encode(Model.MyData)));.

I also use namespacing and closures to avoid poluting the global object.

Guillaume86
  • 14,341
  • 4
  • 53
  • 53