Pass array of object properties to filter - angularjs

I have a custom angular filter that I'm trying to make more generic. The filter looks a collection of employees and filters down the list based on the number of months that have passed since they were hired. I'm trying to make the filter more generic so I can use it on other lists that have dates but will have a different property name (for example: filter invoices that have been created in the last xx months). How can I pass in the name of the property I want to filter on, or how do I pass in the collection of just the date property instead of the whole object?
html
<html ng-app="dateFilterTest">
<head>
</head>
<body ng-controller="filterController">
<select ng-model="maxLookBack">
<option value="6">6 Months</option>
<option value="12" selected="selected">1 Year</option>
<option value="24">2 Year</option>
<option value="999">All</option>
</select>
<ul>
<li ng-repeat="person in people | DateAgeFilter:maxLookBack">{{person.name}}</li>
</ul>
</body>
<script type="text/javascript" src="js/angular.min.js"></script>
<script type="text/javascript" src="js/controllers/dateFiltercontroller.js"></script>
</html>
js
var app = angular.module('dateFilterTest', []);
app.filter('DateAgeFilter', function(){
return function(items, maxage) {
var minDate = new Date()
minDate.setMonth(minDate.getMonth() - maxage);
var result = [];
for (var i=0; i<items.length; i++) {
if (items[i].hireDate.getTime() > minDate.getTime()) {
result.push(items[i]);
}
}
return result;
};
});
app.controller('filterController', function($scope){
$scope.people = [
{'name': 'Person 1',
'hireDate': new Date('Jun 1, 2013')},
{'name': 'Person 2',
'hireDate': new Date('Aug 1, 2012')},
{'name': 'Person 3',
'hireDate': new Date('May 1, 2011')},
{'name': 'Person 4',
'hireDate': new Date('Jun 1, 2012')},
{'name': 'Person 5',
'hireDate': new Date('Mar 1, 2014')},
{'name': 'Person 6',
'hireDate': new Date('Mar 1, 2014')},
];
});
plunker:
http://plnkr.co/edit/9zOWxJdMNg5zHz7qYf7c

You can pass the property name in as an additional parameter to the filter- like so (using an additional colon separator):
DateAgeFilter:maxLookBack:'hireDate'
Below is an example of changing your filter to make use of that, with 2 changes:
1) The additional parameter- I've called it field here.
2) In order to use the parameter to specify the property, we switch from dot notation to bracket notation- changing items[i].hireDate.getTime() into items[i][field].getTime()
app.filter('DateAgeFilter', function(){
return function(items, maxage, field) {
var minDate = new Date()
minDate.setMonth(minDate.getMonth() - maxage);
var result = [];
for (var i=0; i<items.length; i++) {
if (items[i][field].getTime() > minDate.getTime()) {
result.push(items[i]);
}
}
return result;
};
});
updated plunker

Related

Selected value not displayed on AngularJS Select

My problem is when i change the value of the dropdown menu the value isn't displayed in the dropdown menu but the correct value is displayed in my console. Not sure why it isn't working at the moment because i have a same implementation of this in another project. Maybe i'm overlooking things.
On initialization i set
selected_type = types[0]
// What will result in Type1 being displayed on page load.
Fiddle you can find here: https://jsfiddle.net/596tzsh8/7/
Use:
ng-options="selected_type as selected_type.Name for selected_type in types track by selected_type.Id"
// ^
// the object itself
... instead of:
ng-options="selected_type.Id as selected_type.Name for selected_type in types track by selected_type.Id"
// ^
// the object's ID
Then, in your callback function, replace:
var typeID = this.selected_type;
// ^
// the object itself
... by:
var typeID = this.selected_type.Id;
// ^
// the object's ID
... and you're all set!
See your forked JSFiddle here, or play around with the snippet below.
var app = angular.module("app", ["controllers"])
var controllers = angular.module("controllers",[]);
controllers.controller('myController', function($scope){
$scope.types = [{
Id: 1,
Name: 'Type1'
}, {
Id: 2,
Name: 'Type2'
}, {
Id: 3,
Name: 'Type3'
}];
$scope.GetValueTypeDropdown = function () {
var typeID = this.selected_type.Id;
$scope.the_type = $.grep($scope.types, function (selected_type){
return selected_type.Id == typeID;
})[0].Name;
console.log($scope.the_type);
return $scope.the_type;
};
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
<div ng-controller="myController">
<form>
<select name="type" class="col-xs-12 dropdown-list"
ng-model="selected_type" class="col-xs-8"
ng-init="selected_type = types[0]"
ng-options="selected_type as selected_type.Name for selected_type in types track by selected_type.Id"
ng-change="GetValueTypeDropdown()">
</select>
</form>
</div>
</div>
You can change the select as follows,
<select name="type" class="col-xs-12 dropdown-list" ng-model="selected_type" class="col-xs-8" ng-init="selected_type = types[0]" ng-options="type as type.Name for type in types track by type.Id" ng-change="GetValueTypeDropdown()">
also you can have a $scope variable and display it on ng-change
$scope.GetValueTypeDropdown = function() {
console.log($scope.selected);
};
DEMO

Unbinding an array on AngularJS

This is the scenario. I have two scope arrays with predefined values which are "sorts" and "preSort" and they are not binded together. I have buttons that adds values to the "sorts" array and a reset button. The reset button is clicked so that the value of the the "sorts" array will be the same as the value of "preSort" array. However the problem is, when I click the reset. The two arrays are now binded. I wish to be not binded after clicking the reset button
Please see the following code:
function isInArray(value, array) {
return array.indexOf(value) > -1;
}
function ascendingArrayList(value, array){
new_value = value.replace('-', '')
index = array.indexOf(value);
// alert(index);
// Conditional statement is to make sure that it does not delete anything if field did not make any sorting yet
// Comment the conditional to enable single sorting for each field
if(index != -1){
array.splice(index, 1);
}
array.push(new_value);
return array;
}
function descendingArrayList(value, array){
new_value = value.replace('-', '')
index = array.indexOf(new_value);
// Conditional statement is to make sure that it does not delete anything if field did not make any sorting yet
// Comment the conditional to enable single sorting for each field
if(index != -1){
array.splice(index, 1);
}
array.push(value);
return array;
}
var app = angular.module('myApp', []);
app.config(function($interpolateProvider, $httpProvider) {
// Change template tags
$interpolateProvider.startSymbol('[[');
$interpolateProvider.endSymbol(']]');
});
app.controller('myController', function($scope) {
$scope.sorts = ["rank_order"];
$scope.preSort = ["rank_order"];
$scope.tableHeaders = [
{'name': 'No.', 'field':'-rank_order'},
{'name': 'Code'},
{'name': 'Description'},
{'name': 'Department', 'field':'-rank_department'},
{'name': 'Type', 'field':'-rank_type'},
{'name': 'Updated By', 'field':'-updated_by__bio__user_code'},
{'name': 'Date Updated', 'field':'-date_updated'},
];
$scope.reset = function(){
$scope.sorts = $scope.preSort;
}
$scope.sort = function(field){
if(isInArray(field, $scope.sorts)){
$scope.sorts = ascendingArrayList(field, $scope.sorts)
}else{
// $scope.sorts.push(field);
$scope.sorts = descendingArrayList(field, $scope.sorts)
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="myController">
<table border="1px">
<tr>
<th ng-repeat="x in tableHeaders">
[[x.name]] <span ng-if="x.field" ng-click="sort(x.field, $event)"><u>Click to Sort</u></span>
</th>
</tr>
</table>
preSort:[[ preSort ]] ------------------------------- sorts: [[ sorts ]]
<br />
<button ng-click="reset()">Click to reset</button>
</div>
Use angular.copy.
$scope.reset = function(){
$scope.sorts = angular.copy($scope.preSort);
}
For more info, see AngularJS angular.copy API Reference
Inside $scope.reset() you have to use something to clone the array instead of doing an assignment between scope variables.
You can use for example underscore's _.map functions
$scope.sorts = _.map($scope.preSorts, _.clone)

Angular js, Unique filter is not working

I'm trying to apply a check box filter to a list, but the options for the check boxes should also be come the list of items only,
it works fine if i am iterating it for all the check boxes,
the problem is coming when i am trying to apply the unique filter to display check boxes options.
i have included
angular 1.4 and ui-utils
<script src="js/angular.min.js"></script>
<script src="js/ui-utils.min.js"></script>
my view and controller are defined as:
<div ng-controller="Test">
<div ng-repeat="person in persons | unique:type">
<!-- record that this person has been selected -->
<input type="checkbox" ng-checked="person.checked" ng-model="person.checked" /> {{ person.type }}
</div>
<ul>
<li ng-repeat="person in persons | selectedTypes">{{person.name}}</li>
</ul>
</div>
and script is
<script>
var app = angular.module("MyApp", ['ui.utils']);
app.controller("Test", function($scope) {
$scope.persons = [
{ type: 1, name: 'Ankit Balyan' },
{ type: 1, name: 'Abhilaksh' },
{ type: 2, name: 'Sanket Srivastav' },
{ type: 2, name: 'Sachin Sharma' },
{ type: 2, name: 'Rohan Rathor' },
{ type: 2, name: 'Jim' },
];
$scope.$watch('persons', function() {
console.log($scope.persons);
})
});
// Define our filter
app.filter('selectedTypes', function($filter) {
return function(persons) {
var i, len;
// get persons that have been checked
var checkedPersons = $filter('filter')(persons, {checked: true});
// Add in a check to see if any persons were selected. If none, return
// them all without filters
if(checkedPersons.length == 0) {
return persons;
}
//console.log(checkedPersons);
// get all the unique cities that come from these checked persons
var types = {};
for(i = 0, len = checkedPersons.length; i < len; ++i) {
// if this checked persons cities isn't already in the cities object
// add it
if(!types.hasOwnProperty(checkedPersons[i].type)) {
types[checkedPersons[i].type] = true;
}
}
// Now that we have the cities that come from the checked persons, we can
//get all persons from those cities and return them
var ret = [];
for(i = 0, len = persons.length; i < len; ++i) {
// If this person's city exists in the cities object, add it to the
// return array
if(types[persons[i].type]) {
ret.push(persons[i]);
}
}
//console.log(persons.length);
// we have our result!
return ret;
};
});
</script>
You have to put the name of the property as a string :
<div ng-repeat="person in persons | unique: 'type'">
instead of
<div ng-repeat="person in persons | unique: type">
Edit: If you don't provide quotes, you are applying a unique filter by the value of the variable type, which is not defined in your case, therefore the filter has no effect.

AngularJS Select Not Binding To Ng-Model

My angular select isn't binding. I can tell the value is correct, but the select isn't updated. Why is not binding if the value is there?
<div ng-controller="MyController" ng-app>
<select class="form-control" ng-model="colorId"ng-options="color.id as color.name for color in colorList">
<option value="">--Select a Color--</option>
</select>
<input type="button" value="submit" ng-click="Select()"></input>
function MyController($scope) {
$scope.colorList = [{
id: '1',
name: 'red'
}, {
id: '2',
name: 'blue'
}, {
id: '3',
name: 'green'
}];
var colorId = 3;
$scope.colorId = colorId;
alert($scope.colorId);
$scope.Select = function () {
var colorId = 2;
$scope.colorId = colorId;
}
}
Here is a fiddle:
http://jsfiddle.net/ky5F4/23/
you need to change the id to a string when doing Select
$scope.Select = function () {
console.log('select fired');
var colorId = 1;
$scope.mySelection.colorId = colorId + "";
}
http://jsfiddle.net/bxkwfo0s/2/
next you should use a property of an object rather than just a scope variable, this will ensure proper model binding
ng-model="mySelection.colorId"
where the object could be something simple
$scope.mySelection = {colorId : colorId };
There are two errors with your code:
You are using colorList as your model in ng-options, but you are calling it datasets in your controller.
You use strings for the id, but set the $scope.colorId to a number.
Here is an updated fiddle changing ids to numbers and changing $scope.datasets to $scope.colorList
function MyController($scope) {
$scope.colorList = [{
id: 1,
name: 'red'
}, {
id: 2,
name: 'blue'
}, {
id: 3,
name: 'green'
}];
var colorId = 3;
$scope.colorId = colorId;
alert($scope.colorId);
$scope.Select = function () {
var colorId = 2;
$scope.colorId = colorId;
}
}
Consider making your ng-model be an object, specifically one of the objects that are already in your $scope.colorList. If you do that you should be able to avoid the post-processing you're doing in the click handler.
So your select will look like this:
<select class="form-control" ng-model="selectedColor"
ng-options="color.name for color in colorList"></select>
One gotcha is that if you have an object in your controller that looks JUST LIKE your red object, like$scope.selectedColorObj = { id : '1', name:'red' } and set the select's ng-model to that option, it won't work. Angular will see that you're setting to the ng-model to an object that's not actually in your data source and add an extra option with value="?", so I use $filter in this case to grab the matching member of the array:
$scope.colorId = '3';
$scope.selectedColor = $filter('filter')( $scope.colorList,{ id: $scope.colorId})[0];
See http://jsfiddle.net/ky5F4/92/

AngularJS matching data between json arrays and setting a selected option

$scope.opts =
{
unit: [
{ id: 1, val: "px", name: "px"},
{ id: 2, val: "%", name: "%"}
]
}
The above is my options list array and now I set my default option.
$scope.user.unit = $scope.opts.unit[0];
The above creates the following in my html
<select class="unit ng-pristine ng-valid" data-ng-options="a.name for a in opts.unit" data-ng-model="user.unit">
<option value="0" selected="selected">px</option>
<option value="1">%</option>
</select>
When I use the below I am pulling the data that was stored in a db from the options selected in the above example.
$http.get('/assets/inc/file.php?id='+thisPage).success(function(response) {
var userData = response.userData;
var locationData = response.locationData;
$scope.user = userData;
$scope.locations = locationData;
console.log($scope.user.unit);
});
This console.logs me the following Object { id=1, val="px", name="px"}
I may be wrong but the <select> box is binded to $scope.opts
How would I be able to link the retrieved data from $scope.user.unit to $scope.opts.unit so that when the data is retrieved it will then mark the correct option as :selected?
I'm not 100% sure but you can try this (or create JSFiddle):
JS:
$http.get('/assets/inc/file.php?id='+thisPage).success(function(response) {
var userData = response.userData;
var locationData = response.locationData;
$scope.user = userData;
$scope.locations = locationData;
$scope.selected = {};
angular.forEach($scope.opts.unit, function (value)
{
if (value.val == $scope.user.unit.val) {
$scope.selected = value
}
});
console.log($scope.user.unit);
});
and in View:
<select class="unit ng-pristine ng-valid" data-ng-options="a.name for a in opts.unit" data-ng-model="user.unit">
<option value="{{selected.val}}">{{selected.name}}</option>
</select>
Your ng-model for the select element is an object, and not a primitive type, which is fine, but then you reassign $scope.user to a brand new object (returned from $http.get), so user.unit is a new object too, so it's not identical to any of your ng-options. I can think of two ways which should fix the problem:
bind the select to the 'id' property of the unit object:
<select ng-options="a.id as a.name for a in opts.unit" ng-model="user.unit.id">
or leave the select bound to user.unit, but use the track by feature of ng-options:
<select ng-options="a.name for a in opts.unit track by a.id" ng-model="user.unit">
One of the things in Angular is that you rarely need to do is explicitly create <option> elements manually as the framework will generate this for you. Therefore, the following will work: (Working jsfiddle at http://jsfiddle.net/LMHLq/12/)
HTML:
<select data-ng-model='user.unit' data-ng-options="o.id as o.name for o in opts.unit"/>
JavaScript:
$scope.opts ={
unit: [
{ id: 1, val: "px", name: "px"},
{ id: 2, val: "%", name: "%"},
{ id: 3, val: "pt", name: "pt"}
]
}
$http.get('/assets/inc/file.php?id='+thisPage).success(function(response) {
var userData = response.userData;
var locationData = response.locationData;
$scope.user = userData;
$scope.locations = locationData;
console.log($scope.user.unit);
});
$scope.opts ={
unit: [
{ id: 1, val: "px", name: "px"},
{ id: 2, val: "%", name: "%"},
{ id: 3, val: "pt", name: "pt"}
]
}
I noticed that the $scope.opts builds my select element and populates it but when the data is retrieved via db it needs to go into $scope.user.unit but this is binded to $scope.opts so what I have done is sought out the ID for the item that was retrieved and then added -1 to it so it will select from the array of $scope.opts.unit
var testUnit = $scope.user.unit.id-1; //gets the ID of the unit thats been retrieved
$scope.user.unit = $scope.opts.unit[testUnit]; //sets the selected option in the dom

Resources