Angular ng-repeat and array filtering - angularjs

So I have the following array being repeated to list:
<ion-checkbox ng-repeat="item in items | startFrom: currentPage*pageSize | limitTo: pageSize | filter: filterAll as filtered" ng-model="item.selected" ng-show="items.length" item="item" ng-class-even="&#039fade even&#039" ng-class-odd="&#039fade odd&#039">
Filtered with:
listModule.filter('startFrom', function () {
return function (input, start) {
if (input) {
start = +start; //parse to int
return input.slice(start);
}
return [];
}
});
$scope.filters = {
RowNr: "",
LocationNr: "",
ItemType: "",
ItemOwner: ""
};
$scope.filterAll = function () {
var result = $filter('filter')($scope.items, $scope.filterFn);
console.log(result.length);
return result;
}
$scope.filterFn = function (item) {
var result = true;
for (var filter in $scope.filters) {
var obj = $scope.filters[filter];
if (result) {
if (angular.isUndefined(obj) || obj == null || obj == "" || filter == "ItemType" || filter == "ItemOwner") {
result = true;
}
else if (filter == "LocationNr" || filter == "RowNr") {
result = item[filter] === parseInt(obj);
} else {
result = item[filter] == obj;
};
}
}
return result;
};
I can see the right result length being logged inside $scope.filterAll when I type in stuff in the filter inputs but the array displayed to the user wont update with filtered content?

Related

Can I filter an ng-repeat by more than one item at a time?

I have this ng-repeat:
ng-repeat="row in phs.phrasesView =
(phs.phrases | keywordRange:phs.englishRange )"
Here's the code for the filter:
app.filter('keywordRange', function () {
return function (value, english) {
var out = [];
if (!english) {
return value;
}
var ucEnglish = english[0].toUpperCase() + english.substr(1);
value.map((row: any) => {
if (typeof row.english !== 'undefined' &&
typeof row.english === 'string' &&
row.english != null &&
(row.english.includes(english) || row.english.includes(ucEnglish))
) {
out.push(row);
}
});
return out;
};
});
What I would like to do is to extend this so that rows that contain:
phs.englishRange or phs.romajiRange
Are passed through the filter.
Note that only one of englishRange or romajiRange will be populated. There won't be a case where they both have a value.
Can someone tell me is this possible and give me some advice on how I can do this?

Angular filter multiple columns with an array of filters

I am trying to be able to filter multiple columns on multiple values. So far I can filter multiple columns on 1 single value:
myApp.controller('MyCtrl', ['$scope', '$http', function($scope, $http) {
$scope.empList = [];
$http.get('getAllOnline.php')
.success(function(data) {
$scope.empList = data;
});
$scope.column1List = [];
$http.get('getAllSomething.php', {
params: {
wuk: "column1"
}
})
.success(function(data) {
$scope.column1 = data;
});
$scope.column2List = [];
$http.get('getAllSomething.php', {
params: {
wuk: "column2"
}
})
.success(function(data) {
$scope.column2 = data;
});
$scope.column3List = [];
$http.get('getAllSomething.php', {
params: {
wuk: "column3"
}
})
.success(function(data) {
$scope.column3 = data;
});
$scope.setColumn1Value = function(val) {
if ($scope.zelectedColumn1 == val) {
$scope.zelectedColumn1 = undefined;
} else {
$scope.zelectedColumn1 = val;
}
}
$scope.setColumn2Value = function(val) {
if ($scope.zelectedColumn2 == val) {
$scope.zelectedColumn2 = undefined;
} else {
$scope.zelectedColumn2 = val;
}
}
$scope.setColumn3Value = function(val) {
if ($scope.zelectedColumn3 == val) {
$scope.zelectedColumn3 = undefined;
} else {
$scope.zelectedColumn3 = val;
}
}
Then i use this to set my single value filters:
<ul id="Column1" class="collapse">
<li ng-repeat="emp in empList | unique:'Column1'">
<a ng-click="setColumn1Value(emp.Column1);" ng-class="{selected: emp.Column1 === zelectedColumn1}">
<div ng-repeat="someone in Column1List | filter:{Column1_id:emp.Column1}">
{{someone.value}}
</div>
</a>
</li>
</ul>
This works perfect! But now I want to be able to filter on multiple values in 1 column. So I changed my setter functions to:
$scope.zelectedColumn1=[];
$scope.setColumn1Value = function(val) {
var found = jQuery.inArray(val, $scope.zelectedColumn1);
if (found >= 0) {
// Element was found, remove it.
$scope.zelectedColumn1.splice(found, 1);
} else {
// Element was not found, add it.
$scope.zelectedColumn1.push(val);
}
console.log($scope.zelectedColumn1);
}
So now I add or remove indexes to an array instead of storing a single value. This also works, but how do I filter my columns on the contents of an array instead of on a single value as I do now:
<div class='row'>
<div class='col-lg-2 col-md-3 col-sm-6' ng-repeat="emp in empList | filter:{column1:zelectedColumn1,column2:zelectedColumn2,column3:zelectedColumn3} as res">
{{emp.column1}}
{{emp.column2}} <br>
{{emp.column3}} <br>
</div>
</div>
I have been struggling with this al day and hope someone can help me out here!

Truncate ng-bind-html in AngularJS

I'm using truncate.js https://github.com/sparkalow/angular-truncate and it works great for codes like this:
{{announcement.content | characters:25}}
However, i can't seem to setup for the following and i can't get it to work:
<p ng-bind-html="parseTrustedHtml(announcement.content | characters : 25)"></p>
I encountered a similar issue, the issue is that angular-truncate is meant for strings, not HTML. Here is my solution:
Markup:
<div class="container" ng-controller="parentCtrl">
<div ng-bind-html="text | limitHtml : maxNumberOfChar:'...' | trustAsHtml"></div>
</div>
Code:
.filter('trustAsHtml', ['$sce', function($sce) {
return $sce.trustAsHtml;
}])
.filter('limitHtml', function() {
return function(text, limit, ellipsis) {
var _getClosedTagsString = function(_tagArray) {
var _returnArray = [],
_getTagType = function(_string) {
return _string.replace(/<[\/]?([^>]*)>/,"$1");
};
angular.forEach(_tagArray,function(_tag,_i) {
if(/<\//.test(_tag)) {
if(_i === 0) {
_returnArray.push(_tag);
} else if(_getTagType(_tag) !== _getTagType(_tagArray[_i - 1])) {
_returnArray.push(_tag);
}
}
});
return _returnArray.join('');
},
_countNonHtmlCharToLimit = function(_text,_limit) {
var _isMarkup = false,
_isSpecialChar = false,
_break = false,
_underLimit = false,
_totalText = 0,
_totalChar = 0,
_element,
_return = {
textCounter : 0,
offsetCounter : 0,
setEllipsis : false,
overElementArray : []
};
angular.forEach(_text,function(_c) {
_underLimit = _return.textCounter < _limit;
if(_c === '<' && !_isMarkup && !_isSpecialChar) {
(!_underLimit) && (_element = '<');
_isMarkup = true;
} else if(_c === '&' && !_isMarkup && !_isSpecialChar) {
_isSpecialChar = true;
} else if(_isMarkup) {
//tracking html elements that are beyond the text limit
(!_underLimit) && (_element = _element + _c);
if(_c === '>') {
//push element in array if it is complete, and we are
//beyond text limit, to close any html that is unclosed
(!_underLimit) && (_return.overElementArray.push(_element));
_break = true;
_isMarkup = false;
}
} else if(_c === ';' && _isSpecialChar) {
_isSpecialChar = false;
//count as one character
_return.textCounter++;
_break = true;
}
if(_underLimit) {
if(!_isMarkup && !_isSpecialChar && !_break) {
//counting number of characters in non html string
_return.textCounter++;
}
_return.offsetCounter++;
} else {
_return.setEllipsis = true
}
_break = false;
});
//returns offset within html of number of non html characters found
return _return;
},
_charToLimitOutput = _countNonHtmlCharToLimit(text.toString(),limit);
return text.toString().substr(0, _charToLimitOutput.offsetCounter) +
ellipsis + _getClosedTagsString(_charToLimitOutput.overElementArray);
}
})
.controller('parentCtrl', function($scope,$timeout) {
$scope.text = "<span><h1>Example </h1><p>Special Text</p><div>other stuff</div></span>";
$scope.maxNumberOfChar = 10;
});
No need to use truncate.js
You can solve this using custom directives and filters.
try this one: https://stackoverflow.com/a/45076560/6816707
Need more information, however can you check if parseTrustedHtml method/function is available at $scope or $rootscope.

Angular JS radio lists and dropdowns. Issue with dropdowns changing value of radio list value when triggered

I think I have a logic issue with my Angular that I need some help on.
We have a service that connects to a DB that gets saved values so our Angular form can get the values and use them. That is all set up.
My issue is, in our form we have 1 radio button list using an ng-repeat to generate the list and 3 dropdown lists. Our dropdown lists are triggering and update function to update the values of themselves. We are showing and hiding the dropdown menus based on the radio button lists selection (I will post the code). This should be noted we are using Umbraco CMS that this control is being used for, but this is not the actual issue.
The issue is, we console our response the value of the radio button list outputs fine to the selection we chose, BUT when I select one of the dropdown list items, the value of the dropdown list returns to the previously selected value. Any help figuring this out would be greatly appreciated.
Code is below:
my.controller.js
angular.module("umbraco")
.controller("Our.GalaxyEventSelectorController", function ($scope, $routeParams, notificationsService, GalaxyEventSelectorResource) {
$scope.emptyList = [{}];
$scope.ETypeRadio = { 0: "NA", 1: "RepeatingTimedEvent", 2: "RepeatSingleDayEvent", 3: "SingleTimedEvent" };
GalaxyEventSelectorResource.getEventById($routeParams.id, $scope.model.alias).then(function (response) {
// console.log("REsponse: " + response);
console.log("intial load on DOM load")
var resp = (response.data.indexOf("{") > -1 ? angular.fromJson(JSON.parse(response.data)) : "");
$scope.previousSelectedTypeOfEvent = (resp == "" ? "" : resp.TypeOfEvent);
$scope.previousSelectedEventTypeId = (resp == "" ? 0 : resp.EventTypeId);
$scope.previousSelectedEventName = (resp == "" ? "" : resp.EventName);
$scope.previousSelectedEventId = (resp == "" ? 0 : resp.EventId);
$scope.previousSelectedEventDate = (resp == "" ? "" : resp.EventDate);
//This loads the selection the initial time.
$scope.selectedTypeOfEvent = $scope.previousSelectedTypeOfEvent; //THIS Gets the previous radio button selection
//init EventTypeId dropdown
var initIdx = 0;
$scope.getEventTypeIds(true, initIdx);
}).then(function() {
//init Name dropdown
var initIdx = -1;
$scope.getEventNames(true, initIdx, $scope.previousSelectedEventTypeId);
}).then(function () {
//init EventIds and Dates dropdowns
var initIdx = -1;
$scope.getEventIdsAndDates(true, initIdx, $scope.previousSelectedEventName);
$scope.getEventDatesOnly(true, initIdx, $scope.previousSelectedEventName);
}).then(function () {
// init model values
$scope.typeOfEventRadioSelected($scope.previousSelectedTypeOfEvent);
$scope.updateModelValue(
$scope.previousSelectedTypeOfEvent,
$scope.previousSelectedEventTypeId,
$scope.previousSelectedEventName,
$scope.previousSelectedEventId,
$scope.previousSelectedEventDate);
});
$scope.updateModelValue = function (typeOfEvent, eventTypeId, eventName, eventId, eventDate) {
$scope.model.value = {
TypeOfEvent: typeOfEvent,
EventTypeId: eventTypeId,
EventName: eventName,
EventId: eventId,
EventDate: eventDate
}
console.log("Scope load and scope change");
console.log($scope.model.value);
};
//not used...attempting to make the visual nice onscreen but causes unpredictable loss of data. could be useful
$scope.addSpacesToCamelCase = function(txt) {
return txt.replace(/([a-z])([A-Z])/g, "$1 $2");
}
$scope.typeOfEventRadioSelected = function(selectedTypeOfEvent) {
//triggered when radio button selected.
var typeOfEvent = selectedTypeOfEvent;
var eventTypeId = $scope.selectedGalaxyEventTypeId != null
? $scope.selectedGalaxyEventTypeId.EventTypeId
: "";
var eventName = "";
var eventId = "";
var eventDate = "";
var initIdx = -1;
$("#GalaxyEventNameDdl").show();
$("#GalaxyEventIdDdl").show();
$("#GalaxyEventDatesDdl").show();
switch (selectedTypeOfEvent) {
case "NA":
$scope.getEventNames(false, initIdx, eventTypeId);
$scope.GalaxyEventIdsAndDates = $scope.initial;
$scope.GalaxyEventDates = $scope.initial;
$("#GalaxyEventNameDdl").hide();
$("#GalaxyEventIdDdl").hide();
$("#GalaxyEventDatesDdl").hide();
break;
case "RepeatingTimedEvent":
//$scope.getEventNames(false, initIdx, eventTypeId);
eventName = $scope.selectedGalaxyEventName != null
? $scope.selectedGalaxyEventName.EventName
: "";
$scope.GalaxyEventIdsAndDates = $scope.initial;
$scope.GalaxyEventDates = $scope.initial;
$("#GalaxyEventIdDdl").hide();
$("#GalaxyEventDatesDdl").hide();
break;
case "RepeatSingleDayEvent":
//$scope.getEventNames(false, initIdx, eventTypeId);
eventName = $scope.selectedGalaxyEventName != null
? $scope.selectedGalaxyEventName.EventName
: "";
$scope.getEventDatesOnly(false, initIdx, eventName);
eventDate = $scope.selectedGalaxyEventDates != null
? $scope.selectedGalaxyEventDates.EventDate
: "";
$scope.GalaxyEventIdsAndDates = $scope.initial;
$("#GalaxyEventIdDdl").hide();
break;
case "SingleTimedEvent":
//$scope.getEventNames(false, initIdx, eventTypeId);
eventName = $scope.selectedGalaxyEventName != null
? $scope.selectedGalaxyEventName.EventName
: "";
$scope.getEventIdsAndDates(false, initIdx, eventName);
eventId = $scope.selectedGalaxyEventId != null
? $scope.selectedGalaxyEventId.EventId
: "";
eventDate = $scope.selectedGalaxyEventDates != null
? $scope.selectedGalaxyEventDates.EventDate
: "";
$scope.GalaxyEventDates = $scope.initial;
$("#GalaxyEventDatesDdl").hide();
break;
}
$scope.updateModelValue(
typeOfEvent,
eventTypeId,
eventName,
eventId,
eventDate
);
};
$scope.eventTypeIDSelected = function (selectedEventTypeId) {
console.log("Event Type ID selected");
//triggered when EventTypeId dropdown is changed. update the datatype value & provide names in name dropdown
var initIdx = -1;
$scope.getEventNames(false, initIdx, selectedEventTypeId.EventTypeId);
$scope.GalaxyEventIdsAndDates = $scope.initial;// NOT WORKING TO WIPE THE LIST...WHY?
$scope.GalaxyEventDates = $scope.initial; // NOT WORKING TO WIPE THE LIST...WHY?
$scope.updateModelValue(
$scope.selectedTypeOfEvent,
selectedEventTypeId.EventTypeId,
"",
"",
"");
};
$scope.eventNameSelected = function (selectedName) {
//triggered when eventName dropdown is changed. update the datatype value & provide dates and ids in dropdowns
var initIdx = -1;
$scope.getEventIdsAndDates(false, initIdx, selectedName.EventName);
$scope.getEventDatesOnly(false, initIdx, selectedName.EventName);
$scope.updateModelValue(
$scope.selectedTypeOfEvent,
$scope.selectedGalaxyEventTypeId.EventTypeId,
selectedName.EventName,
"",
"");
};
$scope.eventIdSelected = function (selectedEventId) {
//triggered when eventId dropdown is changed. update the datatype value & init date dropdown
var initIdx = -1;
$scope.getEventDatesOnly(false, initIdx, $scope.selectedGalaxyEventName.EventName);
$scope.updateModelValue(
$scope.selectedTypeOfEvent,
$scope.selectedGalaxyEventTypeId.EventTypeId,
$scope.selectedGalaxyEventName.EventName,
selectedEventId.EventId,
selectedEventId.EventDate);
};
$scope.eventDatesSelected = function (selectedEventDate) {
//triggered when eventDate dropdown is changed. update the datatype value & init Id dropdown
var initIdx = -1;
$scope.getEventIdsAndDates(false, initIdx, $scope.selectedGalaxyEventName.EventName);
$scope.updateModelValue(
$scope.selectedTypeOfEvent,
$scope.selectedGalaxyEventTypeId.EventTypeId,
$scope.selectedGalaxyEventName.EventName,
"",
selectedEventDate.EventDate);
};
$scope.getEventTypeIds = function (initVal, idx) {
GalaxyEventSelectorResource.getEventIds().then(function (eventTypeIds) {
$scope.GalaxyEventTypes = eventTypeIds.data;
console.log("successfully retrieved galaxyeventids");
//console.log("Event Type IDs:", eventTypeIds.data[0]);
if (initVal) {
$scope.GalaxyEventTypes.some(function (x, i) {
if (x.EventTypeId == $scope.previousSelectedEventTypeId) {
idx = i;
return true;
}
});
}
$scope.selectedGalaxyEventTypeId = $scope.GalaxyEventTypes[idx];
},
function (data) {
console.log("failed to retrieve galaxyeventids");
});
};
$scope.getEventNames = function (initVal, idx, eventTypeId) {
GalaxyEventSelectorResource.getEventNamesByEventId(eventTypeId).then(function (eventNames) {
$scope.GalaxyEventNames = eventNames.data;
console.log("successfully retrieved galaxyeventnames");
if (initVal) {
$scope.GalaxyEventNames.some(function (x, i) {
if (x.EventName == $scope.previousSelectedEventName) {
idx = i;
return true;
}
});
}
$scope.selectedGalaxyEventName = $scope.GalaxyEventNames[idx];
},
function (data) {
console.log("failed to retrieve galaxyeventnames");
});
};
$scope.getEventIdsAndDates = function(initVal, idx, eventName) {
GalaxyEventSelectorResource.getEventIdsAndDatesByEventName(eventName).then(function (eventIds) {
$scope.GalaxyEventIdsAndDates = eventIds.data;
console.log("successfully retrieved galaxyIdsanddates");
if (initVal) {
$scope.GalaxyEventIdsAndDates.some(function(x, i) {
if (x.EventId == $scope.previousSelectedEventId) {
idx = i;
return true;
}
});
}
$scope.selectedGalaxyEventId = $scope.GalaxyEventIdsAndDates[idx];
},
function(data) {
console.log("failed to retrieve galaxyIdsanddates");
});
};
$scope.getEventDatesOnly = function (initVal, idx, eventName) {
GalaxyEventSelectorResource.getEventDatesByEventName(eventName).then(function (eventDates) {
$scope.GalaxyEventDates = eventDates.data;
console.log("successfully retrieved galaxydates");
if (initVal) {
$scope.GalaxyEventDates.some(function (x, i) {
if (x.EventDate == $scope.previousSelectedEventDate) {
idx = i;
return true;
}
});
}
$scope.selectedGalaxyEventDates = $scope.GalaxyEventDates[idx];
},
function (data) {
console.log("failed to retrieve galaxydates");
});
};
EventSelector.html
<div ng-controller="Our.GalaxyEventSelectorController">
<h5>Select Type of Event</h5>
<!--<div>-->
<div ng-repeat="n in ETypeRadio">
<!-- need to use ng-click as an ng-change on an ng-repeat element does not work -->
<!-- <input type="radio" ng-model="selectedTypeOfEvent" name="tOfE" ng-click="typeOfEventRadioSelected(selectedTypeOfEvent)" ng-value="{{n}}" value="{{n}}" />{{n}}-->
<input type="radio" ng-model="selectedTypeOfEvent" name="tOfE" ng-click="typeOfEventRadioSelected(selectedTypeOfEvent)" ng-value="{{n}}" value="{{n}}" />{{n}}
</div>
<!--</div>-->
<h5>Galaxy Event Type</h5>
<select ng-model="selectedGalaxyEventTypeId" ng-change="eventTypeIDSelected(selectedGalaxyEventTypeId)" ng-options="eventType.EventTypeId + ' - ' + eventType.EventTypeIdDescription for eventType in GalaxyEventTypes track by eventType.EventTypeId"></select>
<br/>
<div id="GalaxyEventNameDdl">
<h5>Galaxy Event Name</h5>
<select ng-model="selectedGalaxyEventName" ng-change="eventNameSelected(selectedGalaxyEventName)" ng-options="name.EventName for name in GalaxyEventNames">
<option value=""> --- Select Event Name ---</option>
</select>
<br />
</div>
<div id="GalaxyEventIdDdl">
<h5>Galaxy Event Id</h5>
<select data-ng-model="selectedGalaxyEventId" ng-change="eventIdSelected(selectedGalaxyEventId)" ng-options="id.EventId + ' - ' + id.EventDate for id in GalaxyEventIdsAndDates track by id.EventId">
<option value=""></option>
</select>
<br />
</div>
<div id="GalaxyEventDatesDdl">
<h5>Galaxy Event Date</h5>
<select data-ng-model="selectedGalaxyEventDates" ng-change="eventDatesSelected(selectedGalaxyEventDates)" ng-options="date.EventDate for date in GalaxyEventDates">
<option value=""></option>
</select>
</div>
We have a .resource.js but this is just performing gets to load our original data from our service.
angular.module("umbraco.resources").factory("GalaxyEventSelectorResource", function ($http) {
var galaxyEventService = {};
galaxyEventService.getEventIds = function () {
return $http.get("/umbraco/backoffice/api/GalaxyEventSelector/GetAllEventTypeIDs");
};
galaxyEventService.getEventById = function (id, propertyType) {
return $http.get("/umbraco/backoffice/api/GalaxyEventSelector/GetEventById?id=" + id + "&propertyType=" + propertyType);
};
galaxyEventService.getEventNamesByEventId = function(eventId) {
return $http.get("/umbraco/backoffice/api/GalaxyEventSelector/GetEventNamesByEventId?eventId=" + eventId);
};
galaxyEventService.getEventIdsAndDatesByEventName = function(eventName) {
return $http.get("/umbraco/backoffice/api/GalaxyEventSelector/GetEventIdsAndDatesByEventName?eventName=" + eventName);
};
galaxyEventService.getEventDatesByEventName = function(eventName) {
return $http.get("/umbraco/backoffice/api/GalaxyEventSelector/GetEventDatesByEventName?eventName=" + eventName);
};

How to make ng-repeat filter out duplicate results

I'm running a simple ng-repeat over a JSON file and want to get category names. There are about 100 objects, each belonging to a category - but there are only about 6 categories.
My current code is this:
<select ng-model="orderProp" >
<option ng-repeat="place in places" value="{{place.category}}">{{place.category}}</option>
</select>
The output is 100 different options, mostly duplicates. How do I use Angular to check whether a {{place.category}} already exists, and not create an option if it's already there?
edit: In my javascript, $scope.places = JSON data, just to clarify
You could use the unique filter from AngularUI (source code available here: AngularUI unique filter) and use it directly in the ng-options (or 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>
Or you can write your own filter using lodash.
app.filter('unique', function() {
return function (arr, field) {
return _.uniq(arr, function(a) { return a[field]; });
};
});
You can use 'unique'(aliases: uniq) filter in angular.filter module
usage: colection | uniq: 'property'
you can also filter by nested properties: colection | uniq: 'property.nested_property'
What you can do, is something like that..
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: We filter by customer id, i.e remove duplicate customers
<th>Customer list: </th>
<tr ng-repeat="order in orders | unique: 'customer.id'" >
<td> {{ order.customer.name }} , {{ order.customer.id }} </td>
</tr>
result
Customer list:
foo 10
bar 20
baz 30
this code works for me.
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;
};
})
and then
var colors=$filter('unique')(items,"color");
If you want to list categories, I think you should explicitly state your
intention in the view.
<select ng-model="orderProp" >
<option ng-repeat="category in categories"
value="{{category}}">
{{category}}
</option>
</select>
in the controller:
$scope.categories = $scope.places.reduce(function(sum, place) {
if (sum.indexOf( place.category ) < 0) sum.push( place.category );
return sum;
}, []);
Here's a straightforward and generic example.
The filter:
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;
};
})
The markup:
<div ng-repeat = "item in items | unique:'name'">
{{ item.name }}
</div>
<script src="your/filters.js"></script>
I decided to extend #thethakuri's answer to allow any depth for the unique member. Here's the code. This is for those who don't want to include the entire AngularUI module just for this functionality. If you're already using AngularUI, ignore this answer:
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;
};
});
Example
<div ng-repeat="item in items | unique : 'subitem.subitem.subitem.value'"></div>
I had an array of strings, not objects and i used this approach:
ng-repeat="name in names | unique"
with this filter:
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();
}
};
}
UPDATE
I was recomending the use of Set but sorry this doesn't work for ng-repeat, nor Map since ng-repeat only works with array. So ignore this answer. anyways if you need to filter out duplicates one way is as other has said using angular filters, here is the link for it to the getting started section.
Old answer
Yo can use the ECMAScript 2015 (ES6) standard Set Data structure, instead of an Array Data Structure this way you filter repeated values when adding to the Set. (Remember sets don't allow repeated values). Really easy to use:
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
It seems everybody is throwing their own version of the unique filter into the ring, so I'll do the same. Critique is very welcome.
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]];
});
};
});
Here's a template-only way to do it (it's not maintaining the order, though). Plus, the result will be ordered as well, which is useful in most cases:
<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>
None of the above filters fixed my issue so I had to copy the filter from official github doc. And then use it as explained in the above answers
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;
};
});
If you want to get unique data based on the nested key:
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;
};
});
Call it like this :
<div ng-repeat="notify in notifications | unique: 'firstlevel':'secondlevel'">
Add this filter:
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;
};
});
Update your markup:
<select ng-model="orderProp" >
<option ng-repeat="place in places | unique" value="{{place.category}}">{{place.category}}</option>
</select>
This might be overkill, but it works for me.
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;
}
The distinct function depends on the contains function defined above. It can be called as array.distinct(prop); where prop is the property you want to be distinct.
So you could just say $scope.places.distinct("category");
Create your own array.
<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);
}
});

Resources