Referencing server-generated fields in script

I had to fix a bug today in somebody else’s code that was caused by some Javascript referencing an element by id. The problem was that this id was assumed by the script to be “#Parameter1” whereas the server-side code generating it was:

@Html.TextBox("Parameter1", part.Parameter1)

If the template containing this code was alone on the page, there is a chance this might actually work, but in general it won’t: the html element resulting from the helper will most of the time have a prefix, giving a final client-side id looking like SomePrefix_Parameter1, so it was very brittle to hard-code that id in script.

A much better practice is to pass any such id as a parameter to a function call. Your script shouldn’t contain any hard-coded identifier, but should just expose an API that takes those ids as parameters. You should also do the same for any action URLs.

This should be adapted to any module pattern you may/should be using, but for simplicity, I’ll assume a global function is exposed by the script file:

function myModuleSetup(parameter1Id, someActionUrl) {
// do stuff with those parameters
}

Then you need to build a small dynamic block of script to initialize things from your cshtml template:

using (Script.Foot()) {
<script type="text/javascript">
$(function() {
myModuleSetup(
@Html.Raw(JsonConvert.SerializeObject(Html.Id("Parameter1"))),
@Html.Raw(JsonConvert.SerializeObject(Url.Action("SomeAction", "SomeController")))
);
});
</script>
}

There are some Orchard-isms in there (the using statement to send the script to the end of the page), but the code should be easy to follow. Note the usage of Json serialization to make sure things are going to get properly encoded no matter what.

This is much more stable, cleaner, and will avoid quite a few bugs.

No Comments