如何过滤角度模型(数组)而不破坏它

时间:2016-02-27 19:00:53

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

我有一个模型供我观看。
该模型是对象数组:

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

今天我按照以下方式过滤它:

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

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

更新

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

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

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

BOUNTY UPDATE

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

11 个答案:

答案 0 :(得分:4)

简短的回答是

在两种情况下进行过滤时,

Angular 不会销毁数组

以HTML格式

{{ arr | filter : filterTerm}}

或在JS中:

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

它将是新阵列。

答案 1 :(得分:3)

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

问题

  • 确定范围:将您的范围移出一点。由于您需要使用filterTerm,因此它需要在您的控制器范围内,因此请将范围移出一个级别。我将其移至<body>标记 - 请参阅plnkr

  • 结构:尝试始终在<body>标记的末尾包含您的JS文件,并确保您拥有正确的顺序。即在angular.js

  • 之前加入angular-simple-logger.js
  • 使用$ 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

答案 2 :(得分:3)

您需要使用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;
   }
 })

检查羽毛,在文本框中输入m1,m2,m3并标签。 https://plnkr.co/edit/GI4gn5

答案 3 :(得分:2)

控制器中应用角度滤镜时,这是一次性过程。您的用例似乎更适合在视图中应用过滤器,如下所示:

{{ arr | filter : filterTerm}}

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

答案 4 :(得分:2)

问题在于您尝试过滤对象而不是数组。

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

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

答案 5 :(得分:2)

您使用新过滤的数组覆盖数组。

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

相同
var x = 5;
x = x+4;

答案 6 :(得分:1)

你可以做我在a plunker forked from yours做过的事。创建一个工厂,在那里保留标记对象,将它们按照调用的方式返回到控制器,然后根据filterTerm过滤它们(这不在原始plunker的控制器范围内,顺便说一下)。 / p>

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的值以过滤{ {1}}相应的对象。

$scope.markers

现在它会动态过滤并在减少过滤器术语时添加标记。请注意,我正在使用lodash的... 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作为依赖项了。

答案 7 :(得分:1)

在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];

答案 8 :(得分:1)

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

$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。

答案 9 :(得分:0)

我不知道有一个简单的内置&#39;角度&#39;接近这个的方法。以下是我将如何处理多个过滤器过滤列表的方法。我会保留一系列过滤器,然后随时更改任何过滤器,根据所有过滤器重新生成结果列表。

看看这个片段,看看它是否符合您的要求。

&#13;
&#13;
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;
    }
  });
&#13;
<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>
&#13;
&#13;
&#13;

答案 10 :(得分:0)

使用$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 on指令:

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

请参阅更新的plunker:https://plnkr.co/edit/P5bNzHmZ2CRjImbMztyr?p=preview