Order by with key value object in Angularjs - angularjs

I have a select populated by a key/value object in Angular.
$scope.event_types = {
"CASE_STATE_CHANGE": "Case state change",
"NEW_ISSUE": "New Issue",
"CASE_REVERT": "Case Revert".......}
etc
However when I do an order by, it doesn't work.
<select
class="form-control"
name="chase2"
id="chase2"
ng-model="eventTypeFilter"
ng-change="updateEventFilterInput(eventTypeFilter)"
ng-options="key as value for (key , value) in event_types | orderBy:'value'">
Is this possible with this kind of array?

You cannot sort an Object. On most systems it will be sorted alphabetically. If you want to control the order that the elements will be presented you should use an array instead that guarantees ordering.
You could do something like:
var events_array = Object.keys(event_types)
.map(function (key) { return {key: key, value: event_types[key]} })
.sort(function(a,b) { return a.key < b.key; } );
and then use events_array on ng-options like:
<select ng-model="eventTypeFilter"
ng-options="event.key as event.value for event in events_array"></select>
Here is a jsfiddle (need to open console to see the output)
PS: you should consider sorting the values on the controller and then assign to the view as it is much more efficient than ordering on the view

Related

What is the criteria for md-select to check duplicate options in md-options

I have been using Angular Material for a while in my project. While using md-select, I am stuck to a problem wherein I am getting Duplicate md-option values error.
I am aware that md-options takes unique values and I am assigning an array to md-options. This is however, an array of objects. So I would like to know what is the criteria that is used to differentiate objects. The API do not say much about it.
My use case demands to change md-options of an md-select, based on selection from another md-select. So I am watching the selection of first md-select and firing a watch on its change and updating md-options of second md-select.
Below is the approach I am using to assign array to md-options:
$scope.$watch('search.selectedTrades', function(newTrades, oldTrades) {
if ((newTrades.length === 0)) {
$rootScope.search.selectedTrades = oldTrades;
return;
}
if ($rootScope.search.selectedTrades && $rootScope.search.selectedTrades.length > 0) {
if (!$rootScope.identity.isClusterManager) {
$rootScope.search.selectedTrades = newTrades;
SearchFilterData.setSelectedTrades(newTrades);
$rootScope.search.selectedClusters = [];
$scope.clusters = [];
$scope.subareas = [];
var clusterKeys = [];
$rootScope.search.selectedTrades.forEach(function(t) {
t.lstClusters.forEach(function(c) {
if (clusterKeys.indexOf(c.ClusterKey) == -1) {
clusterKeys.push(c.ClusterKey);
$scope.clusters.push(c);
}
})
})
}
} else {
$scope.clusters = [];
$scope.subareas = [];
$rootScope.search.selectedClusters = [];
$rootScope.search.selectedSubAreas = [];
SearchFilterData.setSelectedTrades($rootScope.search.selectedTrades);
}
});
In above code, clusterKey is a unique entity for each object. So I am using it to push unique values into array.
This however happens on few random scenarios, after I have selected and de-selected various options. Please advise what I am doing wrong and what is the criteria for marking two objects duplicate
You did not provide your markup, so I cannot be sure, but in my case the problem was caused by omitting the double curleys on the 'value' attribute in the md-option tag.
This is bad: Notice the missing curly braces
<md-option ng-repeat="item in vm.list" value="item.id">{{item.text}}</md-option>
This is not:
<md-option ng-repeat="item in vm.itemlist" value="{{item.id}}">{{item.text}}</md-option>
I believe the reason that this fails is that each item will be placed into the option list will be given a value of 'item.id' (literally). It will fail on the second iteration of the repeat.
Using the curly braces causes the value in 'item.id' to be used.
Hope this helps.
Try using ng-value instead of just value attribute.
<md-option ng-repeat="item in vm.list" ng-value="item.id">{{item.text}}</md-option>

AngularJS ng-options to get selected option from an array that matches another scope var

I have an array and a var
$scope.fcFeedTypes = [{"name":"Plain Text","value":"Plain Text"},{"name":"MQ Message","value":"MQ Message"}];
}
$scope.feedEditor.ft = "MQ Message"; // this is dynamically obtained from some other source
I want a dropdown select with default selection based on the value of $scope.feedEditor.ft
HTML:
<select ng-model="fcFeedTypes"
ng-options="ft.name for ft in fcFeedTypes"
ng-init="ft=feedEditor.ft">
I am new to AngularJS and need some help...
If you just want the string "MQ Message" (or the value of whatever a user selects) as your ng-model value, then it's quite simple:
<select ng-model="someModelName"
ng-options="ft.value as ft.name for ft in fcFeedTypes"
ng-init="someModelName=feedEditor.ft">
If you want the full object as the ng-model value, then you've got to find the right one in JS. It must be referentially/strictly equal to the one in the options.
<select ng-model="someModelName"
ng-options="ft.name for ft in fcFeedTypes">
-
$scope.fcFeedTypes = [{"name":"Plain Text","value":"Plain Text"},{"name":"MQ Message","value":"MQ Message"}];
$scope.feedEditor.ft = "MQ Message"; // this is dynamically obtained from some other source
angular.forEach($scope.fcFeedTypes, function(feedType){
if(feedType.value === $scope.feedEditor.ft){
$scope.someModelName = feedType;
}
});

how to prevent duplicate in array push in angularjs

my code is like this :
var arr = [];
arr.push(item1,item2);
so arr will contain like:
["name","thing1"]
But i got problem when pushing element with same exact value, how do i filter same element value but still accepting update/changes. JSFIDDLE
You can use arr.indexOf which returns -1 if it is not found, so you can add it then.
e.g.
if (arr.indexOf(item) == -1) {
arr.push(item);
}
However, this does not work in old browsers...
JQuery has a method ($.indexOf) that works in every browser, even very old ones.
Just javascript is enough.
If an array contains the item its index is >= 0
so you can do this, if index == -1 , item does not exist in the array, so you can push unique item
if(arr.indexOf(item) == -1) {
arr.push(item);
}
Edit:
You asked for changing the number, this is how
var index = arr.indexOf(item);
if(index > -1) { //checking if item exist in array
arr[index]++; // you can access that element by using arr[index],
// then change it as you want, I'm just incrementing in above example
}
As what most of the answers have pointed out, you can use the Array.prototype.indexOf() method to determine if a value exists or not. In order to check for this, check the array of strings against your ng-models value property, selects.value, within the ng-change() event callback.
DEMO
Javascript
$scope.goChange = function(name, value){
if(!~arr.indexOf(value)) {
arr.push(name, value);
console.log(arr);
}
};
HTML
<select ng-model="selects" ng-change="goChange(item.name, selects.value)" ng-options="i as i.value for i in options">
<option value=""></option>
</select>
You should use Angular Filters.
Some implementations can be found as answers to a similar question: How to make ng-repeat filter out duplicate results
To filter duplicates in an array (es6):
let arr = ['foo', 'foo', 'bar', 'baz'];
Array.from(new Set(arr));
console.log(arr);
// ['foo', 'bar', 'baz'];

How do I use ng-options with an object data source index by integers?

I have a <select> element with its ng-options attribute set to an object data source:
<select id="mySelect3"
name="mySelect3"
ng-model="mySelect3"
ng-options="key as value for (key, value) in objectDataSourceInts"></select>
The object data source is indexed with integers instead of strings:
$scope.objectDataSourceInts = {
1: 'one'
, 2: 'two'
, 4: 'four'
, 6: 'six'
};
I’m trying to set the value of this element in my controller on page load:
$scope.mySelect3 = 6;
I’d expect the matching value ('six') to be the selected option in the <select> on page load, but it’s not. An empty option is selected instead.
Full Plunk example: http://plnkr.co/edit/OWePopAWAIy9qcgNgQE6?p=preview
How can I set this value on page load?
Aha — it looks like I need to specify the key as a string, not its original integer form, when setting the value for this field. So, instead of:
$scope.mySelect3 = 6;
I do this:
$scope.mySelect3 = "6";
And it works as expected.
http://plnkr.co/edit/OWePopAWAIy9qcgNgQE6?p=preview

AngularJS custom filter function

Inside my controller, I would like to filter an array of objects. Each of these objects is a map which can contain strings as well as lists
I tried using $filter('filter')(array, function) format but I do not know how to access the individual elements of the array inside my function. Here is a snippet to show what I want.
$filter('filter')(array, function() {
return criteriaMatch(item, criteria);
});
And then in the criteriaMatch(), I will check if each of the individual property matches
var criteriaMatch = function(item, criteria) {
// go thro each individual property in the item and criteria
// and check if they are equal
}
I have to do all these in the controller and compile a list of lists and set them in the scope. So I do need to access the $filter('filter') this way only. All the examples I found in the net so far have static criteria searches inside the function, they don't pass an criteria object and test against each item in the array.
You can use it like this:
http://plnkr.co/edit/vtNjEgmpItqxX5fdwtPi?p=preview
Like you found, filter accepts predicate function which accepts item
by item from the array.
So, you just have to create an predicate function based on the given criteria.
In this example, criteriaMatch is a function which returns a predicate
function which matches the given criteria.
template:
<div ng-repeat="item in items | filter:criteriaMatch(criteria)">
{{ item }}
</div>
scope:
$scope.criteriaMatch = function( criteria ) {
return function( item ) {
return item.name === criteria.name;
};
};
Here's an example of how you'd use filter within your AngularJS JavaScript (rather than in an HTML element).
In this example, we have an array of Country records, each containing a name and a 3-character ISO code.
We want to write a function which will search through this list for a record which matches a specific 3-character code.
Here's how we'd do it without using filter:
$scope.FindCountryByCode = function (CountryCode) {
// Search through an array of Country records for one containing a particular 3-character country-code.
// Returns either a record, or NULL, if the country couldn't be found.
for (var i = 0; i < $scope.CountryList.length; i++) {
if ($scope.CountryList[i].IsoAlpha3 == CountryCode) {
return $scope.CountryList[i];
};
};
return null;
};
Yup, nothing wrong with that.
But here's how the same function would look, using filter:
$scope.FindCountryByCode = function (CountryCode) {
// Search through an array of Country records for one containing a particular 3-character country-code.
// Returns either a record, or NULL, if the country couldn't be found.
var matches = $scope.CountryList.filter(function (el) { return el.IsoAlpha3 == CountryCode; })
// If 'filter' didn't find any matching records, its result will be an array of 0 records.
if (matches.length == 0)
return null;
// Otherwise, it should've found just one matching record
return matches[0];
};
Much neater.
Remember that filter returns an array as a result (a list of matching records), so in this example, we'll either want to return 1 record, or NULL.
Hope this helps.
Additionally, if you want to use the filter in your controller the same way you do it here:
<div ng-repeat="item in items | filter:criteriaMatch(criteria)">
{{ item }}
</div>
You could do something like:
var filteredItems = $scope.$eval('items | filter:filter:criteriaMatch(criteria)');

Resources