In my MVC views I frequently need to reference various elements from JavaScript, and for that purpose I define the id attribute for those elements. This is often in conjunction with Ajax calls that update content of various container elements.
A short example (Razor):
<table id="PersonList">
...
</table>
<div id="PersonDetails">
<!-- This would be loaded dynamically using Ajax -->
<input type="hidden" name="CurrentPersonId" value="@Model.PersonId">
...
</div>
This contains three "magic strings": "PersonList", "PersonDetails" and "CurrentPersonId". An Ajax call might look like this:
$('#PersonDetails').load('@Url.Action("GetPersonDetails")', { PersonId: ... });
Again, the magic string "PersonDetails" appears.
Not good!
Can anyone suggest some "best practice" to define these magics string in a single place and use them in 1) the views, 2) preferably static JavaScript files that implement Ajax calls etc, and 3) CSS files?
I'm, thinking perhaps _Layout.cshtml could include an partial view that defines the magic strings for that controller or even for a specific action. It would examine what controller and/or action called it, and call the appropriate partial view based on that. I do something similar for .css and static .js already, letting me simply add Person.css and have that automatically included for all views for the Person controller.
The partial view would do something like this:
@{
const string PersonListId = "PersonList";
const string PersonDetailsId = "PersonDetails";
const string CurrentPersonIdName = "CurrentPersonId";
}
<script type="text/javascript">
NamesAndIds = {};
NamesAndIds.PersonListId = '@Html.Raw(PersonListId)';
NamesAndIds.PersonDetailsId = '@Html.Raw(PersonDetailsId)';
NamesAndIds.CurrentPersonIdName = '@Html.Raw(CurrentPersonIdName)';
</script>
This should let Razor code use the C# string consts to generate appropriate HTML, and static JavaScript files could reference NamesAndIds in jQuery selectors etc. (Assumes that the consts defined in the partial view will be available in the calling view, which I doubt (haven't checked it yet)... How to use them in .css files I don't know.
Any better suggestions? How do you handle this problem?
I hope someone can come up with something better, but this is at least something.
In the main (non-partial) view I have a section at the top that defines the ids and names I need to use in multiple places in C#, HTML and JavaScript:
@{
const string PersonListId = "PersonList";
const string PersonDetailsId = "PersonDetails";
const string CurrentPersonIdName = "CurrentPersonId";
}
At the bottom, I have a script section that assigns the strings to suitable namespace container objects:
<script type="text/javascript">
MyNamespace = {};
MyNamespace.Ids = {};
MyNamespace.Names = {};
MyNamespace.Ids.PersonList = '@Html.Raw(PersonListId)';
MyNamespace.Ids.PersonDetails = '@Html.Raw(PersonDetailsId)';
MyNamespace.Names.CurrentPersonId = '@Html.Raw(CurrentPersonIdName)';
</script>
In each partial view that introduces additional items that I need to reference by id or name, I add similar code to extend MyNamespace.Ids
and MyNamespace.Names
with the required strings.
Now I can use the C# string constants in Razor view code to generate markup with the right ids and names and I can write regular static JavaScript files that reference MyNamespace.Ids
and MyNamespace.Names
to find the right ids and names, e.g. in jQuery selectors.
I also added similar stuff for action URLs that my Ajax calls use, and put them in MyNamespace.Urls
:
MyNamespace.Urls.GetPerson = '@Html.Raw(Url.Action("GetPerson"))';
It's not ideal but it's straightforward and solves the most pressing issue of magic strings scattered all over the place. It will not detect errors at compile time, but renaming items will require a single string to be renamed at a single place, and if I rename or misspell MyNamespace.Ids.Something
it will at least generate a runtime JavaScript error that can be seen in a JS console or similar.