angular-hotkeys icon indicating copy to clipboard operation
angular-hotkeys copied to clipboard

On route reload, new directives bind before old ones unbind

Open avolkovi opened this issue 11 years ago • 4 comments

Looks like when angular does a route reload, it loads the new template alongside the old one, so if I have the same hotkey defined in multiple templates, the new template will bind before the old template unbinds and I will lose the hotkey.

The order in which things run ends up being:

  • angular route $reload
  • new template loaded, directive processed, hotkey added/bound
  • old template unloaded, $destroy fired, hotkey removed/unbound

I was able to work around this by marking my hotkeys as persistent: false and not removing them on $destroy (as the new ones have bound at that point) and instead manually calling purgeHotkeys() before I do the route reload, though a better solution might be to delay hotkey binding in the directive until $routeChangeSuccess or something, not exactly sure.

This problem is also present when declaring the hotkeys in the controller, since the new controller is processed before the old controller is destroyed.

We're using angular v1.2.9 if that matters, though I don't think this new behavior in ngRoute

avolkovi avatar Aug 25 '14 20:08 avolkovi

Cool find. I'll confirm via tests and take a look

chieffancypants avatar Aug 26 '14 00:08 chieffancypants

I'm assuming this only affects hotkeys created via directives, correct?

chieffancypants avatar Aug 26 '14 00:08 chieffancypants

I'm not sure if I'm having the same issue or not - I have the same hotkey defined on multiple scopes (same controller, loaded multiple times on the page), it all works until I destroy one of the scopes - then they all stop working.

      scope.$on('$destroy', function () {
        var i = boundScopes[scope.$id].length;
        while (i--) {
          _del(boundScopes[scope.$id][i]);
          delete boundScopes[scope.$id][i];
        }
      });

if I remove this part of the code, it stops breaking, so I'm assuming it's a problem in the _del function.

FWIW, I'm not using it as a directive, just call hotkeys.bindTo($scope).add(....) in my controller.

Madd0g avatar Jul 07 '15 12:07 Madd0g

I added a try/catch block around the offending code in the _del function in hotkeys. The below is my code. Would be cool if this gets fixed or at least the below workaround is added to hotkeys master branch.

  function _del (hotkey) {
    var combo = (hotkey instanceof Hotkey) ? hotkey.combo : hotkey;

    Mousetrap.unbind(combo);

    if (angular.isArray(combo)) {
      var retStatus = true;
      var i = combo.length;
      while (i--) {
        retStatus = _del(combo[i]) && retStatus;
      }
      return retStatus;
    } else {
      var index = scope.hotkeys.indexOf(_get(combo));

      if (index > -1) {
        // if the combo has other combos bound, don't unbind the whole thing, just the one combo:
        if (scope.hotkeys[index].combo.length > 1) {
          scope.hotkeys[index].combo.splice(scope.hotkeys[index].combo.indexOf(combo), 1);
        } else {

          // remove hotkey from bound scopes
          angular.forEach(boundScopes, function (boundScope) {
            try { //Added by traviskds
              var scopeIndex = boundScope.indexOf(scope.hotkeys[index]);
              if (scopeIndex !== -1) {
                boundScope.splice(scopeIndex, 1);
              }
            } catch (e) {} //Added by traviskds
          });

          scope.hotkeys.splice(index, 1);
        }
        return true;
      }
    }

    return false;

  }

traviskds27 avatar Jan 14 '16 20:01 traviskds27