Reputation: 2777
In an Aurelia viewmodel component, I have the following JQuery code that works to capture Ctrl+S or Ctrl+Enter while a modal is visible and call the save function:
$(window).bind('keydown', function(event) {
if (event.ctrlKey || event.metaKey) { // Ctrl + ___
if ((event.which == 83) || (event.which == 115) || (event.which == 10) || (event.which == 13)) { // Ctrl+Enter or Ctrl+S
// Save button
event.preventDefault();
if ($(self.edit_calendar).is(':visible')) {
self.saveCalendar();
}
}
}
});
However, I foresee adding a similar function to 40+ viewmodels, and that doesn't seem very DRY and adds some ugly code to each of my viewmodels. I would like to create a generic addEventListener function in a singleton class to easily call from each of my views. Here's what I have in mind:
addListenerSave(visible, callback) {
// Add an event listener to redirect keyboard shortcuts to specific actions
console.log("addListenerSave()");
$(window).bind('keydown', function(event) {
if (event.ctrlKey || event.metaKey) { // Ctrl + ___
if ((event.which == 83) || (event.which == 115) || (event.which == 10) || (event.which == 13)) { // Ctrl+Enter or Ctrl+S
// Save button
event.preventDefault();
if ($(visible).is(':visible')) {
console.log("Keyboard shortcut: Save");
callback();
}
}
}
});
}
Then, in my individual components, I should only need the following code on instantiation (in the attached()
component life cycle):
this.config.addListenerSave(this.edit_calendar, this.saveCalendar);
However, this does not work. saveCalendar()
is called but maybe from another scope/context, so I get an error inside saveCalendar
that says:
"Cannot read property 'selectedId' of undefined".
This is referring to the saveCalendar()
code if (this.selectedId)...
. What am I doing wrong?
Finally, should I also be removing this event listener when my Aurelia component is detached? How?
One alternate idea I had was to use Aurelia's eventAggregator
to create a global event listener that always is listening for Ctrl+S or Ctrl+Enter and then publishing a message that can be subscribed in each component.
Upvotes: 0
Views: 2488
Reputation:
To answer your original question, you're on the right track - but due to the semantics of this
in JavaScript, you'll need to bind your function. (If you're coming from a C# perspective, it may help to think that all functions in JavaScript are essentially extension methods; as such, passing functions can be VERY powerful.) It's easy to miss this because of the new ES6 class syntax.
This should work to mitigate your issue:
this.config.addListenerSave(this.edit_calendar, this.saveCalendar.bind(this));
That said, your solution using Aurelia's Event Aggregator is a much better fit for your use case and much more scalable. I thought I'd post this answer to address the original issue, which was simply a matter of function scope.
Upvotes: 1
Reputation: 2777
I successfully implemented the alternate solution of adding a global event listener that uses Aurelia's EventAggregator to share Ctrl+S/Ctrl+Enter. Original question still stands but perhaps it wasn't the best approach anyway. Here's my solution:
config.js (global singleton class)
@inject(EventAggregator)
export class Config {
constructor(eventAggregator) {
var self = this;
this.eventAggregator = eventAggregator;
// listen for Ctrl+S or Ctrl+Enter and publish event
window.addEventListener("keydown", function(event) {
if (event.ctrlKey || event.metaKey) { // Ctrl + ___
if ((event.keyCode == 83) || (event.keyCode == 115) || (event.keyCode == 10) || (event.keyCode == 13)) { // Ctrl+Enter or Ctrl+S
// Save button
console.log("Publishing ewKeyboardShortcutSave...");
event.preventDefault();
self.eventAggregator.publish('ewKeyboardShortcutSave', true);
}
}
});
}
}
Then, inside my component viewmodel calendar.js:
@inject(EventAggregator)
export class Calendar {
constructor(eventAggregator) {
this.eventAggregator = eventAggregator;
}
attached() {
var self = this;
// Ctrl+Enter is save
this.eventAggregator.subscribe('ewKeyboardShortcutSave', response => {
console.log("I heard ewKeyboardShortcutSave: " + response);
if ($(self.edit_calendar).is(':visible')) {
self.saveCalendar();
}
});
}
}
Works like a charm, and now I can freely add more component event listeners and even extend the functionality to add a global listener for Ctrl+F (for find), etc.
Upvotes: 0