Enums are useful for flags or for any known limited set of inputs. Using them in C# allows a measure of protection in that invalid values will be disallowed at compile time. However runtime errors can still occur anytime you have serialiation of the values, for example when receiving requests to your API. To minimise the chance of errors, we need to keep the values in sync on both the server and client side.

A number of solutions can be applied depending on your project needs:

Option 1

Create copies of the numbers or enum strings on the client side. This litters your code base with “magic numbers” but it is the least effort.

Option 2

Create an API method to return a list of enums as you need them. You will mostly likely have to create one method per Enum type, or filter the returned list somehow. This is more effort than Option 1 but removes the magic number problem. However you now have to request these enums as you need them and either cache them client side for the session or repeat the request each time. If your API is consumed by multiple clients, this has the added benefit that it will allow them to match an int to a text value.

Option 3

Generate a JavaScript file with all the enums as the server starts up. This is also requires some effort while avoiding the issues of Options 1 and 2, and still giving the benefits of Option 2. (You can also serve this .js file via a JSON API endpoint if you wish.) The file can also be minified and bundled as well which saves on requests. The only requirement is that your server can write to the location where the JavaScript files are read from.

The rest of this post will look at my implementation of Option 3. First off we need some content to put in our file, then we can worry about saving it to disk. So get a list of enums that you need on the client side:

public enum Jedi
{
    Youngling = 0,
    Padawan = 1,
    Knight = 2,
    Master = 3
}

public enum LightSaber
{
    Blue = 0,
    Green = 1,
    Red = 2,
    Purple = 3
}

Now we need a basic string generator, which loads the enums and loops through them writing out both the string and int value.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public string GenerateFileContents(IEnumerable<Type> enumsToUse)
{
    StringBuilder sb = new StringBuilder();

    foreach (var enumType in enumsToUse)
    {
        var enumNames = Enum.GetNames(enumType);
        var enumValues = Enum.GetValues(enumType);

        sb.Append("var ");
        sb.Append(enumType.Name);
        sb.Append(" = {");
        sb.AppendLine();

        foreach (var value in enumValues)
        {
            var name = Enum.GetName(enumType, value).ToString();
            var intValue = (int)value;
            sb.Append(" " + name);
            sb.Append(": " + intValue);
            sb.Append(",");
        }

        sb.AppendLine();
        sb.Append("};");
        sb.AppendLine();
    }

    return sb.ToString();
}

This produces the following output:

var Jedi = {
 Youngling: 0, Padawan: 1, Knight: 2, Master: 3,
};
var LightSaber = {
 Blue: 0, Green: 1, Red: 2, Purple: 3,
};

The next step is to write this to disk so our web server can bundle and minify it. Assuming you are running a WebApi project, add the following to your Global.asax:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
IEnumerable<Type> enumsToUse = new List<Type>()
{
    typeof(Jedi),
    typeof(LightSaber),
};

var generator = new JavascriptEnumFileGeneration();
var contents = generator.GenerateFileContents(enumsToUse);

var filePath = Server.MapPath("Scripts") + "\\Enums.js";
if (System.IO.File.Exists(filePath))
{
    try
    {
        System.IO.File.WriteAllText(filePath, contents);
    }
    catch (UnauthorizedAccessException)
    {
        // Swallow because we are not able to do anything about it now.
    }
}

Then just add the Scripts\Enums.js to one of your existing JavaScript bundles in your BundleConfig.cs under AppStart. (For bonus points you can edit your _references.js file in your Scripts folder in to include Enums.js which will enable Intellisense for any included enums! Win right there!)

And boom! We now have these enum values from C# to use in our JavaScript! On the front end we can now define the select options in code. Assume we have the following html:

<form>
    <label for="jediType">Select Jedi training:</label>
    <select class="form-control" id="jediType"></select>
</form>

We can add the following JavaScript to populate the select as the page loads (or as needed);

1
2
3
4
5
6
7
8
9
$(document).ready(function () {
    var optionsStr = '<option>Select level</option>';

    $.each(Jedi, function (index, value) {
        optionsStr += '<option value="' + value + '">' + index + '</option>';
    });

    $('#jediType').append(optionsStr);
});

This gives the following (interactive) html:


If you add another enum value, the Enums.js file will be re-generated when the server recycles and the select list will now be auto-updated as the page is rendered. Using this enum value now allows you to be more specific and explicit in conditional statements. Consider the following code snippet (ala Option 1 implementation):

1
2
3
4
5
6
7
8
9
$(document).ready(function () {
     $('#jediType').on('change', function () {
        var value = $(this).val();

        if (value === '3') {
            alert('Master is very advanced, are you sure?');
        }
    });
});

On changing the select, the value that we are checking for is hardcoded (line 5) and provides no other context for a future developer. Now consider the next snippet:

1
2
3
if (value === Jedi.Master.toString()) {
    alert('Master is very advanced, are you sure?');
}

On line 1 we now know exactly what value we are checking for, but the text in the alert (line 2) is still hardcoded. So let’s extend the JS enums object to include the value and the text:

var Jedi = {
  Youngling : function () { var fn = function () { return 0; }; fn.Text = "Youngling"; fn.Value = 0; return fn; }(),
  Padawan : function () { var fn = function () { return 1; }; fn.Text = "Padawan"; fn.Value = 1; return fn; }(),
  Knight : function () { var fn = function () { return 2; }; fn.Text = "Knight"; fn.Value = 2; return fn; }(),
  Master : function () { var fn = function () { return 3; }; fn.Text = "Master"; fn.Value = 3; return fn; }(),
};
var LightSaber = {
  Blue : function () { var fn = function () { return 0; }; fn.Text = "Blue"; fn.Value = 0; return fn; }(),
  Green : function () { var fn = function () { return 1; }; fn.Text = "Green"; fn.Value = 1; return fn; }(),
  Red : function () { var fn = function () { return 2; }; fn.Text = "Red"; fn.Value = 2; return fn; }(),
  Purple : function () { var fn = function () { return 3; }; fn.Text = "Purple"; fn.Value = 3; return fn; }(),
};

We can now get the text value or the int value and using a function object instead of an object literal allows us to preserve the value from being changed. (We wrap the whole line in an IIFE to preserve the function scope.)

var value = Jedi.Knight(); // value = 2
var value = Jedi.Padawan.Value; // value = 1
var text = Jedi.Youngling.Text; // text = "Youngling"

Our code snippet now becomes:

1
2
3
if (value === Jedi.Master.Value.toString()) {
    alert(Jedi.Master.Text + ' is very advanced, are you sure?');
}

Bingo! Now our int value and our text value can be provided by a key lookup on our enum object. If anything changes, it will be updated automatically.

One thing that we have not yet handled, is when the enum text contains special characters, or we want to customise the display text to be used. Consider the below enum definition for TIE Fighters:

public enum TIESeriesFigther
{
    [Description("TIE Starfighter")]
    TIEStarfighter = 0,
    [Description("TIE/LN Starfighter (Light Duty)")]
    TIELNStarfighter_Light_Duty = 1,
    [Description("Super TIE")]
    SuperTIE = 2,
    [Description("TIE/IN Interceptor (Emperor's Royal Guard)")]
    TIEINInterceptor_Emperors_Royal_Guard = 3,
    [Description("TIE Hunter")]
    TIEHunter = 4,
    [Description("TIE Heavy Bomber")]
    TIEHeavyBomber = 5
}

The Description attribute allows us to change the default text used and gives us the option of special characters and more formatting. We now have to update the generation of the javascript file to account for this metadata. We can update the enumValues for loop (lines 15-22 from above) with the following:

1
2
3
4
5
6
7
8
9
10
11
foreach (var value in enumValues)
{
    var name = Enum.GetName(enumType, value).ToString();
    var description = ((Enum)value).ToDescription();
    var intValue = (int)value;
    sb.AppendFormat(
        @"  {0} : function () {{ var fn = function () {{ return { 2 }; }}; fn.Text = ""{1}""; fn.Value = { 2 }; fn.Key = ""{0}""; return fn; }}()",
        name, description, intValue);
    sb.Append(",");
    sb.AppendLine();
}

The ToDescription() method call is simply an extension method for enums and looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
using System.ComponentModel;

public static class Extensions
{
    public static string ToDescription(this Enum source)
    {
        var field = source.GetType().GetField(source.ToString());
        if (field != null)
        {
            var attribute = Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)) as DescriptionAttribute;
            return attribute == null ? source.ToString() : attribute.Description;
        }
        return string.Empty;
    }
}

The ToDescription() method call now replaces to the original ToString() call, but we keep the original enum name as the key property in the JavaScript in case we need it in future. The resulting JavaScript file now looks like this:

var Jedi = {
  Youngling : function () { var fn = function () { return 0; }; fn.Text = "Youngling"; fn.Value = 0; fn.Key = "Youngling"; return fn; }(),
  Padawan : function () { var fn = function () { return 1; }; fn.Text = "Padawan"; fn.Value = 1; fn.Key = "Padawan"; return fn; }(),
  Knight : function () { var fn = function () { return 2; }; fn.Text = "Knight"; fn.Value = 2; fn.Key = "Knight"; return fn; }(),
  Master : function () { var fn = function () { return 3; }; fn.Text = "Master"; fn.Value = 3; fn.Key = "Master"; return fn; }(),
};
var LightSaber = {
  Blue : function () { var fn = function () { return 0; }; fn.Text = "Blue"; fn.Value = 0; fn.Key = "Blue"; return fn; }(),
  Green : function () { var fn = function () { return 1; }; fn.Text = "Green"; fn.Value = 1; fn.Key = "Green"; return fn; }(),
  Red : function () { var fn = function () { return 2; }; fn.Text = "Red"; fn.Value = 2; fn.Key = "Red"; return fn; }(),
  Purple : function () { var fn = function () { return 3; }; fn.Text = "Purple"; fn.Value = 3; fn.Key = "Purple"; return fn; }(),
};
var TIESeriesFigther = {
  TIEStarfighter : function () { var fn = function () { return 0; }; fn.Text = "TIE Starfighter"; fn.Value = 0; fn.Key = "TIEStarfighter"; return fn; }(),
  TIELNStarfighter_Light_Duty : function () { var fn = function () { return 1; }; fn.Text = "TIE/LN Starfighter (Light Duty)"; fn.Value = 1; fn.Key = "TIELNStarfighter_Light_Duty"; return fn; }(),
  SuperTIE : function () { var fn = function () { return 2; }; fn.Text = "Super TIE"; fn.Value = 2; fn.Key = "SuperTIE"; return fn; }(),
  TIEINInterceptor_Emperors_Royal_Guard : function () { var fn = function () { return 3; }; fn.Text = "TIE/IN Interceptor (Emperor's Royal Guard)"; fn.Value = 3; fn.Key = "TIEINInterceptor_Emperors_Royal_Guard"; return fn; }(),
  TIEHunter : function () { var fn = function () { return 4; }; fn.Text = "TIE Hunter"; fn.Value = 4; fn.Key = "TIEHunter"; return fn; }(),
  TIEHeavyBomber : function () { var fn = function () { return 5; }; fn.Text = "TIE Heavy Bomber"; fn.Value = 5; fn.Key = "TIEHeavyBomber"; return fn; }(),
};

Great! Now we have now met all the requirements we discussed at the beginning of this post, as well as some extended use cases. The source code for this post is also on GitHub, if you want to check it out and provide input. PR’s always welcome! ;)