I'm creating a contextual menu in JavaScript for a web app. The menu can appear in numerous contexts, and have different choices. I could have a different function for each context/choice:
grid1_delete()
grid1_duplicate()
grid2_delete()
grid2_add()
grid2_duplicate()
and hard code those in as the menu is being built. The thing I don't like about that is there will probably be a lot of repeated code. So I was thinking about using a dispatcher function, but it's resulting in potentially long, nested switch statement thus:
function contextMenuClick(context, menuItem) {
var action = menuItem.innerHTML;
switch (context) {
case 'grid1':
switch(action) {
case('delete'):
// do delete for grid1
break;
case('duplicate'):
// do duplicate for grid1
break;
default:
console.log('undefined action in contextMenuClick/grid1: ' + context);
}
break;
case 'grid2':
switch(action) {
case('add'):
// do add for grid2
break;
case('delete'):
// do delete for grid2
break;
case('duplicate'):
// do duplicate for grid2
break;
default:
console.log('undefined action in contextMenuClick/grid2: ' + context);
}
break;
default:
console.log('undefined context in contextMenuClick: ' + context);
}
Yuck. There's got to be a better way. Maybe the dispatcher is more trouble than it's worth. I've looked at some of the related posts, but I'm not quite getting how to apply them to this exact situation.
Switch statements are very rarely necessary in Javascript. In general, you can just use objects like dictionaries/maps and do the lookup directly: foo.bar
is equivalent to foo['bar']
.
Also, for "global" variables, some_global_func()
is equivalent to window.some_global_func()
, which can also be written as var f = 'some_global_func'; window[f]()
: you don't ever need eval
in order to select a variable or call a function dynamically based on its name. In general, when doing that, though, you should prefer to store the function in an object rather than at global scope (i.e. in the window
object).
So, assuming that grid1_delete
and grid2_delete
are fundamentally different and can't be combined into a generic function, you could do something like the following without changing your code much:
var grid_actions = {
'grid1': {
'delete': function() { /* ... */ },
'duplicate': function() { /* ... */ }
},
'grid2': {
'delete': function() { /* ... */ },
'add': function() { /* ... */ },
'duplicate': function() { /* ... */ }
}
}
function contextMenuClick(context, menuItem) {
var action = menuItem.innerHtml;
if (context in grid_actions) {
if (action in grid_actions[context]) {
grid_actions[context][action]();
} else {
console.log('undefined action in contextMenuClick/' + context + ': ' + action);
}
} else {
console.log('undefined context in contextMenuClick: ' + context);
}
}
A better solution, though, is to refactor things to have have these functions be methods of objects for each context, like @le dorfier suggests.
How about passing an actual object reference in for "context" instead of just a string? That way, you have just one switch statement:
function contextMenuClick(grid, menuItem) {
var action = menuItem.innerHTML;
switch(action) {
case('delete'):
grid.delete();
break;
case('duplicate'):
grid.duplicate();
break;
}
}
Better yet, simply bind the handler directly to the correct object/method.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With