How to filter angular model (array) without destroying it

I have a model for my presentation.
This model is an array of objects:

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

And today I filter it as follows:

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

And after this line, I get good filtered data, but if I run this filter again, I don't have 500 elements, but 4.
Therefore, to avoid this, I store the original array in a temporary object, and when the user changes the filter, I first update the arr with the backup data (these are the original 500 elements) and perform filtering.
Now I have problems, because I have several filters, and I have to restore the original data before each filter ... anyway it's a mess :)
Is there a better way (angular) to do this when filtering javascript?

UPDATE

To better explain what the problem is, I created a plunker:

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

As you can see, I am loading markers with objects and want to filter them using a text box.
But I can’t, because I always get some errors.
Am I doing something wrong here?
And to avoid this and implement the filter somehow, so I decided to do it in the code and save the original array after each filter, but this is a very complicated solution, and I will not make it in a more natural way angular.

BOUNTY UPDATE

I am filtering an object in js code because I cannot find a way to filter markers in this directive in the standard angular way.
This is why I filter the code and always make a copy before the movie.
I need help filtering marker objects in this directive in the standard angular way.
Plunker implements this directive, but I do not know how to filter it.

+5
source share
11 answers

Good. So, you have a few things that are happening.

Questions

  • Scoping : slightly change the scope. Since you need to use filterTerm , it must be within your controller, so move the area to the level. I moved it to the <body> - see plnkr .

  • Structure . Try to always include JS files at the end of the <body> and make sure you have the correct order. Ie include angular.js to angular-simple-logger.js

  • Using $ scope : you can directly use a scope, you do not need to expand it, which just makes it difficult to read.

  • The structure of the model . Markers elements are too deep, I make the Markers variable an array of marker objects.

Decision

Use Angular filter , this is pretty good, but you need to use it correctly. This is usually the case: {{ array_to_filter | filter : filter_term}} {{ array_to_filter | filter : filter_term}}

So in this case you can use it like this:

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

Now you need to work, just try to find London or Park .

If you use a filter in JS code, it's easier to just make it a function where the variable dies at the end of its scope. Otherwise, you will always rewrite the variable.

TL RD

Here is plnkr containing the working version.

+2
source

short answer

Angular does not destroy the array when filtering in both cases:

either in HTML

 {{ arr | filter : filterTerm}} 

or in JS:

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

it will be a new array.

+4
source

You need to copy an array of markers using angular.copy

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

Write a client filter to filter an object instead of an array

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

Check the plunker, write m1, m2, m3 in the text box and exit it. https://plnkr.co/edit/GI4gn5

+3
source

When you apply the angular filter in the controller , this is a one-time process. It seems your use case is really suitable for applying a filter in a view , eg:

 {{ arr | filter : filterTerm}} 

This will leave your model unchanged, but for now it will only show the filtered elements in the view. This fiddle shows usage with an input field for filterTerm.

+2
source

The problem is that you are trying to filter an object instead of an array.

Try creating your own custom file:

 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; } }); 

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

+2
source

You are rewriting your array with a new filtered array.

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

Same as

 var x = 5; x = x+4; 
+2
source

You could do something like what I did in the plunker that is forged from yours . Create a factory where you save marker objects, return them to the controller as called, and then filter them according to filterTerm (which is not included in the scope of your controller in your original plunter, by the way).

 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 } }); 

And then in your controller, you can initialize the map by placing all the markers on $scope (using angular.extend($scope, { markers: myMarkers.markers }); ), and then look at the value of your $scope.filterTerm to filter the $scope.markers object $scope.markers respectively.

 ... 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); } }); 

Now it is filtered on the fly and adds backward markers when you reduce the filter term. Note that I use the lodash _.filter() method to filter in the factory, but you probably already got lodash as a dependency.

+1
source

One way to filter in angular view

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

Or you can also try something like this in the controller

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

One way to achieve this is to create two copies of the data. keep the original the same as before, and assign a filtered copy of the data to display as a marker. when the user changes the filtering period, apply a filter to the source data and assign the result to the filtered data variable. eg,

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

Excuse my coding, its just sudo.

+1
source

I do not know that there is a simple built-in "angular" way. This is how I would handle filtering a list with multiple filters. I would save an array of filters, and then at any time any of these filters will change, regenerate the list of results based on ALL filters.

Take a look at this snippet and see if it does what you are looking for.

 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> 
0
source

Use $watch('searchTerm') to filter changes and convert the markers object to an array before applying $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; }); 

Finally, use the filterMarkers directive with the directive:

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

See the updated plunker: https://plnkr.co/edit/P5bNzHmZ2CRjImbMztyr?p=preview

0
source

Source: https://habr.com/ru/post/1244022/


All Articles