添加具有特定回调的通用事件侦听器

Adding generic event listener with specific callback

在 Aurelia 视图模型组件中,我有以下 JQuery 代码可用于捕获 Ctrl+SCtrl+Enter 当模态可见时调用保存函数:

$(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();
      }
    }
  }
});

但是,我预见到将向 40 多个视图模型添加类似的功能,这看起来不是很枯燥,并且向我的每个视图模型添加了一些丑陋的代码。我想在单例 class 中创建一个通用的 addEventListener 函数,以便从我的每个视图中轻松调用。这是我的想法:

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();
        }
      }
    }
  });
}

然后,在我的各个组件中,我应该只需要以下实例化代码(在attached()组件生命周期中):

this.config.addListenerSave(this.edit_calendar, this.saveCalendar);

但是,这不起作用。 saveCalendar() 被调用但可能来自另一个 scope/context,所以我在 saveCalendar 中得到一个错误,它说:

"Cannot read property 'selectedId' of undefined".

这是指 saveCalendar() 代码 if (this.selectedId)...。我做错了什么?

最后,当我的 Aurelia 组件分离时,我是否也应该删除这个事件侦听器?怎么样?

我的另一个想法是使用 Aurelia 的 eventAggregator 创建一个始终监听 Ctrl+S[= 的全局事件监听器42=]或者Ctrl+回车然后在各个组件中发布可以订阅的消息

我成功实现了添加全局事件侦听器的替代解决方案,该侦听器使用 Aurelia 的 EventAggregator 共享 Ctrl+S/Ctrl+Enter。最初的问题仍然存在,但也许这不是最好的方法。这是我的解决方案:

config.js(全局单例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);
        }
      }
    });
  }
}

然后,在我的组件视图模型中 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();
      }
    });
  }
}

非常有效,现在我可以自由添加更多组件事件侦听器,甚至可以扩展功能为 Ctrl+F(用于查找)等添加全局侦听器

要回答您最初的问题,您的方向是正确的 - 但由于 this 在 JavaScript 中的语义,您需要绑定您的函数。 (如果您从 C# 的角度来看,认为 JavaScript 中的所有函数本质上都是扩展方法可能会有所帮助;因此,传递函数可能非常强大。)由于新的ES6 class 语法。

这应该可以缓解您的问题:

this.config.addListenerSave(this.edit_calendar, this.saveCalendar.bind(this));

就是说,您使用 Aurelia 的事件聚合器的解决方案更适合您的用例并且更具可扩展性。我想我会 post 这个答案来解决原来的问题,这只是一个功能范围的问题。