Using MVC to Avoid Magic Numbers in Your JavaScript

Hard-coding “magic numbers” in your code has always been a problem for several reasons: they’re not meaningful, they’re difficult to search for reliably, and the chance of using the wrong value is unacceptably high. The first improvement was preprocessor macros in C:

#define TICKETSTATE_DRAFT      1
#define TICKETSTATE_OPEN       2
#define TICKETSTATE_CLOSED     3
#define TICKETSTATE_CANCELLED  4

Clearly,

    if (ticket->state == TICKETSTATE_OPEN)

is much more meaningful (and therefore maintainable) than

    if (ticket->state == 2)

However, macros are not part of the language itself, occupy a flat namespace, are not visible when debugging, and are unknown to the IDE (Integrated Development Environment) and thus cannot auto-complete. So “enums,” which addressed all of the above drawbacks, were introduced:

    public enum TicketState
    {
        Draft = 1,
        Open,
        Closed,
        Cancelled,
    }

so your code can be written as

    public void CancelTicket(Ticket ticket)
    {
        if (ticket.State == TicketState.Open)
        {
            ticket.State = TicketState.Cancelled;
            ticket.Update();
        }
    }

Notice that each particular enum is scoped, so duplicate names can never clash — and since they’re self-incrementing by default, you don’t even have to specify values when declaring them. Now your C# code can easily use them, your IDE knows them, and they show up by name while debugging.

BUT they’re still tied to the language — the current technology of front-end and back-end leaves a disconnect which the IDE does not handle. Thanks to Razor’s understanding of your .cs files, you’re allowed to use an enum directly in the .cshtml code:

    <a href=”#” onclick=”export(this, @((int)(ExportType.PDF)));”>@(ExportType.PDF)</a>

which conveys both the integer value (shown as a parameter to the js function) and the name itself as a string, which is very convenient. You’re even able to use it within JavaScript code in your .cshtml file:

     function testPage(pageNumber) {
        if (pageNumber == @(MyApp.Common.PageEnums.Reports)) {
        }
    }

But, since developers have good reasons to place most of their JavaScript code in .js files and Razor does not process those, your C# enums cannot be understood there, so you’d seem to be out of luck. Happily, there’s an easy work-around. Declare an object inside a JavaScript function in a .cshtml file:

    <script type=”text/javascript”>

    var GlobalEnums = {};

    GlobalEnums.TicketState            = {};
    GlobalEnums.TicketState.Draft      = @((int)TicketState.Draft);
    GlobalEnums.TicketState.Open       = @((int)TicketState.Open);
    GlobalEnums.TicketState.Closed     = @((int)TicketState.Closed);
    GlobalEnums.TicketState.Cancelled  = @((int)TicketState.Cancelled);

    GlobalEnums.TicketPriority         = {};
    GlobalEnums.TicketPriority.Low     = @((int)TicketPriority.Low);
    GlobalEnums.TicketPriority.Medium  = @((int)TicketPriority.Medium);
    GlobalEnums.TicketPriority.High    = @((int)TicketPriority.High);
    GlobalEnums.TicketPriority.Urgent  = @((int)TicketPriority.Urgent);

    </script>

It’s best to put them into a separate file named Views/Shared/Constants.cshtml to make it easily include-able, with a

    @Html.Partial(“Constants”)

in an appropriate place of your Index.cshtml (or other view). The disadvantage, of course, is the manual copy and pasting necessary to create these assignments and the need to maintain them, but the benefit is certainly worth the cost: as you can now write code in a .js file such as

    if (ticketState == GlobalEnums.TicketState.Open) {

Another example is the communcation during an ajax call between back-end:

    public JsonResult Process(int id)
    {
        bool success = true;
        int numberProcessed = 0;

        // do something

        return this.Json(new
        {
            Success  = success,
            Count    = numberProcessed,
            NewState = TicketState.Closed
        });
    }

and front-end:

    function ProcessDone(result, textStatus, jqXHR) {
        if (result.NewState == GlobalEnums.TicketState.Closed) {
        }
    }

There are, of course, many other approaches easily found by Googling, but most of them are very high-end, involving writing back-end classes which select custom-attributed Types from GetExecutingAssembly() and adding methods to the controller to dynamically generate Javascript code. If that suits your needs, fine . . . but if an easy-to-maintain list constructed with simple cut and paste from the C# code is sufficient, avoid over-engineering.


 

What are some other benefits of avoiding magic numbers? Share your wisdom in a comment below or tweet us @EasyDynamicsWhile you’re here, make yourself comfortable and check out our blog home page to explore other technologies we use on a daily basis and the fixes we’ve solved in our day to day work. To make your life even easier, subscribe to our blog to get instant updates sent straight to your inbox:

{{cta(‘33985992-7ced-4ebd-b7c8-4fcb88ae3da4’)}}

Leave a Comment

Easy Dynamics Login