How to use functions with ngOptions? - angularjs

I have these 2 functions,
function functionCall(orderByRelease_date){
orderByClick = "release_date"
orderBy();
console.log ('release')
}
function functionCall(orderByCreated_at){
orderByClick = "created_at"
orderBy();
console.log ('created')
}
These functions set a variable orderByClick for this function,
var orderBy = function(){
$scope.movies = response.data;
var orderBy = $filter('orderBy');
$scope.orderedMovies = orderBy($scope.movies, orderByClick);
var movies = $scope.orderedMovies
var i,j,temparray,chunk = 8, movieGroups=[];
for (i=0,j=movies.length; i<j; i+=chunk) {
temparray = movies.slice(i,i+chunk);
movieGroups.push(temparray);
}
$scope.movieGroupsSuggestions = movieGroups
}
I had created a options list like this,
%select
%option{:onselect => "functionCall(orderByRelease_date)"} Sort by release date.
%option{:onselect => "functionCall(orderByCreated_at)"} Sort by newest
But it doesn't seem to do anything.
So I stumbled on to ngoptions and it seemed the way to go, but it's not clear to me how I can use functions with it.
I've created a scope with the options,
$scope.orderList = [
'created_at',
'release_date'
]
And a ng-options element,
%select{"ng-options" => "option for option in orderList"}
This displays the values from orderList, but how do I hook this up so that the variable oderByClick in my orderBy function changes?

To answer my own question,
I've created a scope with the values I want to insert in my javascript code,
$scope.listOfOptions = [
'release_date',
'created_at',
];
Then create a function that reacts to the ng-options,
$scope.selectedItemChanged = function(){
orderByClick = $scope.selectedItem;
orderBy();
}
So this sets the value from listOfOptions to the variable orderByClick, and then calls orderBy function.
And then in the oderBy function I have,
$scope.orderedMovies = orderBy($scope.movies, orderByClick);
And this is the haml,
%select{"ng-change" => "selectedItemChanged()", "ng-model" => "selectedItem", "ng-options" => "option for option in listOfOptions"}
My problem was not knowing about the ng-change, that's what creates the interaction between controller and the view.

Related

AngularJS: How to create new arrays from JSON response?

I have a function tied to the factory for my controller:
fac.GetDailyCountersList = function () {
return $http.get('/Data/GetDailyCountersList')
}
Inside the controller, here's where the function is called:
$scope.DailyCounters = null;
DailyCounterService.GetDailyCountersList().then(function (d) {
$scope.DailyCounters = d.data;
}, function (error) {
alert('Error!');
});
Based on this, if I were to output the variable on the html page, this would show:
[{"Id":1,"MIDay":"16","MITime":"900","MICounter":0},
{"Id":2,"MIDay":"16","MITime":"915","MICounter":1},
{"Id":3,"MIDay":"16","MITime":"930","MICounter":0},
{"Id":4,"MIDay":"17","MITime":"945","MICounter":0},
{"Id":5,"MIDay":"17","MITime":"1000","MICounter":0},
{"Id":6,"MIDay":"17","MITime":"1015","MICounter":52},
{"Id":7,"MIDay":"18","MITime":"1030","MICounter":2},
{"Id":8,"MIDay":"18","MITime":"1045","MICounter":3},
{"Id":9,"MIDay":"18","MITime":"1100","MICounter":0}]
My question is how to take this data and create other arrays based on one of the properties of the Json data.
For example, how would I put arrays
{"Id":1,"MIDay":"16","MITime":"900","MICounter":0},
{"Id":2,"MIDay":"16","MITime":"915","MICounter":1},
{"Id":3,"MIDay":"16","MITime":"930","MICounter":0}
Into a new array "ArrayDay16"? And the rest into their respective arrays as well.
I was thinking about doing it this way:
var myArray = [];
if (d.data['MIDay'] == '16')
myArray.push(d.data);
But this is incorrect/fails. Any ideas for an efficient solution?
Use the filter function to populate new arrays. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
//This will populate "someArray" with only the data that has MIDAY = 16
DailyCounterService.GetDailyCountersList().then(function (d) {
$scope.someArray = d.data.filter(function(ele) {
return ele.MIDAY === '16';
});
}
You can use the filter function:
var daySixteen = data.filter(function (d) { return d.MIDay === "16"; });
you can try the next:
d.data.foreach(function(item){
if(item.MIDay == 16)
myArray.push(d.data);
});
If you want to be efficient you should do this on the back end with constraint(s). It's not the browsers job.
Although, if you still want it to filter it on the front end you could use the filter-function with some lamdba expressions
var newList = oldList.filter(i => i["MIDay"] == "16");

FIlter in controller with multiple values

I have 3 switches (checkboxes, they return true or false) to filter in a list.
The list:
vm.products = Product.query();
In my controller, i want to filter vm.products, everytime one of the switchboxes/checkboxes get changed.
All i got so far, is a none working, filter argument:
vm.products = $filter('filter')('id', 1);
The parameter 'filter' - sems like its pointing at a directive? Do i have to do that? And what would be the best way of making a dynamic filter function/builder, when there is multiple values to check on?
I found a way using a custom filter;
.filter("myFilter", function(){
return function(products, productTypes){
var selectedProducts = [];
for (i = 0; i < products.length; i++) {
if (productTypes.indexOf(products[i].productTypeId.toString()) > -1) {
selectedProducts.push(incidents[i]);
}
}
return selectedProducts;
};
})
I then use this in my controller to call the filter function;
vm.filterTypeUpdated = function ($event, typeId) {
var productTypesId = "";
if (vm.filterType1)
productTypesId += "1,";
if (vm.filterType2)
productTypesId += "2,";
if (vm.filterType3)
productTypesId += "3,";
vm.productsRoot = $filter('myFilter')(vm.products, productTypesId);
}
I don't feel like this is the cleanest way of doing it, but it works. If there are any inputs on optimizing this, i am all ears :-)

AngularJS - Multiple Filters usage in controller

I want to use multiple filters in controller
Currently using
$filter('limitTo')($filter('lowercase')($filter('translate')('ACTIVE')), 5)
If we have more filters like this. How can I use multiple filters in controller rather conventional format like this?
You can simply introduce variables:
var limitTo = $filter('limitTo');
var lowercase = $filter('lowercase');
var translate = $filter('translate');
var filteredValue = limitTo(lowercase(translate('ACTIVE')), 5);
Or even
var lowercaseStatus = lowercase(translate('ACTIVE'));
var filteredValue = limitTo(lowercaseStatus, 5);
Another strategy would be to use the same syntax as in the view:
var filteredValue = $scope.$eval('"ACTIVE" | translate | lowercase | limitTo:5');
This is an interesting question. Usually you would do something like that or something like this:
var translatedValue = $filter('translate')('ACTIVE');
var lowercaseValue = $filter('lowercase')(translatedValue);
$scope.finalValue = $filter('limitTo')(lowercaseValue, 5)
I created a service inspired by this answer.
app.service('FilterChain',
['$filter', function($filter) {
var chain = {
value : '',
start : function(value) {
this.value = value;
return this;
},
applyFilter : function(filterName, args) {
args = args || [];
args.unshift(this.value);
this.value = $filter(filterName).apply(undefined, args)
return this;
}
};
return chain;
}]);
Usage is like this
$scope.value = FilterChain.start('Active')
.applyFilter('translate')
.applyFilter('limitTo', [5])
.applyFilter('uppercase')
.value;
You can use the service with other filters and objects such as arrays. See a working example here: JSFiddle

Testing Angular Filter That Returns An Array with Jasmine

So, I'm having issues testing an angular filter that takes an array that has previously been sorted by a group property. It uses a flag property to indicate that the item is the first observation of that group, and then false for subsequent observations.
I'm doing this to have a category header in the UI with an ng-repeat directive.
When I test the filter, the output does not return the array with the flags unless I create new objects for the return array. This is a problem, because it causes an infinite loop when running in a webpage. The code works in the webpage when it just adds a flag property to the input object.
Is there some additional step I should be taking to simulate how angular handles filters so that it outputs the proper array?
This is what my test looks like right now.
describe('IsDifferentGroup', function() {
var list, itemOne, itemTwo, itemThree;
beforeEach(module("App.Filters"));
beforeEach(function () {
list = [];
itemOne = new ListItem();
itemTwo = new ListItem();
itemThree = new ListItem();
itemOne.group = "A";
itemTwo.group = "B";
itemThree.group = "C";
list.push(itemOne);
list.push(itemOne);
list.push(itemOne);
list.push(itemOne);
list.push(itemTwo);
list.push(itemThree);
list.push(itemThree);
list.push(itemThree);
list.push(itemThree);
list.push(itemThree);
});
it('should flag the items true that appear first on the list.', (inject(function (isDifferentGroupFilter) {
expect(list.length).toBe(10);
var result = isDifferentGroupFilter(list);
expect(result[0].isDifferentGroup).toBeTruthy();
expect(result[1].isDifferentGroup).toBeFalsy();
expect(result[4].isDifferentGroup).toBeTruthy();
expect(result[5].isDifferentGroup).toBeTruthy();
expect(result[6].isDifferentGroup).toBeFalsy();
expect(result[9].isDifferentGroup).toBeFalsy();
})));
});
And here is something like the code with the filter:
var IsDifferentGroup = (function () {
function IsDifferentGroup() {
return (function (list) {
var arrayToReturn = [];
var lastGroup = null;
for (var i = 0; i < list.length; i++) {
if (list[i].group != lastGroup) {
list[i].isDifferentGroup = true;
lastAisle = list[i].group;
} else {
list[i].isDifferentGroup = false;
}
arrayToReturn.push(list[i]);
}
return arrayToReturn;
});
}
return IsDifferentGroup;
})();
Thanks!
I figured out my issue.
When I was passing the items into the list, I just pushed a pointer to an item multiple times. I was not passing in unique objects so the flag was being overridden by the following flag in the array(I think). So, I just newed up 10 unique objects using a loop, pushed them into the array and ran it through the filter. And it worked.
I'm not entirely sure my analysis is correct about the override, because itemTwo was not being flagged as unique when it was the only itemTwo in the array. But the test is working as I would expect now so I'm going to stop investigating the issue.

Checking if object is empty, works with ng-show but not from controller?

I have a JS object declared like so
$scope.items = {};
I also have a $http request that fills this object with items. I would like to detect if this item is empty, it appears that ng-show supports this... I enter
ng-show="items"
and magically it works,I would also like to do the same from a controller but i can't seem to get it to work, it appears I may have to iterate over the object to see if it has any properties or use lodash or underscore.
Is there an alternative?
I did try
alert($scope.items == true);
but it always returns false , when the object is created and when populated with $http, so its not working that way.
Or you could keep it simple by doing something like this:
alert(angular.equals({}, $scope.items));
In a private project a wrote this filter
angular.module('myApp')
.filter('isEmpty', function () {
var bar;
return function (obj) {
for (bar in obj) {
if (obj.hasOwnProperty(bar)) {
return false;
}
}
return true;
};
});
usage:
<p ng-hide="items | isEmpty">Some Content</p>
testing:
describe('Filter: isEmpty', function () {
// load the filter's module
beforeEach(module('myApp'));
// initialize a new instance of the filter before each test
var isEmpty;
beforeEach(inject(function ($filter) {
isEmpty = $filter('isEmpty');
}));
it('should return the input prefixed with "isEmpty filter:"', function () {
expect(isEmpty({})).toBe(true);
expect(isEmpty({foo: "bar"})).toBe(false);
});
});
regards.
Use an empty object literal isn't necessary here, you can use null or undefined:
$scope.items = null;
In this way, ng-show should keep working, and in your controller you can just do:
if ($scope.items) {
// items have value
} else {
// items is still null
}
And in your $http callbacks, you do the following:
$http.get(..., function(data) {
$scope.items = {
data: data,
// other stuff
};
});
another simple one-liner:
var ob = {};
Object.keys(ob).length // 0
If you couldn't have the items OBJ equal to null, you can do this:
$scope.isEmpty = function (obj) {
for (var i in obj) if (obj.hasOwnProperty(i)) return false;
return true;
};
and in the view you can do:
<div ng-show="isEmpty(items)"></div>
You can do
var ob = {};
Object.keys(ob).length
Only if your browser supports ECMAScript 5. For Example, IE 8 doesn't support this feature.
See http://kangax.github.io/compat-table/es5/ for more infos
if( obj[0] )
a cleaner version of this might be:
if( typeof Object.keys(obj)[0] === 'undefined' )
where the result will be undefined if no object property is set.
Or, if using lo-dash: _.empty(value).
"Checks if value is empty. Arrays, strings, or arguments objects with a length of 0 and objects with no own enumerable properties are considered "empty"."
Check Empty object
$scope.isValid = function(value) {
return !value
}
you can check length of items
ng-show="items.length"

Resources