【问题标题】:Dropdown binding issue with track bytrack by 的下拉绑定问题
【发布时间】:2017-09-07 15:32:00
【问题描述】:

我在将下拉值与关联数组绑定时遇到问题。

问题在于 track by 所以就像当我不将 track by 添加到我的下拉列表时我与下拉列表绑定并且当我添加 track 时 O 无法自动选择下拉值。

我想通过 ng-options 使用 track by,这样 angular js 就不会添加 $$hashKey 并利用与跟踪依据相关的性能优势。

我不明白为什么会发生这种行为。

注意:我只想为我的每个 $scope.items 而不是整个对象绑定 选择名称,例如 Pizza 或 burger .

更新:据我了解,我的 $scope.items 的当前数据结构做了很多尝试,它不适用于 ng-options,我想使用带有 track by 的 ng-options避免由 Angular js 生成哈希键。我还按照@MarcinMalinowski 的建议尝试了 ng-change,但我得到的 key 是未定义的。

那么,当我需要访问我的 $scope.items 中的任何项目时,我的 $scope.items 数据结构应该是什么?我可以在不执行循环的情况下访问它(就像我们从关联数组中访问项目一样),就像我现在可以使用正确的数据结构访问它并且只使用 ngoptions 和 track by。

var app = angular.module("myApp", []);
app.controller("MyController", function($scope) {
  $scope.items = [
  {
    "title": "1",
    "myChoice" :"",
      "choices": {
        "pizza": {
          "type": 1,
          "arg": "abc",
          "$$hashKey": "object:417"
        },
        "burger": {
          "type": 1,
          "arg": "pqr",
          "$$hashKey": "object:418"
        }
      }
   },
   {
    "title": "2",
     "myChoice" :"",
      "choices": {
        "pizza": {
          "type": 1,
          "arg": "abc",
          "$$hashKey": "object:417"
        },
        "burger": {
          "type": 1,
          "arg": "pqr",
          "$$hashKey": "object:418"
        }
      }
   }
  ];
   
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<ul ng-app="myApp" ng-controller="MyController">
   <div ng-repeat="data in items">
       <div>{{data.title}}
       </div>
     <select ng-model="data.myChoice" 
     ng-options="key as key for (key , value) in data.choices track by $index"><option value="">Select Connection</option></select>
   </div>
   
   </ul>

【问题讨论】:

  • 为什么不track by $index
  • @MaximShoustin 添加 trackby $index 总是自动选择最后一项。查看我的更新,我已经添加了 $index,现在我无法选择任何其他项目
  • 当提到自己(“我”)时,请始终使用大写字母。这将在改进您的帖子时为一些志愿编辑节省一些工作(大概所有 278 个都有相同的错误)。谢谢!

标签: javascript angularjs


【解决方案1】:

您的代码中的问题是:

1) track by $index is not supported by ngOptions,这将导致option 的值为undefined(在您的情况下,它将是$indexngRepeat);

2) track by 不适用于对象数据源(它应该与数组数据源一起使用),from the docs

trackexpr:在处理对象数组时使用。的结果 该表达式将用于识别数组中的对象。

当然,您可以使用ngRepeat 来生成option 元素,但就我个人而言,我更喜欢使用ngOptions 而不使用track by,因为它比ngRepeat 更有优势。

更新:以下代码说明了如何更改初始数据源并使用track by 预先选择一个选项以防模型为对象。但即使在第一个示例中,console.log() 也表明 $$hashKey 并未添加到 choices 对象中。

var app = angular.module("myApp", []);
app.controller("MyController", ['$scope', '$timeout', function($scope, $timeout) {
  $scope.items = [
  {
    "title": "1",
    "myChoice" :"burger",
      "choices": {
        "pizza": {
          "type": 1,
          "arg": "abc"
        },
        "burger": {
          "type": 1,
          "arg": "pqr"
        }
      }
   },
   {
    "title": "2",
     "myChoice" :"",
      "choices": {
        "pizza": {
          "type": 1,
          "arg": "abc"
        },
        "burger": {
          "type": 1,
          "arg": "pqr"
        }
      }
   }
  ];
  
  $scope.itemsTransformed = angular.copy($scope.items).map(function(item){
    delete item.myChoice;
    item.choices = Object.keys(item.choices).map(function(choice){
        item.choices[choice].name = choice;
        return item.choices[choice];
    });
    return item;
  });
  
  //select an option like an object, not a string
  $scope.itemsTransformed[1].myChoice = $scope.itemsTransformed[1].choices[0];
  
  $timeout(function() {
    //changes a prop in opts array - options are not-re-rendered in the DOM
    //the same option is still selected
    $scope.itemsTransformed[1].choices[0].arg = "xyz";
  }, 3000);
  
  $scope.selectionChanged =function(key, items){
    console.log(items); //as we can see $$hashKey wasn't added to choices props
  };
   
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<ul ng-app="myApp" ng-controller="MyController">
   <p>Without track by:</p>
   <div ng-repeat="data in items track by data.title">
     <div>{{data.title}} - {{data.myChoice}}</div>
       
     <select ng-model="data.myChoice" 
             ng-options="key as key for (key , value) in data.choices"
             ng-change="selectionChanged(key, items)">
       <option value="">Select Connection</option>
     </select>
     
   </div>
   <hr/>
    <p>Using track by name to pre-select an option:</p>
    <div ng-repeat="data in itemsTransformed track by data.title">
     <div>{{data.title}} - {{data.myChoice}}</div>
       
     <select ng-model="data.myChoice" 
             ng-options="choice as choice.name for choice in data.choices track by choice.name"
             ng-change="selectionChanged(key, itemsTransformed)">
       <option value="">Select Connection</option>
     </select>
     
   </div>
</ul>

更新 2: 一个简单的示例向我们展示了在使用 ngOptions 而不使用 track by 时不会将 $$hashKey 属性添加到对象中的事实:

var app = angular.module("myApp", []);
app.controller("MyController", ['$scope', '$timeout', function ($scope, $timeout) {
    $scope.items = {
        "pizza": {
          "type": 1,
          "arg": "abc"
        },
        "burger": {
          "type": 1,
          "arg": "pqr"
        }
      };

    $scope.selectionChanged = function (key, items) {
        console.log($scope.items);
    };

}]);
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="MyController">
    <hr/>
    <p>Example without track by:</p>

    <select ng-model="myChoice"
            ng-options="key as key for (key , value) in items"
            ng-change="selectionChanged(myChoice, items)">
        <option value="">Select Connection</option>
    </select> 
    <hr/>
    {{myChoice}}
</div>

更新 3: 最终结果如下(适用于 angularjs 版本 $scope.itemsTransformed):

angular.module("myApp", [])
.controller("MyController", ['$scope', function ($scope) {
    $scope.items = [
        {
            "title": "1",
            "myChoice": "burger",
            "choices": {
                "pizza": {
                    "type": 1,
                    "arg": "abc"
                },
                "burger": {
                    "type": 1,
                    "arg": "pqr"
                }
            }
        },
        {
            "title": "2",
            "myChoice": "",
            "choices": {
                "pizza": {
                    "type": 1,
                    "arg": "abc"
                },
                "burger": {
                    "type": 1,
                    "arg": "pqr"
                }
            }
        }
    ];
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="MyController">
    <div ng-repeat="data in items track by data.title">
        <div>{{data.title}} {{data.myChoice}}</div>

        <select ng-model="data.myChoice"
                ng-options="key as key for (key , value) in data.choices">
            <option value="">Select Connection</option>
        </select>

    </div>
</div>

【讨论】:

  • 赞成您为帮助我所做的努力,但没有跟踪的 ng-options 是否比 ng-repeat 有性能优势??
  • 不带跟踪的 ng-options 的问题会生成我在问题中所说的哈希值,我不希望这样。这就是为什么我想使用 track by 以便 angular 不会生成哈希值来跟踪更改
  • @Learning,尝试从ngOptions 中删除track by,但我没有看到$$hashKey 正在创建(将添加代码来说明。)。 track byngRepeat 在性能方面是有意义的,但我不确定它在 ngOptions 指令中的作用相同 - 从文档中它用于在重新创建选项时保留选择。跨度>
  • @Learning 添加了sn-p代码,请查收。正如文档中所述,ngOptions 指令比ngRepeat 具有性能优势(使用track by 来提高其性能并没有什么意义,我在the sources 中也看不到相关内容)。你从哪里得到track by 影响ngOptions 性能的信息?顺便说一句,在 sn-p 中您使用的是 angularjs v1.2,如果您一般关心性能,我建议您升级它:)
  • 在您的第一个示例中添加了 hashkey。我不明白为什么您在第二个示例的代码中使用了超时
【解决方案2】:

ngOptions 不会为每个项目创建像 ngRepeat 指令这样的新范围,因此您无需担心摆脱 $$hashKey

我会使用ng-repeat 迭代&lt;option&gt;(假设您不创建长列表):

<select ng-model="data.myChoice">    
    <option value="">Select Connection</option>
    <option ng-repeat="(key , value) in data.choices track by key" ng-value="key" title="{{key}}"
    >{{key}}</option>
</select>

Working Demo Fiddle


看看这个问题:github.com/angular/angular.js/issues/6564 - ng-options track by and select as are not compatible

我相信这个问题仍然存在,所以建议您改用ngRepeattrack by。对于小列表,没有性能损失

【讨论】:

  • 赞成您为帮助我所做的努力,但您能否向我解释一下,例如我的代码中存在什么问题以及为什么不使用 ng-options ??
  • @Learning 你的代码没有问题,但结构复杂。所以我用基本的ng-repeat替换了ngOptions指令
  • @Learning 看看这个问题:github.com/angular/angular.js/issues/6564 - ng-options track by and select as are not compatible
  • 非常感谢您为帮助所做的努力,请继续像这样提供帮助。感谢 :)
【解决方案3】:

ngOptions 属性可用于为使用数组或对象的元素动态生成元素列表

ngModel 通过引用而不是值来监视模型。在将 select 绑定到作为对象或集合的模型时,了解这一点很重要。

1.如果将模型设置为与集合中的对象相同的对象,ngOptions 将无法设置选择,因为对象不相同。因此,默认情况下,您应该始终引用集合中的项目进行预选,例如:$scope.selected = $scope.collection[3]

  1. ngOptions 将不通过引用跟踪项目的身份,而是通过表达式跟踪的结果。例如,如果您的收藏项目有一个 id 属性,您将按 item.id 进行跟踪。

例如:

 $scope.items = [
  {
    "title": "1",
    "myChoice" :"",
      "choices": {
        "pizza": {
          "type": 1,
          "arg": "abc"
        },
        "burger": {
          "type": 1,
          "arg": "pqr"
        }
      }
   },
   {
    "title": "2",
     "myChoice" :"",
      "choices": {
        "pizza": {
          "type": 1,
          "arg": "abc"
        },
        "burger": {
          "type": 1,
          "arg": "pqr"
        }
      }
   }
  ];

从以上第二点开始,追踪物品的身份,而不是通过引用。

在对象中添加key的keyName并通过keyName或通过arg跟踪,输入。

按参数或类型跟踪:

 <select ng-model="data.myChoice" 
                 ng-options="choice as choice.arg for choice in data.choices track by choice.arg">
           <option value="">Select Connection</option>
         </select>

或者在选择对象中添加keyName

 $scope.items = $scope.items.filter(function(item){
    delete item.myChoice;
    item.choices = Object.keys(item.choices).map(function(choice){
        item.choices[choice].keyName = choice;
        return item.choices[choice];
    });
    return item;
  });

HTML 代码:

<div ng-controller="MyCtrl">
   <ul>
   <div ng-repeat="data in items">
     <select ng-model="data.selected" 
             ng-options="choice as choice.keyName for choice in data.choices track by choice.keyName"
             ng-change="selection(data.selected)">
       <option value="">Select</option>
     </select>

   </div>
   </ul>
</div>

演示链接Example

【讨论】:

  • 非常感谢您为帮助所做的努力,请继续像这样提供帮助。感谢 :)
【解决方案4】:

您需要添加 ng-change 并在那里传递/使用您的 ng-model 值以获得您想要的任何属性。

【讨论】:

  • 赞成您为帮助我所做的努力,但我在这里有点困惑。如果我在那里设置我的 ng-model 的值,那么我的 ng-model 应该是什么。您能否添加您所说的任何示例代码??
  • ng-change="youFuncHere(key)"
  • 但是当我没有在我的下拉列表中添加 ng-model 时,我得到一个错误:无法找到指令 'ngOptions' 所需的控制器 'ngModel'!所以你是说我指定ng-model 但仍然在 youFuncHere 方法中设置我的模型??
  • 不确定这个答案是否与ngOptions 用法有关。
【解决方案5】:
<select class="form-control pickupaddress ng-pristine ng-valid ng-touched m-r-sm m-t-n-xs" ng-model="item.pickup_address" tabindex="0" aria-invalid="false" ng-options="add._id as add.nick_name for add in addPerFood[item.food._id] | unique:'nick_name'" ng-change="dropDownSelect(item.pickup_address,allCarts,item,$index)">

【讨论】:

  • 请多解释。
  • 非常感谢您为帮助所做的努力,请继续像这样提供帮助。感谢 :)
猜你喜欢
  • 2018-01-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-13
  • 1970-01-01
相关资源
最近更新 更多