【问题标题】:How to filter angular model (array) without destroying it如何过滤角度模型(数组)而不破坏它
【发布时间】:2016-06-10 23:43:46
【问题描述】:

我有一个视图模型。
该模型是对象数组:

var arr = { "12345qwery": { prop1: "value", prop2: "value" } } // contains 500 items

今天我用以下方式过滤它:

arr = $filter('filter')(arr, filterTerm); // contains 4 items

在这一行之后,我得到了很好的过滤数据,但如果我再次运行这个过滤器,我没有 500 个项目,而是 4 个。
因此,为了避免这种情况,我将原始数组存储在临时对象中,当用户更改过滤器时,我首先使用备份数据(它是原始 500 项)更新 arr 并进行过滤。
现在我遇到了麻烦,因为我有多个过滤器,我必须在每个过滤器之前恢复原始数据......无论如何它是一团糟:)
有没有更好的(角度)方法可以在 javascript 过滤中做到这一点?

更新

为了更好地解释我创建 plunker 的问题是什么:

https://plnkr.co/edit/99b02UtUfPeM3wl4IiX6?p=preview

如您所见,我使用对象加载标记并希望通过文本字段对其进行过滤。
但我不能,因为我总是遇到一些错误。
我在这里做错了吗?
为了避免这种情况并以某种方式实现过滤器,这就是为什么我决定在代码中执行它并在每个过滤器之后保留原始数组,但这是非常复杂的解决方案,我不想以更自然的角度方式来实现它。

赏金更新

我在js 代码中过滤对象,因为我找不到以标准角度方式过滤此指令上的标记的方法。
这就是为什么我在代码中进行过滤,在过滤之前总是复制它。
我需要帮助以标准角度方式过滤此指令上的标记对象。
Plunker 实现了这个指令,但我不知道如何过滤它。

【问题讨论】:

  • 问题不清楚,能否请您添加 jsfiddler 举例说明问题?
  • 你有什么理由从 JS 而不是 HTML 模板中过滤数组?
  • 我正在使用这个指令。而且它不能以标准的角度方式过滤,我不知道为什么:github.com/tombatossals/angular-leaflet-directive/issues/395
  • 您的 plunk 如何尝试使用过滤?我找不到 $filter 或 {{ |过滤 }} 语法。
  • 您不能在对象上使用过滤管道,它必须在数组上使用。在您的 Plunker 中,arr 是一个对象。使用过滤器的任何变体都会导致类似this 的错误。

标签: javascript angularjs angularjs-scope angularjs-filter angular-leaflet-directive


【解决方案1】:

当您在 控制器 中应用角度滤镜时,这是一个一次性的过程。看起来您的用例实际上更适合在 view 中应用过滤器,如下所示:

{{ arr | filter : filterTerm}}

这将使您的模型保持不变,但无论如何只会在视图中显示过滤后的项目。此fiddle 显示了 filterTerm 输入字段的用法。

【讨论】:

  • 但是我如何从代码中运行过滤呢?当我输入文本输入时,我的过滤器应该可以工作,所以我需要这个事件。
  • @1110 在 filterTerm 中使用 ng-model 和相同的对象
  • 添加了示例用法。
  • 我用完整的 plunker 示例更新了问题。如果我尝试使用filterTerm,我会在控制台中收到大量错误。
【解决方案2】:

您可以像我在a plunker forked from yours 中所做的那样。创建一个工厂,在其中保留标记对象,将它们返回给调用的控制器,然后根据 filterTerm 过滤它们(顺便说一句,这不在您的原始 plunker 的控制器范围内)。

app.factory('myMarkers', function() {

    var markers = {
        m1: {
            lat: 51.505,
            lng: -0.09,
            data: 'a'
        },
        m2: {
            lat: 51,
            lng: 0,
            data: 'ab'
        },
        m3: {
            lat: 51,
            lng: 0.1,
            data: 'abc'
        },
        m4: {
            lat: 51,
            lng: 0.14,
            data: 'abcd'
        }
    };

    function filterMarkersBy(term) {
        return _.filter(markers, function(marker) {
          return marker.data.indexOf(term) > -1;
        });
    }

    return {
        markers: markers,
        filterMarkersBy: filterMarkersBy
    }
});

然后在您的控制器中,您可以通过将所有标记放在$scope 上(使用angular.extend($scope, { markers: myMarkers.markers });)来初始化地图,然后观察您的$scope.filterTerm 的值以相应地过滤$scope.markers 对象。

...
angular.extend($scope, { markers: myMarkers.markers });

$scope.filterTerm;

$scope.$watch(function() { return $scope.filterTerm; }, function(newVal) {
    if (newVal) {
      console.log(newVal);
        $scope.markers = myMarkers.filterMarkersBy(newVal);
    }
});

现在它会即时过滤并在您减少过滤条件时添加标记。请注意,我在工厂中使用 lodash 的 _.filter() 方法进行过滤,但您可能已经将 lodash 作为依赖项。

【讨论】:

    【解决方案3】:

    在 angularjs 视图中过滤的一种方法

    <input type="text" ng-model="filterBy"><!-- this is filter option -->
    <div ng-repeat="row in rows| filter: {filterName : filterBy}">
    

    或者你也可以在控制器中尝试这样的东西

    $scope.filtered = $filter('filter')($scope.results, filterTerm)[0];
    

    【讨论】:

      【解决方案4】:

      实现此目的的一种方法是创建两个数据副本。保持原样不变,并将过滤后的数据副本分配给地图作为标记。当用户更改过滤器术语时,对原始数据应用过滤器并将结果分配给过滤后的数据变量。例如,

      $scope.orignalMarkers = {1,2,3,4}
      $scope.filteredMarkers = $scope.orignalMarkers // initial values for both are same
      
      watch (filterTerm){
         $scope.filteredMarkers = $filter on $scope.orignalMarkers
      }
      $scope.filteredMarkers// assign this variable to map.
      

      请原谅我的编码,它只是 sudo。

      【讨论】:

        【解决方案5】:

        问题是你试图过滤一个对象而不是一个数组。

        尝试构建自己的自定义文件管理器:

        app.filter('myObjectFilter', function() {
          return function(input, search) {
            var result = {};
            for (var key in input) {
              if (input.hasOwnProperty(key)) {
                if (input[key].data.toLowerCase().indexOf(search.toLowerCase()) > -1) {
                  result[key] = input[key];
                }
              }
            }
            return result;
          }
        });
        

        见:https://plnkr.co/edit/Gi4gWHne57owB44MTsAB?p=preview

        【讨论】:

          【解决方案6】:

          好的..所以你有一些事情要做。

          问题

          • 作用域:将作用域稍微向外移动。由于您需要使用filterTerm,它需要在您的控制器范围内,因此将范围移出一个级别。我把它移到了&lt;body&gt; 标签——见plnkr

          • 结构:尝试始终将 JS 文件包含在 &lt;body&gt; 标记的末尾,并确保顺序正确。即在angular-simple-logger.js之前包含angular.js

          • $scope的使用:你可以直接使用scope,不需要扩展它,只是让它更难阅读。

          • 模型结构:您的Markers 元素级别太深,我将Markers 变量设为标记对象数组。

          解决方案

          使用Angular的filter,还不错,但需要正确使用。 一般是这样的: {{ array_to_filter | filter : filter_term}}

          所以在这种情况下,你可以像这样使用它:

          <leaflet defaults="defaults" markers="markers | filter: filterTerm " height="480px" width="640px"></leaflet>

          现在应该可以了,尝试搜索LondonPark

          如果您在 JS 代码中使用过滤器,则更容易将其设置为函数,其中变量在其作用域结束时死亡。否则你将永远覆盖你的变量。

          TL;RD

          这是一个包含工作版本的plnkr

          【讨论】:

            【解决方案7】:

            简短的回答是

            当您在这两种情况下进行过滤时,Angular 不会破坏数组

            在 HTML 中

            {{ arr | filter : filterTerm}}
            

            或在 JS 中:

            newArray = $filter('filter')(arr, filterTerm);
            

            这将是一个新数组。

            【讨论】:

              【解决方案8】:

              您需要使用 angular.copy 复制标记数组

              angular.extend($scope,{
                filteredMarkers:angular.copy($scope.markers)
               });  
              

              编写您的客户过滤器以过滤对象而不是数组

              app.filter('markerFilter',function(){
                 return function(input,filterBy){
                   var markers = [];
                   if(input && filterBy && input[filterBy]){
                     markers.push(input[filterBy]);
                     return markers;
                   }
                   return input;
                 }
               })
              

              查看 plunker,在文本框中输入 m1、m2、m3 并用 Tab 标记。 https://plnkr.co/edit/GI4gn5

              【讨论】:

                【解决方案9】:

                您正在用新过滤的数组覆盖您的数组。

                arr = $filter('filter')(arr, filterTerm);
                

                是一样的

                var x = 5;
                x = x+4;
                

                【讨论】:

                  【解决方案10】:

                  我不知道有一种简单的内置“角度”方式来解决这个问题。这是我将如何处理通过多个过滤器过滤列表的方法。我会保留一组过滤器,然后只要这些过滤器中的任何一个发生更改,就会根据所有过滤器重新生成结果列表。

                  看看这个 sn-p 看看它是否符合您的要求。

                  angular.module('app', [])
                    .controller('MyController', function($filter) {
                      var vm = this;
                      vm.markers = [{
                        lat: 51.505,
                        lng: -0.09,
                        data: 'a'
                      }, {
                        lat: 51,
                        lng: 0,
                        data: 'ab'
                      }, {
                        lat: 51,
                        lng: 0.1,
                        data: 'abc'
                      }, {
                        lat: 51,
                        lng: 0.14,
                        data: 'abcd'
                      }];
                  
                      //list of all terms to filter the data by
                      vm.filterTerms = [];
                  
                      //start out with an unfiltered list of markers
                      vm.filteredMarkers = vm.markers;
                  
                      vm.addFilterTerm = function(term) {
                        vm.filterTerms.push(term);
                        vm.filterMarkers();
                      };
                  
                      /**
                       * Takes the source array, and applies every filter to it and saves it to the filtered array
                       */
                      vm.filterMarkers = function() {
                        //start with the original data
                        var result = vm.markers;
                        for (var i in vm.filterTerms) {
                          //get the current term
                          var filterTerm = vm.filterTerms[i];
                          //filter the results by the current filter term
                          result = $filter('filter')(result, filterTerm);
                        }
                        vm.filteredMarkers = result;
                      }
                    });
                  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.min.js"></script>
                  <div ng-app="app">
                    <div ng-controller="MyController as vm">
                      <input type="text" ng-model="vm.currentFilterTerm" />
                      <button ng-click="vm.addFilterTerm(vm.currentFilterTerm);">Add to filter</button>
                      <h1>Filters</h1>
                      <ul>
                        <li ng-repeat="term in vm.filterTerms">{{term}}</li>
                      </ul>
                      <h1>Markers</h1>
                      <ul>
                        <li ng-repeat="marker in vm.filteredMarkers">{{marker}}</li>
                      </ul>
                    </div>
                  </div>

                  【讨论】:

                    【解决方案11】:

                    使用$watch('searchTerm') 过滤更改并将markers 对象转换为数组,然后再应用$filter

                      $scope.filteredMarkers=$scope.markers;          
                      $scope.$watch("filterTerm",function(filterTerm){
                            $scope.arr=Object.keys($scope.markers).map(function(key) {
                              return $scope.markers[key];
                            });
                            $scope.filteredMarkers=filterTerm ? $filter('filter')($scope.arr, {'data':filterTerm}) : $scope.markers;
                        });
                    

                    最后在指令上使用filteredMarkers:

                    <leaflet defaults="defaults" markers="filteredMarkers" height="480px" width="640px"></leaflet>
                    

                    查看更新的插件:https://plnkr.co/edit/P5bNzHmZ2CRjImbMztyr?p=preview

                    【讨论】:

                      猜你喜欢
                      • 2012-09-04
                      • 1970-01-01
                      • 2021-05-11
                      • 2018-01-04
                      • 1970-01-01
                      • 2018-10-04
                      • 2020-06-26
                      • 2015-01-15
                      • 2017-12-20
                      相关资源
                      最近更新 更多