It's been quite some time since I've done much with jQuery (or javascript in general). I'm trying to take our app from having all methods on the global scope, to a point where everything is in it's own namespace.
The approach I'm taking is to use a hybrid of the revealing module pattern, and the object literal pattern.
I'm using the object literal pattern on each individual page, and I'm using it to simply setup variables that come from the server (ASP.NET MVC Razor)
var st = st || {};
st.SharedContextMenuCommon = {
ContextMenuControllerName: '@Model.ControllerName',
BindingTargetName: '@Model.TargetName',
StratosphereGlobalImageUrl: '@Web_Helpers.StratosphereImageUrl("")' // yes this is empty since we only need the root. we can't pass a JS variable to Razor
};
From here, I have an external file that does all the heavy lifting. In this particular circumstance, I have a Kendo ContextMenu and I need it to initialize it's listeners (jQuery) from within the nested namespace.
If I remove all of the namespace bits, this code works as expected, however, when I use the Revealing Module pattern, the listener doesn't trigger on the 'click' event.
var st = st || {};
st.SharedContextMenu = (function() {
// check to see if menu exists
var menu = $("#contextMenu");
var initMenu = function() {
menu = $("#contextMenu").kendoContextMenu({
orientation: 'vertical',
alignToAnchor: true,
filter: ".contextMenu",
showOn: "click",
animation: {
open: {
effects: "fadeIn"
},
duration: 250
},
select: function(e) {
this.close(); // close the context menu
var action = $(e.item).find("[data-action]").data("action"); // extract the javascript string that is to be actioned on
var id1 = e.target.dataset.recordid; // extract the specific record ID (typically a GUID)
var id2 = e.target.dataset.recordidalt; // extract the specific alt record ID (typically a GUID)
var func = new Function(String.format(action, id1, id2)); // format the action (if the string is a formattable)
return (func()); // execute the string. This is essentially like EVAL, but since this is an internal app, it should be safe.
}
});
};
// only init the menu if it exists.
if (menu) {
initMenu();
$('#' + st.SharedContextMenuCommon.BindingTargetName).on('click', '.contextMenu', function() {
var recordId = $(this).data('recordid');
$.getJSON(st.SharedContextMenuCommon.ContextMenuControllerName + '/?recordId=' + recordId, function(data) {
var contextMenu = $('#contextMenu').data('kendoContextMenu');
var items = [];
$.each(data, function(key, value) {
items.push({
text: '<span data-action="' + value.OnClickJavascript + '">' + value.Text + '</span>',
encoded: false,
imageUrl: st.SharedContextMenuCommon.StratosphereGlobalImageUrl + value.Image
});
});
contextMenu.setOptions({
dataSource: items
});
});
});
}
})();
Can someone point me to the piece I'm missing?
Alright, so I had it nearly right... turns out it still needed to be nested in a document ready call.
"USE STRICT";
var st = st || {};
$(document).ready(function () {
setTimeout(function () {
st.SharedContextMenu = (function () {
// check to see if menu exists
var menu = $("#stratosphereContextMenu");
var onMenuItemSelected = function (e) {
this.close(); // close the context menu
var action = $(e.item).find("[data-action]").data("action"); // extract the javascript string that is to be actioned on
var id1 = e.target.dataset.recordid; // extract the specific record ID (typically a GUID)
var id2 = e.target.dataset.recordidalt; // extract the specific alt record ID (typically a GUID)
var func = new Function(String.format(action, id1, id2)); // format the action (if the string is a formattable)
return (func()); // execute the string. This is essentially like EVAL, but since this is an internal app, it should be safe.
};
var initMenu = function () {
menu = $("#stratosphereContextMenu").kendoContextMenu({
orientation: 'vertical',
alignToAnchor: true,
filter: ".contextMenu",
showOn: "click",
animation: {
open: {
effects: "fadeIn"
},
duration: 250
},
select: onMenuItemSelected
});
};
// only init the menu if it exists.
if (menu) {
initMenu();
$('#' + st.SharedContextMenuCommon.BindingTargetName).on('click', '.contextMenu', function () {
var recordId = $(this).data('recordid');
$.getJSON(st.SharedContextMenuCommon.ContextMenuControllerName + '/?recordId=' + recordId, function (data) {
var contextMenu = $('#stratosphereContextMenu').data('kendoContextMenu');
var items = [];
$.each(data, function (key, value) {
items.push({
text: '<span data-action="' + value.OnClickJavascript + '">' + value.Text + '</span>',
encoded: false,
imageUrl: st.SharedContextMenuCommon.StratosphereGlobalImageUrl + value.Image
});
});
contextMenu.setOptions({
dataSource: items
});
});
});
}
})();
}, 0);
});