1

We are trying to continually improve our source code base and a question came up recently which throws open a new area to improvement. Where best to put URLs that are needed in JavaScript?

  1. Do we put the URLs in the JavaScript files themselves?
    function onButtonClick() {
        $.ajax({url:"/my/url/directly/in/the/javascript"});
    }

Note: The magic string above is incidental. There could be an object that returns URLs or whatever. The point is that the URLs are stored in the JavaScript files.

  1. Do we have some mechanism to inject the URLs into the JavaScript. e.g. Having a small script on the page that sets up the URLs from which the JavaScript in the files can access.
    <script>
        var ps = new pageScript();
        ps.initUrls({someUrl:<%= Url.Action("SomeAction", "SomeController").ToJson() %>);
        ps.run();
    </script>

The .ToJson() method is an extension method that turns an object into its JSON serialised version, so, for a string it outputs the quotation marks and escapes it if need be.

Then in the JavaScript file it can pick up the URLs from where ever the initUrls() function stored them.

    function onButtonClick() {
        $.ajax(myUrls.someUrl);
    }
  1. Or something else?

We already use 1 and 2 in our code base from various developers over the years, but is there a better way? Or is either of the above a good overall solution?

1
  • 1
    Ajax is often used to replace what a standard form would otherwise submit to the server, so one of the best places to put the URL is in the [action] attribute of a <form>.
    – zzzzBov
    Nov 25, 2014 at 20:32

3 Answers 3

2

I am a fan of using data attributes:

HTML:

<div class="ajax-widget" data-endpoint="/api/some-endpoint">
    ...
</div>

JS:

// Or whatever your initialisation logic is.
$('.ajax-widget').each(function() {

    // Grab the endpoint and use it for something.
    // e.g. AJAX call, assigning to a property for later etc.
    var endpoint = $(this).data('endpoint');
    ...
});

The benefits to this are many.

  • They are easily parsed with jQuery.
  • It offers good separation of concerns (allowing you to keep your JS out of your views and in JS files).
  • You can create flexible, modular widgets/plugins using the same JavaScript that can be pointed to various endpoints without modifying JavaScript.
  • Avoids having to have funky environment checks in your JS - configure environment-specific URLs server-side and let the client just do its thing with whatever you give it.
  • You can make use of MVC helpers without littering your JavaScript (and, again, pulling it into your views).
  • You can easily build HTML prototypes which point to endpoints returning pre-specified results (such as static JSON files) allowing you to develop and test client-side code independently without having to change JS code in production.
  • Probably many more that I can't think of right now.

This is the standard convention we use and I honestly think it's spot on. The benefits I've listed above are in the context of AJAX calls but they apply for just about any scenario you can name in which you'd need to do something like this (client-side redirects, dynamic hyperlink creation and so on).

1

What you want to do is start with a config object:

myApp.config = {
  myUrls: {
    someUrl: "http://default.com";
  },
  someSharedPropAcrossAllEnvs: 'something'
};

And then create environment specific versions that override the appropriate values:

var envs = {};

var envs.dev = {
  myUrls: {
    someUrl: "http://localhost:8080";
  }
};

var envs.test = {
  myUrls: {
    someUrl: "http://test.com";
  }
};

var evns.production = {
  myUrls: {
    someUrl: "http://productiob.com";
  }
};

You then somehow need to set the appropriate environment that you are running in:

var env = envs['<%= SOME_SERVER_VALUE_OR_SOMETHING%>'];

Which should bind to:

env = env['dev'] // test or production

Then mixin the appropriate config in and override the default values

config = $.extend({}, config, env);

This will mixin the correct environment vars which you can use:

   function onButtonClick() {
        $.ajax(myApp.config.myUrls.someUrl);
    }
2
  • So, to clarify this is a variant on 1 where all the URLs are in the JavaScript files. The main difference here is that you've taken out the magic string and replaced it will an environment configuration. Nov 25, 2014 at 14:35
  • This seems like it's a mechanism to combine 1 and 2, with a single point of definition for the endpoints. the key is the (written ASPX engine style in the post) var env = env[@Application.EnvironmentKey]; bit. Nov 25, 2014 at 15:18
0

In that case I would do something like this (pardon my Razor syntax):

HTML Link code:

    @Html.ActionLink("Link Text","ActionMethod","MyController", new { someArgument }, new{ id="mylink"})

Javascript code:

$("#mylink").click(function(e){
   e.preventDefault();
   $.ajax({url:'@Url.Action("ActionMethod","MyController",new{ someArgument })'});
});

This gives you a few benefits:

  1. Ease of adaptation to routing changes -- Your ASP.NET MVC app has routing configurations set up. What happens if your routing configuration changes so that your URIs look different? By using the helper methods you don't have to worry about manually changing them. The framework is injecting the correct URI on your behalf, and when you change your routing configuration your links will be updated for you.

  2. Graceful degredation -- You're building your site in a way that allows the link to work without Javascript. When the DOM is ready you're then blocking the href action that's been created in the HTML and enhancing it with an AJAX call. If you subscribe to the Unobstrusive Javascript train of thought, this is a huge plus.

As you can see it's quite similar to what you're doing in #2 with some minor changes. I don't think there's much benefit in optimizing any further than this. You know where URLs are, and the server-side framework is determining how to build them correctly. Your developers decide where and how they are created.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.