Uruchamiam prosty ng-repeat
plik JSON i chcę uzyskać nazwy kategorii. Istnieje około 100 obiektów, każdy należący do kategorii - ale jest tylko około 6 kategorii.
Mój obecny kod to:
<select ng-model="orderProp" >
<option ng-repeat="place in places" value="{{place.category}}">{{place.category}}</option>
</select>
Rezultatem jest 100 różnych opcji, głównie duplikatów. Jak użyć Angulara, aby sprawdzić, czy {{place.category}}
już istnieje, i nie tworzyć opcji, jeśli już istnieje?
edytuj: W moim javascript $scope.places = JSON data
, tylko dla wyjaśnienia
db.collection.distinct("places")
, co było o wiele, znacznie lepsze niż robienie tego w Angular! Niestety to nie zadziała dla wszystkich.Odpowiedzi:
Możesz użyć unikalnego filtra z AngularUI (kod źródłowy dostępny tutaj: unikalny filtr AngularUI ) i użyć go bezpośrednio w ng-options (lub ng-repeat).
<select ng-model="orderProp" ng-options="place.category for place in places | unique:'category'"> <option value="0">Default</option> // unique options from the categories </select>
źródło
angular.module('yourModule', ['ui', 'ui.filters']);
. Byłem zaskoczony, dopóki nie zajrzałem do pliku js AngularUI.unique
Filtr można obecnie znaleźć w ramach angularjs UI Utilsangular.module('ui.filters')
nazwę aplikacji.Możesz też napisać własny filtr za pomocą lodash.
app.filter('unique', function() { return function (arr, field) { return _.uniq(arr, function(a) { return a[field]; }); }; });
źródło
_.uniqBy(arr, field);
powinno działać dla zagnieżdżonych właściwościMożesz użyć filtru „unique” (aliasy: uniq) w module angular.filter
użycie:
colection | uniq: 'property'
możesz również filtrować według zagnieżdżonych właściwości:
colection | uniq: 'property.nested_property'
To, co możesz zrobić, jest takie ...
function MainController ($scope) { $scope.orders = [ { id:1, customer: { name: 'foo', id: 10 } }, { id:2, customer: { name: 'bar', id: 20 } }, { id:3, customer: { name: 'foo', id: 10 } }, { id:4, customer: { name: 'bar', id: 20 } }, { id:5, customer: { name: 'baz', id: 30 } }, ]; }
HTML: filtrujemy według identyfikatora klienta, tj. Usuwamy zduplikowanych klientów
<th>Customer list: </th> <tr ng-repeat="order in orders | unique: 'customer.id'" > <td> {{ order.customer.name }} , {{ order.customer.id }} </td> </tr>
wynik
Lista klientów:
foo 10
bar 20
baz 30
źródło
ten kod działa dla mnie.
app.filter('unique', function() { return function (arr, field) { var o = {}, i, l = arr.length, r = []; for(i=0; i<l;i+=1) { o[arr[i][field]] = arr[i]; } for(i in o) { r.push(o[i]); } return r; }; })
i wtedy
var colors=$filter('unique')(items,"color");
źródło
Jeśli chcesz wymienić kategorie, myślę, że powinieneś wyraźnie określić swój zamiar w widoku.
<select ng-model="orderProp" > <option ng-repeat="category in categories" value="{{category}}"> {{category}} </option> </select>
w kontrolerze:
$scope.categories = $scope.places.reduce(function(sum, place) { if (sum.indexOf( place.category ) < 0) sum.push( place.category ); return sum; }, []);
źródło
group by
wyrażenia wselect
dyrektywie.Oto prosty i ogólny przykład.
Filtr:
sampleApp.filter('unique', function() { // Take in the collection and which field // should be unique // We assume an array of objects here // NOTE: We are skipping any object which // contains a duplicated value for that // particular key. Make sure this is what // you want! return function (arr, targetField) { var values = [], i, unique, l = arr.length, results = [], obj; // Iterate over all objects in the array // and collect all unique values for( i = 0; i < arr.length; i++ ) { obj = arr[i]; // check for uniqueness unique = true; for( v = 0; v < values.length; v++ ){ if( obj[targetField] == values[v] ){ unique = false; } } // If this is indeed unique, add its // value to our values and push // it onto the returned array if( unique ){ values.push( obj[targetField] ); results.push( obj ); } } return results; }; })
Znaczniki:
<div ng-repeat = "item in items | unique:'name'"> {{ item.name }} </div> <script src="your/filters.js"></script>
źródło
Cannot read property 'length' of undefined
w liniil = arr.length
Postanowiłem rozszerzyć odpowiedź @ thethakuri, aby pozwolić na jakąkolwiek głębię dla unikalnego członka. Oto kod. To jest dla tych, którzy nie chcą dołączać całego modułu AngularUI tylko dla tej funkcjonalności. Jeśli już używasz AngularUI, zignoruj tę odpowiedź:
app.filter('unique', function() { return function(collection, primaryKey) { //no need for secondary key var output = [], keys = []; var splitKeys = primaryKey.split('.'); //split by period angular.forEach(collection, function(item) { var key = {}; angular.copy(item, key); for(var i=0; i<splitKeys.length; i++){ key = key[splitKeys[i]]; //the beauty of loosely typed js :) } if(keys.indexOf(key) === -1) { keys.push(key); output.push(item); } }); return output; }; });
Przykład
<div ng-repeat="item in items | unique : 'subitem.subitem.subitem.value'"></div>
źródło
Miałem tablicę ciągów znaków, a nie obiektów i zastosowałem następujące podejście:
ng-repeat="name in names | unique"
z tym filtrem:
angular.module('app').filter('unique', unique); function unique(){ return function(arry){ Array.prototype.getUnique = function(){ var u = {}, a = []; for(var i = 0, l = this.length; i < l; ++i){ if(u.hasOwnProperty(this[i])) { continue; } a.push(this[i]); u[this[i]] = 1; } return a; }; if(arry === undefined || arry.length === 0){ return ''; } else { return arry.getUnique(); } }; }
źródło
AKTUALIZACJA
Polecałem użycie Set, ale przepraszam, że to nie działa dla ng-repeat ani Map, ponieważ ng-repeat działa tylko z array. Więc zignoruj tę odpowiedź. w każdym razie, jeśli musisz odfiltrować duplikaty w jeden sposób, tak jak powiedział inny, używając
angular filters
, tutaj jest link do sekcji z wprowadzeniem .Stara odpowiedź
Możesz użyć standardowej struktury danych zestawu ECMAScript 2015 (ES6) zamiast struktury danych tablicowych, w ten sposób filtrujesz powtarzające się wartości podczas dodawania do zestawu. (Pamiętaj, że zestawy nie pozwalają na powtarzanie wartości). Naprawdę łatwy w użyciu:
var mySet = new Set(); mySet.add(1); mySet.add(5); mySet.add("some text"); var o = {a: 1, b: 2}; mySet.add(o); mySet.has(1); // true mySet.has(3); // false, 3 has not been added to the set mySet.has(5); // true mySet.has(Math.sqrt(25)); // true mySet.has("Some Text".toLowerCase()); // true mySet.has(o); // true mySet.size; // 4 mySet.delete(5); // removes 5 from the set mySet.has(5); // false, 5 has been removed mySet.size; // 3, we just removed one value
źródło
Wygląda na to, że każdy wrzuca
unique
do pierścienia własną wersję filtra, więc zrobię to samo. Krytyka jest bardzo mile widziana.angular.module('myFilters', []) .filter('unique', function () { return function (items, attr) { var seen = {}; return items.filter(function (item) { return (angular.isUndefined(attr) || !item.hasOwnProperty(attr)) ? true : seen[item[attr]] = !seen[item[attr]]; }); }; });
źródło
Oto sposób, w jaki można to zrobić tylko za pomocą szablonu (nie jest to jednak utrzymanie kolejności). Ponadto wynik zostanie również uporządkowany, co jest przydatne w większości przypadków:
<select ng-model="orderProp" > <option ng-repeat="place in places | orderBy:'category' as sortedPlaces" data-ng-if="sortedPlaces[$index-1].category != place.category" value="{{place.category}}"> {{place.category}} </option> </select>
źródło
Żaden z powyższych filtrów nie rozwiązał mojego problemu, więc musiałem skopiować filtr z oficjalnego dokumentu github. A następnie użyj go, jak wyjaśniono w powyższych odpowiedziach
angular.module('yourAppNameHere').filter('unique', function () {
return function (items, filterOn) {
if (filterOn === false) { return items; } if ((filterOn || angular.isUndefined(filterOn)) && angular.isArray(items)) { var hashCheck = {}, newItems = []; var extractValueToCompare = function (item) { if (angular.isObject(item) && angular.isString(filterOn)) { return item[filterOn]; } else { return item; } }; angular.forEach(items, function (item) { var valueToCheck, isDuplicate = false; for (var i = 0; i < newItems.length; i++) { if (angular.equals(extractValueToCompare(newItems[i]), extractValueToCompare(item))) { isDuplicate = true; break; } } if (!isDuplicate) { newItems.push(item); } }); items = newItems; } return items; }; });
źródło
Jeśli chcesz uzyskać unikalne dane na podstawie zagnieżdżonego klucza:
app.filter('unique', function() { return function(collection, primaryKey, secondaryKey) { //optional secondary key var output = [], keys = []; angular.forEach(collection, function(item) { var key; secondaryKey === undefined ? key = item[primaryKey] : key = item[primaryKey][secondaryKey]; if(keys.indexOf(key) === -1) { keys.push(key); output.push(item); } }); return output; }; });
Nazwij to tak:
<div ng-repeat="notify in notifications | unique: 'firstlevel':'secondlevel'">
źródło
Dodaj ten filtr:
app.filter('unique', function () { return function ( collection, keyname) { var output = [], keys = [] found = []; if (!keyname) { angular.forEach(collection, function (row) { var is_found = false; angular.forEach(found, function (foundRow) { if (foundRow == row) { is_found = true; } }); if (is_found) { return; } found.push(row); output.push(row); }); } else { angular.forEach(collection, function (row) { var item = row[keyname]; if (item === null || item === undefined) return; if (keys.indexOf(item) === -1) { keys.push(item); output.push(row); } }); } return output; }; });
Zaktualizuj swoje znaczniki:
<select ng-model="orderProp" > <option ng-repeat="place in places | unique" value="{{place.category}}">{{place.category}}</option> </select>
źródło
To może być przesada, ale dla mnie działa.
Array.prototype.contains = function (item, prop) { var arr = this.valueOf(); if (prop == undefined || prop == null) { for (var i = 0; i < arr.length; i++) { if (arr[i] == item) { return true; } } } else { for (var i = 0; i < arr.length; i++) { if (arr[i][prop] == item) return true; } } return false; } Array.prototype.distinct = function (prop) { var arr = this.valueOf(); var ret = []; for (var i = 0; i < arr.length; i++) { if (!ret.contains(arr[i][prop], prop)) { ret.push(arr[i]); } } arr = []; arr = ret; return arr; }
Odrębna funkcja zależy od funkcji zawiera zdefiniowanej powyżej. Można to nazwać tak, że
array.distinct(prop);
gdzie prop jest właściwością, którą chcesz wyróżnić.Więc możesz po prostu powiedzieć
$scope.places.distinct("category");
źródło
Utwórz własną tablicę.
<select name="cmpPro" ng-model="test3.Product" ng-options="q for q in productArray track by q"> <option value="" >Plans</option> </select> productArray =[]; angular.forEach($scope.leadDetail, function(value,key){ var index = $scope.productArray.indexOf(value.Product); if(index === -1) { $scope.productArray.push(value.Product); } });
źródło