For循环仅将数组的最后一个值分配给eventlisteners

时间:2017-05-17 08:36:49

标签: javascript angularjs arrays json for-loop

我有以下AngularJS控制器;

app.controller('playerController', ['$scope', '$http', 'playerService', function ($scope, $http, playerService) {
     $scope.soundeffects = playerService.getSoundeffects();

     function soundeffectCreation() {
        var maxrows = 3;
        var n = 0;

        var buttonTable = "<table class='table text-center'><tr>";

        for (var item = 0; item < $scope.soundeffects.length; item++) {
            var soundeffect = $scope.soundeffects[item];
            var soundeffectName = soundeffect.name;
            buttonTable += "<td><label>" + soundeffectName + "</label><button type='button' class='btn btn-default btn-block' id='" + soundeffectName + "'><i class='fa fa-play'></i></button></td>"
             if(n == maxrows) {
                buttonTable += "</tr><tr>"
                n = 0;
             }
             else{
                n++;
            }
        }
        buttonTable += "</tr></table>";
        document.getElementById('soundeffects').innerHTML = buttonTable;

         for (var item = 0; item < $scope.soundeffects.length; item++) {
             var soundeffect = $scope.soundeffects[item];
             var soundeffectName = soundeffect.name;
             var button = document.getElementById(soundeffectName);

             console.log(soundeffect);
             console.log(soundeffectName);
             console.log(button);
             button.addEventListener('click', function () {
                console.log(soundeffectName);
            })
        }
    }

    soundeffectCreation();
}

它在以下HTML上发挥了作用:

<div id="soundeffects"></div>

使用到目前为止只包含3个项目的JSON数组:

{
   "soundeffects": [
      {
        "name": "Wilhelm Scream",
        "video": "r6JK-gRELI0"
      },
      {
        "name": "Fireball",
        "video": "AHRf27GPhQc"
      },
      {
        "name": "Mario Jump",
        "video": "37-paiEz0mQ"
      }
   ]
}

在playerService中成功检索此JSON。 表中的按钮创建得很好,它们得到各自的标签并正确分配了id,问题出现在第二个for循环中。 (之所以有第二个for循环是因为我无法在实际创建表之前添加eventlisteners。)

addEventListener之外的前三个console.log返回正确的值:Console

三个不同的对象及其各自的名称和视频。 三个不同的名字(Wilhelm Scream,Fireball&amp; Mario Jump)。 三个不同的按钮,其id等于上述名称。

但是,当我单击创建的按钮时,每个按钮都会返回“Mario Jump”,即数组的最后一个值,而不是相应元素的名称。尽管按钮的id是正确的和一切。我出错的任何建议?

ALTERNATIVE

作为替代方案,我尝试将ng-click='$scope.playSoundeffect(" + soundeffect + ")'放在原始按钮创建器中,但这只会在[Object object]中产生声音效果。任何有关如何使该方法有效的建议都将受到欢迎。

为海外编辑

在获得更多AngularJS经验之后,我重新制作了整个部分。我删除了整个soundeffectCreation()函数,并将其替换为以下html:

<div ng-repeat="soundeffect in soundeffects track by $index" class="col-xs-6 col-md-4 col-lg-3">
    <div class="text-nowrap"><label>{{soundeffect.name}}</label></div>
    <div>
        <button class="btn btn-default" ng-click="playSoundeffect(soundeffect)">
            <i class="fa fa-play"></i>
        </button>
    </div>
</div>

3 个答案:

答案 0 :(得分:1)

使用let代替var for循环:

for (let item = 0; item < $scope.soundeffects.length; item++) {
     let soundeffect = $scope.soundeffects[item];
     let soundeffectName = soundeffect.name;
     let button = document.getElementById(soundeffectName);

     console.log(soundeffect);
     console.log(soundeffectName);
     console.log(button);
     button.addEventListener('click', function () {
        console.log(soundeffectName);
    })
}

问题是每次循环迭代缺少明确的闭包 - 请查看此SO answer以获取for循环中闭包的详细说明。

不支持let

的浏览器解决方案

您还可以使用自调用匿名函数为每次迭代创建新范围:

for (var item = 0; item < $scope.soundeffects.length; item++) {
     (function(i) {
         var soundeffect = $scope.soundeffects[i];
         var soundeffectName = soundeffect.name;
         var button = document.getElementById(soundeffectName);

         console.log(soundeffect);
         console.log(soundeffectName);
         console.log(button);
         button.addEventListener('click', function () {
            console.log(soundeffectName);
        })
     })(item);
}

答案 1 :(得分:1)

您需要像这样

在关闭中捕获soundeffectName
button.addEventListener('click', function (soundeffectName) {
    console.log(soundeffectName);
}.bind(null, soundeffectName))

答案 2 :(得分:0)

你正在处理关闭

 var soundeffectName = soundeffect.name;

 button.addEventListener('click', function () {
     console.log(soundeffectName);
 })

soundEffect是外部作用域的变量,所有侦听器都将保存对外部作用域的引用,并且它们都将指向内存中与上一次迭代的值相同的位置。要在侦听器中保留值的副本,请尝试将代码包装在IIFE中

(function(name){
  button.addEventListener('click', function () {
   console.log(name);
  })
})(soundeffectName)