跟踪下拉绑定问题

时间:2017-09-07 15:32:00

标签: javascript angularjs

我在使用关联数组绑定我的下拉列值时出现问题。

问题在于跟踪,所以当我没有将跟踪添加到我的下拉列表中时,我的下拉列表中有我的绑定,当我添加跟踪时,我无法自动选择下拉值。

我想使用带有ng-options的track by,以便角度js不会添加 $$ hashKey并利用与track by相关的性能优势。

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

注意:我只想为每个 $ scope.items绑定选项名称,例如披萨或汉堡,而不是整个对象

更新:据我所知,并且尝试使用我的$ scope.items的当前数据结构时,它不能使用ng-options,我想使用ng-options和track by来避免Angular js生成哈希密钥。我也按照@MarcinMalinowski的建议尝试了ng-change,但我得到了未定义的密钥。

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

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>

5 个答案:

答案 0 :(得分:9)

您的代码中的问题是:

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

2)ngRepeat不能很好地处理对象数据源(它应该与数组数据源一起使用),from the docs

  

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

当然,您可以使用ngRepeat来生成track by个元素,但就个人而言,我更倾向于使用ngOptions而不使用option,因为它具有track by的优势track by 3}}

更新:以下代码说明了如何更改初始数据源并使用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 }; }]);对象。

&#13;
&#13;
<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>
&#13;
$$hashKey
&#13;
&#13;
&#13;

更新2 :在没有ngOptions的情况下使用track by时,向我们显示事实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); }; }]);属性未添加到对象的简单示例:

&#13;
&#13;
<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>
&#13;
$scope.itemsTransformed
&#13;
&#13;
&#13;

更新3:下面的最终结果(适用于angularjs版本&lt; 1.4,对于1.4+我建议在第一个代码段中将数据结构更改为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" } } } ]; }]);):< / p>

&#13;
&#13;
<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>
&#13;
p {
  margin: 30px;
}
&#13;
&#13;
&#13;

答案 1 :(得分:6)

ngOptions每个项目都没有像ngRepeat指令那样创建新范围,因此您不需要注意摆脱$$hashKey

我会使用ng-repeat来迭代<option>(假设您没有创建长列表):

<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跟踪并选择不兼容

我认为此问题仍然存在,因此建议您使用ngRepeat代替track by。对于小清单,没有性能损失

答案 2 :(得分:4)

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

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

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

  1. ngOptions将不是通过引用跟踪项目的标识,而是通过表达式跟踪结果。例如,如果您的集合项具有id属性,则可以按item.id跟踪。
  2. 例如:

     $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"
            }
          }
       }
      ];
    

    从上述第2点开始,不是通过参考来追踪项目的身份。

    在对象中添加key的keyName,按keyName跟踪或按arg跟踪,键入。

    按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

答案 3 :(得分:3)

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

答案 4 :(得分:2)

2017-09-13 12:50:45.264  WARN --- [           main] o.s.mock.web.MockServletContext          : Couldn't determine real path of resource class path resource [src/main/webapp/WEB-INF/sitemesh.xml]

java.io.FileNotFoundException: class path resource [src/main/webapp/WEB-INF/sitemesh.xml] cannot be resolved to URL because it does not exist
        at org.springframework.core.io.ClassPathResource.getURL(ClassPathResource.java:187)
        at org.springframework.core.io.AbstractFileResolvingResource.getFile(AbstractFileResolvingResource.java:49)
        at org.springframework.mock.web.MockServletContext.getRealPath(MockServletContext.java:458)
        at org.grails.web.sitemesh.Grails5535Factory.<init>(Grails5535Factory.java:78)
        at org.grails.web.servlet.view.SitemeshLayoutViewResolver.loadSitemeshConfig(SitemeshLayoutViewResolver.java:105)
        at org.grails.web.servlet.view.SitemeshLayoutViewResolver.init(SitemeshLayoutViewResolver.java:67)
        at org.grails.web.servlet.view.SitemeshLayoutViewResolver.onApplicationEvent(SitemeshLayoutViewResolver.java:146)
        at org.grails.web.servlet.view.SitemeshLayoutViewResolver.onApplicationEvent(SitemeshLayoutViewResolver.java:42)
        at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:167)
        at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139)
        at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:393)
        at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:347)
        at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:883)
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:546)
        at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:693)
        at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:360)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:303)
        at grails.boot.GrailsApp.run(GrailsApp.groovy:83)
        at grails.ui.command.GrailsApplicationContextCommandRunner.run(GrailsApplicationContextCommandRunner.groovy:55)
        at grails.ui.command.GrailsApplicationContextCommandRunner.main(GrailsApplicationContextCommandRunner.groovy:102)