1

I have an MVC5, C# application using the Razor engine.

In my application I have a view, called "Index.cshtml". In this view I want to present a bootstrap modal to the user.

When presented to the user, this modal will have buttons that should be capable of running JS functions and methods using jQuery.

I have achieved this in the past using a dynamic method:

However, after discussion with the community, we concluded that was a poor design pattern. Therefore, I must ask, what is the easiet way of achieving this objective?

2
  • Why you choose modal? If it blockes ui in background maybe use full page instead. If it doesnt maybe render it as sidebar Commented Dec 5, 2014 at 11:35
  • I use a modal because I can avoid more requests to the server. Overall it is more efficient server side. Commented Dec 5, 2014 at 14:52

1 Answer 1

2

Having read your original thread, I think there was a mix up. TrueBlueAussie was saying that you should use delegated events.

$(document).on("click", "#savePackageChangesBtn", doSomething);

Instead of

$("#savePackageChangesBtn").on("click", doSomething);

Personally I don't see anything wrong with the way you are doing things, other than that. I'm writing an MVC app myself and using bootstrap modals for certain functions.

I have written two functions which create a modal Alert and modal Confirm, these are reusable.

function malert(options) {
    if (typeof options == 'string') { 
        // if all we got was a string, pass that string as a message param
        malert({ message: options });
    }
    else {
        var _options = $.extend({
            title: "Alert", 
            message: "", 
            cssclass: "",
            ok: "OK", 
            ok_class: "btn-primary",
            ok_icon: "ok-circle"
        }, options);

        // find out how many modals are currently VISIBLE on the page
        var level = $(".modal:visible").length;

        // if the title is Error, set our css class to error. Saves a little time
        if (_options.title == "Error")
            _options.cssclass = "error";

        // this is for nested modals, and I have a feeling there's a bug in here somewhere
        // I have encountered instances when the backdrop gets stuck over modals
        // use at your own risk!
        if (level > 0) {
            // move the backdrop in front of the last modal 
            $(".modal-backdrop").css({ "z-index": (1040 + (20 * level)) });
            // move our alert in front of that
            $(".modal.malert").show().removeClass("fade").css({ "z-index": (1050 + (20 * level)) });
        } else {
            $('.modal.malert').modal('show')
        }

        $('.modal.malert').addClass(_options.cssclass)
        $('.modal.malert .modal-header h5').html(_options.title);
        $('.modal.malert .modal-body p').html(_options.message);
        $('.modal.malert .modal-body .btnOK')
            .text('<i class="halflings ok-circle"></i>' + _options.ok)
            .click(function (e) {
                // this is an alert - all the ok button should ever do is close it. 
                // normally we'd just use data-dismiss="modal" but
                // we have to handle nested modals again
                e.preventDefault();
                if (level > 0) {
                    $(".modal-backdrop").css({ "z-index": (1040 + (20 * (level - 1))) });
                    $(".modal.malert").hide().addClass("fade").css({ "z-index": (1050 + (20 * (level - 1))) });
                } else {
                    $('.modal.malert').modal('hide');
                }
                $('.modal.malert').removeClass(_options.cssclass)
            });

        $('.modal.malert .modal-body .btnOK i').attr("class", "halflings").addClass(_options.ok_icon);
    }
}

HTML for modal alert window

<div class="modal slim fade malert" tabindex="-1" role="dialog" aria-hidden="true">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header">
                <h5>Alert</h5>
            </div>
            <div class="modal-body">
                <p></p>
                <a href="#" class="btn btn-primary btnOK"><i class="halflings ok-circle"></i>Ok</a>
            </div>
        </div>
    </div>
</div>

malert can be called in two ways. Either with full options

malert({ message: "File Not Found", title: "Error", ok_class: "btn-danger", ok_icon: "ban-circle" });

... or just with a message, which it will show within a standard alert modal

malert("Something terrible has happened!");

mconfirm is a little more complex.

function mconfirm(options, true_callback, false_callback) {
    // check for existing modal windows

    var _options = $.extend({
        title: "Confirm",
        message: "",
        cssclass: "",
        ok: "OK",
        ok_class: "btn-primary",
        ok_icon: "ok-circle",
        cancel: "Cancel",
        cancel_class: "btn-danger",
        cancel_icon: "ban-circle",
        true_callback: null,
        false_callback: null
    }, options);

    // find out how many modals are currently VISIBLE on the page
    var level = $(".modal:visible").length;

    // this is for nested modals, and I have a feeling there's a bug in here somewhere
    // I have encountered instances when the backdrop gets stuck over modals
    // use at your own risk!
    if (level > 0) {
        $(".modal.mconfirm .modal-body .field-validation-error").remove();
        $(".modal-backdrop").css({ "z-index": (1040 + (20 * level)) });
        $(".modal.mconfirm").show().removeClass("fade").css({ "z-index": (1050 + (20 * level)) });
    } else {
        $('.modal.mconfirm').modal('show')
    }

    $('.modal.mconfirm').addClass(_options.cssclass)
    $('.modal.mconfirm .modal-header h5').html(_options.title);
    $('.modal.mconfirm .modal-body p').html(_options.message);

    $('.modal.mconfirm .modal-body .btnCancel')
        .addClass(_options.cancel_class)
        .html('<i class="halflings ban-circle"></i>' + options.cancel) 
        .click(function (e) {
            e.preventDefault();
            if (level > 0) {
                $(".modal-backdrop").css({ "z-index": (1040 + (20 * (level - 1))) });
                $(".modal.mconfirm").hide().addClass("fade").css({ "z-index": (1050 + (20 * (level - 1))) });
            } else {
                $('.modal.mconfirm').modal('hide');
            }
            $('.modal.mconfirm').removeClass(_options.cssclass);
            $('.modal.mconfirm .modal-body .btnCancel').removeClass(_options.cancel_class);
            // cancel callback can be either a url or a function
            (typeof _options.false_callback == 'string') ? window.location.href = _options.false_callback : _options.false_callback();
        });
    $('.modal.mconfirm .modal-body .btnCancel i').attr("class", "halflings").addClass(_options.cancel_icon);


    $('.modal.mconfirm .modal-body .btnOK')
        .addClass(_options.ok_class)
        .html('<i class="halflings ok-circle"></i>' + _options.ok)
        .click(function (e) {
            e.preventDefault();
            if (level > 0) {
                $(".modal-backdrop").css({ "z-index": (1040 + (20 * (level - 1))) });
                $(".modal.mconfirm").hide().addClass("fade").css({ "z-index": (1050 + (20 * (level - 1))) });
            } else {
                $('.modal.mconfirm').modal('hide');
            }
            $('.modal.mconfirm').removeClass(_options.cssclass);
            $('.modal.mconfirm .modal-body .btnCancel').removeClass(_options.ok_class);
            // cancel callback can be either a url or a function
            (typeof _options.true_callback == 'string') ? window.location.href = _options.true_callback : _options.true_callback();
        });
    $('.modal.mconfirm .modal-body .btnOK i').attr("class", "halflings").addClass(_options.ok_icon);
}

HTML for modal confirm window

<div class="modal slim fade mconfirm" tabindex="-1" role="dialog" aria-hidden="true">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header">
                <h5>Confirm</h5>
            </div>
            <div class="modal-body">
                <p></p>
                <a href="#" class="btn btnCancel"><i class="halflings ban-circle"></i>Cancel</a>&nbsp;<a href="#" class="btn btnOK"><i class="halflings ok-circle"></i>Ok</a>
            </div>
        </div>
    </div>
</div>

mconfirm has two buttons - OK and Cancel. You can configure the text, icons and classes of those buttons, and provide a callback function for each one.

mconfirm({
        message: "Warning: this will delete all of your current message content. Continue?", title: "Upload HTML",
        ok: "Upload", ok_icon: "open", ok_class: "btn-danger",
        cancel: "Cancel", cancel_class: "btn-info",
        true_callback: function () {
            var formData = new FormData($('#upload_html')[0]);
            $.ajax({
                url: "/Messages/_HtmlUploadSingle",  //Server script to process data
                type: "POST",
                // Form data
                data: formData,
                //Options to tell jQuery not to process data or worry about content-type.
                dataType: "json",
                cache: false,
                contentType: false,
                processData: false
            })
            .done(function (json) {
                if (json.status == "success") {
                    $.get("/Messages/GetHTMLPart/" + json.MessageID, function (data) {
                        CKEDITOR.instances["editor_visual"].setData(data)
                    });
                }
                else {
                    malert({ message: json.error, title: "Error" });
                }
            });
        }
    });

The callbacks for OK / Cancel can be either a function or a URL. Sometimes you want to run some code, sometimes you just want to navigate away.

You will run into issues, as I did, if you want to launch nested bootstrap modals - open a second modal over the first one. I had trouble with this and had to write separate js to launch nested modals by adjusting the z-index of the overlay to move it in front of the previous modal.

You can see that in the code of each method - the "if (level > 0)" block.

I get a LOT of reuse out of these. I've called mconfirm maybe 15 times so far in the app. Where it breaks down is when I need to have other controls in a modal, like dropdowns or more than two buttons. Then it's not worth trying to reuse code - I just throw a separate modal into the view and some code to launch it

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.