8

I'm looking at the following jQuery call:

$.get('/Home/GetResult', null, function (data) {

    alert(data);

});

I see this being called the 'Hard Coded' way of doing this compared to:

$.get('@Url.Action("GetResult", "Home")', function (data) { 

    alert(data);

});

which I see referred to as the non hard coded way of doing this.

Could someone please explain to me what this is all about? Seems to me both ways are equally hardcoded ways of calling this because you have the explicit names of the Controller and Method in both.

Does anyone know why one is called hardcoded and the other isn't. Is one better?

Colin Brock
  • 21,267
  • 9
  • 46
  • 61
AnonyMouse
  • 18,108
  • 26
  • 79
  • 131

5 Answers5

5

If we hard code a URL then we lose the ability to later change our routing scheme. So always use URL Helper methods. If you use URL helper, the Helper method will take care of the change, You do not need to go and make the change in 100 places. Also you do not need to worry about how many ../ i have to add as the prefix when the same method is being called from a Home page and an inner page

Checkout these answers by Darin

https://stackoverflow.com/a/7492024/40521

https://stackoverflow.com/a/6454041/40521

Community
  • 1
  • 1
Shyju
  • 214,206
  • 104
  • 411
  • 497
4

"/Home/GetResult" is considered "hard coded" because the developer entered a configuration value directly in the code, where it is more difficult to alter in the future.

It is worse to specifically hardcode URL values because if this view (and associated controller action) is moved to another "area" within the application, "/Home/GetResult" will break. The @Url.Action method is better because will not.

Peter J
  • 57,680
  • 8
  • 38
  • 45
2

I like Peter J 's answer. In addition

You should have the URL as part of the href or as an attribute on a html element.

E.g <a data-getresult="@Url.Action("GetResult", "Home")">click</a>

And then

var clickAction = $(this).attr('data-getresult');
$.get(clickAction, null, function (data) {

    alert(data);

});

Wrap the above in a function and link it to a click event via the attrib name.

Community
  • 1
  • 1
Valamas
  • 24,169
  • 25
  • 107
  • 177
  • 1
    Not sure I see your point. Why have it as part of an html element if it's not necessary (not saying it's never a good idea, just in the original simple example it makes no sense to introduce an element to hold the url)? – Marek Karbarz Jul 24 '12 at 21:47
  • So you do not have inline javascript in your views. – Valamas Jul 24 '12 at 21:47
  • 2
    I prefer this method too, it allows for both easy maintenance (by automatically updating the action) while still allowing you to place the code in minified external .js files. – Kevin B Jul 24 '12 at 21:49
  • You can place it on an existing element. Or as part of a page load js. The point is not to have javascript code in views and to be able to do this is to get the action from the view as an attribute. – Valamas Jul 24 '12 at 21:49
  • I guess if anything I would put it as part of JS initialization code (and yes, still have inline code for that). Adding "unused" HTML elements just to avoid having any JS in a view seems excessive. Especially if the same route could be used for multiple actions on the same page. – Marek Karbarz Jul 24 '12 at 21:54
  • 1
    _"You can place it on an existing element"_ - So, what, you pick a random existing element to store data that has nothing to do with that element? I don't think so. – nnnnnn Jul 24 '12 at 22:12
  • @Marek: I've replied with a laundry list of benefits in my answer here. – shannon Jul 25 '12 at 04:00
  • 1
    @nnnnnn: Make a new element if it's appropriate. But often the data is associated with a specific element, in which case, use that one. Great example, put the controller used for AJAX-auto-completing a text field on the INPUT that uses it. Or for widgets that can occur multiple times on a page, put the data in the root of the widget. Using plain javascript would cause redefinition defects. – shannon Jul 25 '12 at 04:00
1

To simply answer your question, yes, I consider both of the examples you listed "hardcoded". The second, @Url.Action is LESS hard-coded. It has less specificity implied. If you changed the root of your project, the second one would still work, while the first would break as mentioned by @Peter J. Also, I suppose the second would work if you were using areas and changed the area name, while the first would break.

To maybe be a little more helpful, though, I use a third approach. I had exactly your question a month ago, and thanks to ASP.NET MVC AJAX calls without static Controller URLs (magic strings) I have a process that works really well for me.

Index.cshtml

<input data-action='@Url.Action(Mvc.AutoComplete.PostalCode())' type='text' name='postalCode' class='autoComplete'><input>
<input data-action='@Url.Action(Mvc.AutoComplete.ProductCategory())' type='text' name='productCategory' class='autoComplete'><input>

main.js

$('input.autoComplete').each(function () {
    var el = $(this);
    el.autocomplete({source: el.data('action')});
});

Voila! Compile-time checking and clear separation of responsibility via T4MVC and HTML5 data attributes. The controller definition is read from the widget that uses it. Great for partial views that might appear multiple times on a page.

I highly recommend using T4MVC, which is the "Mvc." syntax you see in the example view. If you are trying to avoid 'hard-coding', that's about as dynamic as you can get for this. I use T4MVC ( http://t4mvc.codeplex.com/ ) so I can avoid "magic strings" to refer to controllers and actions in my views. T4MVC isn't perfect, but it's a big improvement. Inside of hidden T4MVC files, there's still hard-coded values, but you never see them, they get generated automatically from your controllers, and get compile-time checked.

Also, as has already been suggested here by @Valamas, I then use data attributes on HTML elements to pass those URLs from my views to javascript.

I especially use this data-attribute approach when I have AJAX calls on my page. A single page might easily have 10 URL dependencies, and it's hard to tell when the links are broken by user-testing that might not have full coverage of conditional features. But, hooray! T4MVC throws compile-time errors when the links don't exist, and if your code is organized with all the data attribute inspections in init, javascript throws load-time errors when the corresponding data attributes are missing (rather than the run-time errors you would get for undefined variables). This offers much earlier/easier detection of linkage defects, even if you aren't unit testing your javascript (and I don't).

I usually have a standard header on every page that includes the current globally-useful information (UserId, for example) as data attributes on the BODY element, or on a display:none SPAN with a well-known ID.

Then, I will typically load all of the data from attributes at a single location near the top of my javascript code (or each javascript file that needs it).

What is the benefit of this? Now you have a single place to look to make sure all of the embedded data parameters required are provided to your javascript. Your javascript doesn't reference variables that are undefined, so if you use a javascript IDE you won't get false errors. Developers looking at your javascript don't scratch their heads trying to find the declaration of the mystery variable in other javascript files; especially troublesome when they aren't also ASP.NET MVC developers. On that note, if you have separate language teams, the implementation responsibilities are clearer (your javascript developers don't come edit your views when they change naming conventions, or the converse). Also, variables are defined at a well-known point-in-time during the client page lifecycle, which is a big benefit for debugging javascript. And your view doesn't contain javascript splattered all over the page, where a minor mid-page HTML defect may unexpectedly result in a complete javascript failure.

Additionally, as shown in the example, this is the only way to fly for partial views that might occur more than once on the page. You can clearly associate the embedded data with the individual HTML widget that uses it. If you plugged in javascript directly to the view you'd be erroneously redefining variables.

The list of benefits goes on and on, but basically it comes down to a separation of responsibility. Everything else follows from that.

Community
  • 1
  • 1
shannon
  • 8,664
  • 5
  • 44
  • 74
0

They would achieve the same thing, most of the time.

Essentially if you have a simple MVC site with no areas and you are using the default route definition you won't notice much change. But if you customize the route definition or use areas in your app, you will likely encounter problems. Where possible, it's recommended you use the soft coded method. This becomes problematic is if you need to embed the URL's in a JavaScript file. However in this case it usually points to a problem with your implementation, and the code in your JS files should accept the URL it will use as a parameter rather than embedding it in your external JS files.

Nick Albrecht
  • 16,607
  • 10
  • 66
  • 101