为什么都在这个指令设定的按键获取的最后一个覆盖? [英] Why do all of the keybindings in this directive get overwritten by the last one?

查看:74
本文介绍了为什么都在这个指令设定的按键获取的最后一个覆盖?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图创建结合特定键presses到在控制器的范围指定功能的指令,但所有的回调函数似乎通过在含有绑定对象中的最后的回调函数来覆盖。我已经使用keymaster.js和mousetrap.js具有相同的结果绑定的事件受审。

code中的jsfiddle

JavaScript的code:

  angular.module('应用',['指令','控制器']);angular.module('指令',[])
.directive(键preSS',[功能(){
    返回功能(范围,元素,ATTRS){
        //执行console.log(范围,元素,ATTRS);
        var属性= $范围的eval(attrs.key preSS ||'{}')。
        为(在属性变种K){
            的console.log('结合'+ K +'为'+属性[K]);
            Mousetrap.bind(K,函数(){返回属性[K](范围元素);});
        }
    };
}]);angular.module(控制器,[])
.controller('TodoController',函数($范围){
    $ scope.shortcuts = {
        W:函数(){的console.log('W'); },
        'S':函数(){的console.log('S'); },
        '一个':功能(){的console.log('一个'); },
        D:函数(){的console.log('D'); }
    };
});

HTML文件:

 < HTML和GT;
  < HEAD>
    &所述; SCRIPT SRC =htt​​p://ajax.googleapis.com/ajax/libs/angularjs/1.0.3/angular.js>&下; /脚本>
    &所述; SCRIPT SRC =htt​​ps://raw.github.com/ccampbell/mousetrap/master/mousetrap.min.js>&下; /脚本>
    <脚本SRC =/ JavaScript的/ app.js>< / SCRIPT>
  < /头>
  <身体GT;
    < D​​IV NG-应用=应用程序>
      < D​​IV NG控制器='TodoController键preSS ='捷径'>富< / DIV>
    < / DIV>
  < /身体GT;
< / HTML>

为什么是'd'总是被写入控制台,无论我是否preSS'W','一','S',或'D'?


解决方案

您已经堕落为一个共同陷阱:在JavaScript中的变量始终是功能范围。当你这样做:

 为(在属性变种K){
    的console.log('结合'+ K +'为'+属性[K]);
    Mousetrap.bind(K,函数(){返回属性[K](范围元素);});
}

使用了绑定(),你创建四种关闭所有封盖变量 K 丁他们都接近过的相同的变量的。你不会得到循环每次运行一个新的。因为的的k 立即使用的的console.log 完美的作品。封不评估 K ,直到它的实际运行,到那时,它的值已更改为不管它是什么,当回路完成。

根据您的目标受众,是迄今为止最简单的方法来解决这个问题是使用而不是 VAR 确实块作用域(工作约你如何期望),但是是一个相当新的发明,我不知道它是如何很好的支持。

否则,得到一个新的范围,需要一个新的功能:

 为(在属性变种K){
    (函数(K){
        的console.log('结合'+ K +'为'+属性[K]);
        Mousetrap.bind(K,函数(){返回属性[K](范围元素);});
    })(k)的;
}

这传递外 K 给函数的内在 K ,其中每一次将是一个不同的变量。你也可以拆了这一点进一点factory函数,但对于一些这个小,我不会理会。

I'm attempting to create a directive that binds specific keypresses to functions specified in the scope of a controller, but all of the callback functions seem to be overridden by the last callback function in the object containing the bindings. I've tried using keymaster.js and mousetrap.js for binding the events with the same results.

Code in a JSFiddle

The Javascript code:

angular.module('app', ['directives', 'controllers']);

angular.module('directives', [])
.directive('keypress', [function () {
    return function (scope, element, attrs) {
        // console.log(scope, element, attrs);
        var attribute = scope.$eval(attrs.keypress || '{}');
        for (var k in attribute) {
            console.log('binding ' + k + ' as ' + attribute[k]);
            Mousetrap.bind(k, function() { return attribute[k](scope, element); });
        }
    };
}]);

angular.module('controllers', [])
.controller('TodoController', function($scope) {
    $scope.shortcuts = {
        'w': function () { console.log('w'); },
        's': function () { console.log('s'); },
        'a': function () { console.log('a'); },
        'd': function () { console.log('d'); }
    };
});

The HTML File:

<html>
  <head>
    <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.3/angular.js"></script>
    <script src="https://raw.github.com/ccampbell/mousetrap/master/mousetrap.min.js"></script>
    <script src="/javascript/app.js"></script>
  </head>
  <body>
    <div ng-app="app">
      <div ng-controller='TodoController' keypress='shortcuts'>Foo</div>
    </div>
  </body>
</html>

Why is 'd' always written to the console, regardless of whether I press 'w', 'a', 's', or 'd'?

解决方案

You've fallen for a common trap: variables in JavaScript are always function scoped. When you do this:

for (var k in attribute) {
    console.log('binding ' + k + ' as ' + attribute[k]);
    Mousetrap.bind(k, function() { return attribute[k](scope, element); });
}

With that bind(), you're creating four closures that all close over the variable k—but they all close over the same variable. You don't get a new one for each run of the loop. The console.log works perfectly because the value of k is used immediately. The closure doesn't evaluate k until it's actually run, and by then, its value has changed to whatever it was when the loop finished.

Depending on your target audience, the easiest way by far to fix this is to use let instead of var. let does block scoping (which works about how you'd expect), but is a fairly recent invention, and I'm not sure how well it's supported.

Otherwise, to get a new scope, you need a new function:

for (var k in attribute) {
    (function(k) {
        console.log('binding ' + k + ' as ' + attribute[k]);
        Mousetrap.bind(k, function() { return attribute[k](scope, element); });
    })(k);
}

This passes the outer k to the function's inner k, which will be a different variable every time. You could also split this out into a little factory function, but for something this tiny, I wouldn't bother.

这篇关于为什么都在这个指令设定的按键获取的最后一个覆盖?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆