如何在AngularJS中使用ng-repeat迭代JavaScript Map?

7
我试图在ng-repeat angular指令中使用JavaScript Maps。在搜索过程中,我发现可以通过以下方式实现:
<ul ng-controller="MyCtrl">
    <li ng-repeat='(key, value) in {"First Name":"John", "Last Name":"Smith"}'>
        <span>Key: {{key}}, value: {{value}}</span>
    </li>
</ul>

但是这仅适用于普通的JSON对象,当我以以下方式创建真正的Map时,它不起作用。
例如,我有这个控制器。
function MyCtrl($scope) {
    $scope.items = new map();
    $scope.items.set('adam',{'age':10,'fav':'doogie'});
    $scope.items.set('amalie',{'age':12});
}

and this html code

<ul>
    <li ng-repeat='(key, value) in items'>
        <span>Key: {{key}}, value: {{value}}</span>
    </li>
</ul>

1
请看这里 https://github.com/angular/angular.dart/issues/359 - Pengyy
3个回答

11

由于 ng-repeat 不支持 Map 迭代,您可以使用以下类似的自定义过滤器fromMap

angular.module('app', []).filter('fromMap', function() {
  return function(input) {
    var out = {};
    input.forEach((v, k) => out[k] = v);
    return out;
  };
}).controller('ctrl',function ($scope) {
    $scope.items = new Map();
    $scope.items.set('adam',{'age':10,'fav':'doogie'});
    $scope.items.set('amalie',{'age':12});
    
    $scope.logAdamAge = function() { console.log($scope.items.get("adam").age) };
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
  <div>with Map</div>
  <ul>
    <li ng-repeat="(key, value) in items | fromMap">
      <div>{{key}}</div>
      <div ng-repeat="(k, v) in value">{{k}}: <input ng-model="value[k]" /></div>
    </li>
  </ul>
  <pre>{{items | fromMap}}</pre>
  <button ng-click="logAdamAge()">log Adam age from Map</button>
</div>


很棒的实现。这是最好的方法,因为它可以在每个摘要周期中保持数据更新。 - Soldeplata Saketos

4
这段代码无法正常工作,因为JavaScript中的Map是一个可迭代对象(只能通过entries迭代器循环,每次调用next()),并且不被Angular的ng-repeat直接支持。
如果您有现成的代码,可以将可迭代对象(map)转换为普通数组。
$scope.itemsToArray = Array.from($scope.items.entries()); 

这将使您的可迭代对象成为一个数组,在此时,您可以按照标准方式循环遍历。
<ul>  
  <li ng-repeat='item in itemsToArray'>
     <span>Key: {{item[0]}}, value: {{item[1]}}</span>
  </li>
</ul>

在这里工作的plnkr 链接


1

尽管我喜欢 Jag's answer,但我建立了一个更广泛的代码片段,使用不同的实现来达到同样的目的,并且我删除了大部分ES6箭头函数和展开运算符,将过滤器实现为一行代码:.filter('fromMap',() => mapObject => [...mapObject])

设置观察者甚至取消对该观察者的订阅。

以下是如何在Map类对象上实现ng-repeat:

angular.module('ui.bootstrap.demo', ['ngAnimate', 'ngSanitize', 'ui.bootstrap']);
angular.module('ui.bootstrap.demo')
  .filter('fromMap', () => mapObject => [...mapObject])
  .controller('myCtrl', function($scope, $log) {
    $scope.myMap = new Map([
      ['key1', 'value1'],
      ['key2', 'value2'],
      ['key3', 'value3'],
      ['key4', 'value4'],
    ]);
    const unsuscribeWatcher1 = $scope.$watch(() => $scope.myMap.size, (newVal, oldVal) => {
      if (newVal !== oldVal) {
        //Splicing and pushing keeps the reference to the Array instead of reassigning.
        $scope.myLoopedMap.splice(0);
        $scope.myLoopedMap.push(...[...$scope.mySet].filter(k => $scope.myMap.has(k)).map(k => $scope.myMap.get(k)))
        $scope.myArrFromMap.splice(0);
        $scope.myArrFromMap.push(...[...$scope.myMap])
      }
    })
    const unsuscribeWatcher2 = $scope.$watch(() => $scope.mySet.size, (newVal, oldVal) => {
      if (newVal !== oldVal) {
        $scope.myLoopedMap.splice(0);
        $scope.myLoopedMap.push([...$scope.mySet].filter(k => $scope.myMap.has(k)).map(k => $scope.myMap.get(k)));
      }
    })
    $scope.mySet = new Set(['key1', 'key3']);
    $scope.mySet.add('not in the Map')
    $scope.mySet.add('key7')
    $scope.myKeys = ['key1', 'key3'];
    $scope.myArr = [
      ['key1', 'value1'],
      ['key2', 'value2'],
      ['key3', 'value3'],
      ['key4', 'value4'],
    ];
    $scope.myArrFromMap = [...$scope.myMap]
    $scope.myLoopedMap = [...$scope.mySet].filter(k => $scope.myMap.has(k)).map(k => $scope.myMap.get(k))
    $scope.myLoopedMap2 = [...$scope.myKeys].reduce((acc, k) => {
      return $scope.myMap.has(k) ? acc.concat($scope.myMap.get(k)) : acc;
    }, [])

    // added to the map later
    setTimeout(() => {
      $scope.$apply(() => {
        // this will be updated as we are watching the Map
        $scope.myMap.set('key7', 'value7')
      })
      setTimeout(() => {
        unsuscribeWatcher1(); // this breaks the watching of the Map
        $scope.$apply(() => {
          // this will not be updated as the watcher is not working anymore
          $scope.myMap.set('key8', 'value8')
        })
      }, 3000)
    }, 3000)


  });
<!doctype html>
<html ng-app="ui.bootstrap.demo">

<head>
  <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.6.1/angular.js"></script>
  <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.6.1/angular-animate.js"></script>
  <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.6.1/angular-sanitize.js"></script>
  <script src="//angular-ui.github.io/bootstrap/ui-bootstrap-tpls-2.5.0.js"></script>
  <script src="example.js"></script>
</head>

<body>

  <div ng-controller="myCtrl">
    <h3>Some test data data</h3>
    <div>Get 'key1'======> {{myMap.get('key1')}}</div>
    <div>Arr ==========> {{myArr}}</div>
    <div>filtered ======> {{myMap | fromMap}}</div>
    <div>myArrFromMap=> {{myArrFromMap}}</div>
    <div>myLoopedMap==> {{myLoopedMap}}</div>
    <div>myLoopedMap2=> {{myLoopedMap2}}</div>

    <h3>ng repeat in myLoopedMap</h3>
    <div ng-repeat="item in myLoopedMap">
      Item:`{{item}}`
    </div>
 
    
    <h3>ng repeat in myArrFromMap</h3>
    <div ng-repeat="(index, value) in myArrFromMap">
      value[0]:`{{value[0]}}`, value[1]:`{{value[1]}}`, index: `{{index}}`
    </div>
    
  </div>
</body>

</html>

还有相应的Plnkr


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接