Reputation: 17574
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?
Upvotes: 1
Views: 989
Reputation: 9645
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> <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
Upvotes: 2