Testing

webTiger Logo Wide

Replacing ASP.NET AJAX Control Toolkit With Bootstrap and jQuery UI in MVC

Many ASP.NET 2 projects will have utilised the AJAX Control Toolkit for more dynamic behaviours and the performance gains of partial page refreshes. A common issue with having used that toolkit when upgrading an ASP.NET 2 project to ASP.NET MVC is how to replicate the controls it offered without adding a lot of additional work to the upgrade project.

When migrating from ASP.NET to MVC as a development platform, one of the my main concerns was how to replace the functionality offered by the AJAX Control Toolkit in ASP.NET without increasing my development workload significantly. I wanted to make a clean break and not rely on hybrid ASP.NET and MVC solutions that may never get fully updated.

Writing a lot of rich web applications (RIAs), I needed to retain the RIA style, look, and feel with the modal popups, accordions and tab containers I had previously used. Due to tight time constraints, and feeling it was quicker to hand-craft exactly what I needed than investigate broader 3rd party libraries, I initially wrote my own code library that squirted out pre-fabricated HTML and JavaScript at runtime as a replacement for the AJAX Control Toolkit functionality I was using (mainly message box and AJAX update panel) in my MVC markup. I later added some more functionality to that library in the form of an accordion, a tab container, etc. on another high pressure project.

I supplemented the C# code library for generating the HTML/JS with a JavaScript library that provided a hand-crafted auto-completion and date picker implementation. In hindsight, this was a pretty nasty solution but it was an acceptable workaround while timescales continued to be tight and there wasn’t time to investigate a better, framework based, alternative.

That code library has actually survived years of use and it is only recently that I’ve found the time to investigate a better (or at least more eloquent) solution. jQuery provides a library, jQuery UI, that offers many replacements to the rich web controls and extenders offered by the AJAX Control Toolkit. It offers replacement accordion, auto-completion, date picker, tab container, and many others.

For jQuery UI, you need to download and add the jQuery UI CSS and JavaScript files to your project before you can use any of the widgets. To do this download the latest stable version from here and then add the files to your project as follows:

  • Copy the jquery-ui.css and jquery-ui-min.css files into the project’s Content folder.
  • Copy any theme specific files to their appropriate sub-folder under the project’s Content folder. For example images that are required by the jQuery UI style-sheets may need to be copied into the Content/images sub-folder.
  • Copy the jquery-ui.js and jquery-ui-min.js files into the project’s Scripts folder.
  • Open the App_Start/BundleConfig.cs file.
  • Add bundle entries for the jQuery UI JavaScript packages and CSS style-sheets:
bundles.Add(
    new ScriptBundle("~/bundles/jqueryui")
        .Include("~/Scripts/jquery-ui.js"));
bundles.Add(
    new StyleBundle("~/Content/jqueryui/css")
        .Include("~/Content/jquery-ui.css"));Code language: C# (cs)
  • Finally, add the necessary script and style rendering to the web page (or parent layout page):
@Styles.Render("~/Content/jqueryui/css")
@Scripts.Render("~/bundles/jquery")
@Scripts.Render("~/bundles/jqueryui")Code language: HTML, XML (xml)

NOTE: The above instructions are for jQuery UI v1.11.4. Earlier or later versions may have slightly different file structures and locations. If several CSS or JS files are required, the bundle definitions can be simplified with wildcards. For example:

bundles.Add(
    new StyleBundle("~/Content/jqueryui/css")
        .Include("~/Content/jqueryui/*.css", "~/Content/jquery-ui.css"));Code language: C# (cs)
@RenderSection("scripts", required: false)Code language: HTML, XML (xml)

After the project is configured to use jQuery UI, consuming widgets is simple. For example:

<div id="myAccordion">
    <h3>Pane 1</h3>
    <div><p>Some content in pane 1.</p></div>
    <h3>Pane 2</h3>
    <div><p>Some content in pane 2.</p></div>
    <h3>Pane 3</h3>
    <div><p>Some content in pane 3.</p></div>
</div>
<script type="text/javascript">
    $(function() { $( '#myAccordion' ).accordion(); });
</script>Code language: HTML, XML (xml)

Even though jQuery UI does offer a very versatile modal dialog, I had already looked into the Bootstrap ‘modal’ and was using that instead since it came bundled into the default MVC code project – plus I prefer it. Both can be defined in pure HTML and JavaScript.

Bootstrap modal:

<div id="myBootModal" class="modal fade text-center" role="dialog">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header text-left">
                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                    <span aria-hidden="true">&times;</span>
                </button>
                Bootstrap Modal (Title)
            </div>
            <div class="modal-body text-left">
                <div class="row">
                    <div class="col-md-12">
                        Are you sure you want to discard any unsaved changes?
                    </div>
                </div>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-success" data-dismiss="modal">Yes</button>
                <button type="button" class="btn btn-danger" data-dismiss="modal">No</button>
            </div>
        </div>
    </div>
</div>
<script type="text/javascript">
    window.onload = function() {
        $('#myBootModal').modal();
    }
</script>Code language: HTML, XML (xml)

jQuery UI modal, in case you prefer it:

<script type='text/javascript'>
    $(function () {
        $('#myJQueryModal').dialog({
            resizable: false,
            height: 140,
            modal: true,
            buttons: {
                'Yes': function () { $(this).dialog('close'); },
                'No': function () { $(this).dialog('close'); }
            }
        });
    });
</script>
<div id="myJQueryModal" title="jQuery Modal (Title)">
    <p>
        <span class="ui-icon ui-icon-alert" style="float: left; margin: 0 7px 20px 0;"></span>
        Are you sure you want to do that?
    </p>
</div>Code language: HTML, XML (xml)

To simplify my coding requirements, I took things one step further and wrapped my Bootstrap based re-usable modals in JavaScript functions in a separate JS file. This way I don’t have to re-author the HTML each time and instead my implementation is reduced to one or two lines of JavaScript. For example:

/*** Beginning of section that would be in separate JS file. ***/

function guid() {
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
        var r = Math.random() * 16 | 0;
        var v = (c === 'x') ? r : (r &amp; 0x3 | 0x8);
        return v.toString(16);
    });
}

function popup(
    title, message, leftButtonText, rightButtonText, leftButtonAction, 
    rightButtonAction) {

    if (typeof title === 'undefined') throw new Error('A title must be supplied.');
    if (typeof message === 'undefined') throw new Error('A message body must be supplied.');

    if (typeof leftButtonText === 'undefined' || 
        leftButtonText === null || 
        leftButtonText.toString().trim() === '') {
        leftButtonText = 'OK';
    }

    if (typeof rightButtonText === 'undefined' || 
        rightButtonText === null || 
        rightButtonText.toString().trim() === '') {
        rightButtonText = '';
    }

    var modalName = 'modal_' + guid();

    var modalPopup = $('<div id="' + modalName + '" class="modal fade text-center" role="dialog"></div>');
    $('body').append(modalPopup);
    var dialog = $('<div class="modal-dialog" />');
    modalPopup.append(dialog);

    var outer = $('<div class="modal-content" />');
    dialog.append(outer);
    var header = $('<div class="modal-header text-left" />');
    outer.append(header);
    header.append('<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>');
    header.append(title);
    var inner = $('<div class="modal-body text-left" />');
    outer.append(inner);
    inner.append('<div class="row"><div class="col-md-12"><span>' + message + '</span></div></div>');

    var buttons = $('<div class="modal-footer" />');
    outer.append(buttons);
    var leftButton = $('<button type="button" class="btn btn-default" data-dismiss="modal">' + leftButtonText + '</button>');
    buttons.append(leftButton);

    if (leftButtonAction) {
        leftButton.click(function () {
            leftButtonAction();
            $('body').remove('#' + modalName);
        });
    } else {
        leftButton.click(function () { $('body').remove('#' + modalName); });
    }

    if (!(rightButtonText === '')) {
        buttons.append('&nbsp;');
        var rightButton = $('<button type="button" class="btn btn-default" data-dismiss="modal">' + rightButtonText + '</button>');
        buttons.append(rightButton);

        if (rightButtonAction) {
            rightButton.click(function () {
                rightButtonAction();
                $('body').remove('#' + modalName);
            });
        } else {
            rightButton.click(function () { $('body').remove('#' + modalName); });
        }
    }

    modalPopup.modal();
}
Code language: JavaScript (javascript)

Consuming the modal functionality in HTML/CSHTML markup is then simple…

<script type="text/javascript">
    function handleDoingSomething() {
        // Code to handle button click omitted for brevity.
    }

    function showModal() { 
        popup(
            'My Modal', 
            'Are you sure you want to do that?', 
            "Yes", 
            "No", 
            handleDoingSomething, 
            null); 
    }
</script>
<button type="button" onclick="showModal">Show Modal</button>Code language: HTML, XML (xml)