AngularJS how to compare similar objects in ng-repeat - angularjs

I currently want to make a system that can compare products and their prices and list them.
I have found a way to filter duplicate products in AngularJS using the unique filter of AngularUI. I have two JSON files each filled with products of two different providers.
But I don't want it to remove the duplicate I want to get back a list of the similar products so someone can compare the prices and choose his favorite provider of the product.
If I run a ng-repeat with the unique filter it simply removes a duplicate. Is there a compare filter?

In order to do what you want, you'd need to write a custom filter that does the comparison and determines whether two objects are similar by your definition. Something like this. Here is a plunk:
var app = angular.module('app', [])
.filter('similar', function() {
return function(input, comparedObject) {
console.log(comparedObject);
var newArray = [];
for (i = 0; i < input.length; i++) {
if (input[i].itemType === comparedObject.itemType) {
newArray.push(input[i]);
}
}
return newArray;
}
});
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
$scope.baseObject = {
name: 'fred',
itemType: 1
};
$scope.castList = [
{
name: 'wilma',
itemType: 2
},
{
name: 'bam-bam',
itemType: 1
},
{
name: 'barney',
itemType: 1
},
{
name: 'dino',
itemType: 3
}
];
});
Then you would need something like this in the view's HTML.
<body ng-app="app">
<div ng-controller="SomeCtrl">
<div ng-repeat="person in castList | similar : baseObject">
<p>{{person.name}}</p>
</div>
</div>
</body>
Notice that the "baseObject" referred to in the filter in the view is defined in the scope from the controller.
This custom filter compares based upon the object's "itemType" property, which in my example is just a number. You'd have to re-write the filter to compare based on what you want. So what you get here is a display of only those objects that have an itemType property of 1, which is the same as the baseObject's.

Related

How to sort on input text in Angular?

I am new to Angular. I have an input text box where I am going to enter sorting criteria like "rating" and "date". I have a controller defined with data.
comments: [{
rating:5,
date:"2012-10-16T17:57:28.556094Z"
},
{
rating:4,
date:"2014-09-05T17:57:28.556094Z"
}]
How can I define filters which will do the sorting based on input?
Working Plnkr
In this example, when you type rating or date in input text field, it will sort the data based on this value. I have called the method on key press. If you want to sort after clicking on sort button, add a button and call he sorting method on ng-click.
var myApp = angular.module('myApp',[]);
myApp.controller('MyController',function($scope,$filter){
$scope.test = 'This is a test!';
$scope.comments = [
{
rating:5,
date:"2012-10-16T17:57:28.556094Z"
},
{
rating:4,
date:"2014-09-05T17:57:28.556094Z"
},
{
rating:7,
date:"2010-09-05T17:57:28.556094Z"
}];
$scope.comments2 = $scope.comments;
$scope.$watch('sortItem', function(val)
{
if(val==='rating' || val==='date'){
$scope.comments = $filter('orderBy')($scope.comments2,val);
}
else{
return;
}
});
});
Hope that solve your problem.
You can use a collections library like underscorejs, and it will be like,
Bind your input box value to a variable as,
<select ng-model=inputVal></select>
Inside your controller function,
$scope.arr = _.sortBy($scope.originalArray, function(o) {
return o[$scope.inputVal];
})
Is this process not working?
<div ng-app ng-controller="myCtrl">
<input type="text" ng-model="sortOption" />
<ul>
<li ng-repeat="comment in comments | orderBy: sortOption">
rate: {{comment.rating}}
</li>
</ul>
AngularJS
$scope.sortOption = "rating";
$scope.comments = [
{
rating:4,
date:"2015-10-16T17:57:28.556094Z"
},
{
rating:5,
date:"2014-09-05T17:57:28.556094Z"
}
]
In your controller add this:
this.sortOption = ''
Call the sortOption above on your input text as follow:
ng-model="sortOption"
Finally modify your ng-repeat as follow:
ng-repeat="comment in YourControllerName.comments|orderBy: sortOption"
In a nutshell :if you type :date then it sorts by date. If you type author then it sorts by author, if you type rating then it sorts by rating.
All you need to do is to apply ng-model to your input to create a binding no need for ng-click. With two way data binding once the input field is touched the filter will be applied so:
<input type="text" ng-model="orderKey" name="orderField">
then using the repeat directive
ng-repeat = "comment in comments | orderBy:orderKey"
Pay attention to spacing between orderBy and the (:) NO SPACE

Angularjs filter by more than one value from the scope data

Currently I have this in my controller:
$scope.showPostsByTags = function (tag) {
$scope.selectedTag = tag
};
And in my view I have this in my ng-repeat:
ng-repeat="post in postsData| filter: selectedTag"
The "selectedTag" property contains one string only. I am getting my tag from one menu with links:
ng-click="showPostsByTags(tag)"
Which is alright!
I am trying to archieve multiple tags and I want to display the posts not only by one string, but by multiple.
For example my scope property would contain many values pushed in array and $scope.selectedTag would return 'world', 'sports', 'strike'.
Unfortunately I cannot filter my posts by multiple values from array/object
I guess I need something to replace my current "filter: selectedTag" but I cannot figure it out. I found solutions like this but they solve different problems. Can you give me some guidance? Thanks a lot!
Do you mean like this?
http://plnkr.co/edit/6GwqaS7xVLZapeADSRUw?p=preview
app.controller('MainCtrl', function($scope) {
$scope.tags = ['apple', 'orange'];
$scope.selectedFilter = function(val) {
var found = false;
angular.forEach($scope.tags, function(tag) {
if (val.indexOf(tag) !== -1) {
found = true;
}
})
return found;
}
$scope.data = [
'scrambled eggs',
'green eggs and ham',
'apples and oranges',
'apple pie',
'orange juice',
'eggs, oranges, apples'
];
});
Template:
<body ng-controller="MainCtrl">
<p>Hello {{name}}!</p>
<p>selected: {{tags}}</p>
<p ng-repeat="d in data | filter:selectedFilter">{{d}}</p>
</body>
This will select only the strings appearing in tags. Hooking that in I leave to you.
I've wrote quickly a custom filter for you:
app.filter('multipleTags', function($filter){
return function multipleTags (items, predicates) {
angular.forEach(predicates, function(predicate){
items = $filter('filter')(items, predicate)
//if you want to match exact tag you can use exact match filter
//items = $filter('filter')(items, predicate, true)
})
return items;
}
})
if you go to this working plunk http://plnkr.co/edit/ZhnuM0WW3GZS5QLxK98t?p=preview you will see it in action
if you specify i.e. single letters like b,e it will select only element that contains both,you can obviously make it works with whole words

Disabling nesting filter in AngularJS

There is the following code:
tr ng-repeat="order in orders | filter: { restaurant: { id: currentRestaurant.id } } | orderBy:'-id'"
This code works good and filter orders by restaurant.id correctly. But I need to disable this filter in some case in order to show all orders. My controller code:
$scope.setCurrentRestaurant = (restaurant) ->
$scope.currentRestaurant = restaurant
I don't know how I can disable filtering programmatically or using filter directive. Thanks in advance.
If the filter parameter is undefined then it does not filter. If the filter parameter is the filtering expression, then it filters based on that.
So, you could introduce a filter expression like so:
"filter: (enableFilter || undefined) && {restaurant:{id:currentRestaurant.id}}"
plunker
I think that Angular doesn't contain native features for that.
But you can create custom solution for this case.
For example, you can broadcast event, when you want to disable filter. This event can be broadcasted from anywhere (service, directive, controller). For simplicity, here I've broadcasted it from controller.
Also I've created filterByRestaurant in the controller.
View:
<div ng-controller="MyCtrl">
<div ng-repeat="order in orders | filter:filterByRestaurant">
{{order.name}}
</div>
<button ng-if="enableFilter" ng-click="disableOrEnableFilter()">Disable filter</button>
<button ng-if="!enableFilter" ng-click="disableOrEnableFilter()">Enable filter</button>
</div>
Controller:
function MyCtrl($scope, $rootScope) {
$scope.enableFilter = true;
$scope.currentRestaurant = {id: 2, name: "Rest2"};
$scope.orders = [
{id:1, restId: 1, name: "order from Rest1"},
{id:2, restId: 2, name: "order from Rest2"},
{id:3, restId: 3, name: "order from Rest3"}
];
$scope.filterByRestaurant = function(order) {
return $scope.currentRestaurant.id == order.restId || !$scope.enableFilter;
};
$scope.$on('filter:disable', function() {
$scope.enableFilter = !$scope.enableFilter;
});
$scope.disableOrEnableFilter = function(){
$rootScope.$broadcast("filter:disable");
};
}
This is working JSFiddle with example.
Hope it will help.

angularjs - how to get in the controller the index of an item in a ng-repeat filter based on the value of one of its properties?

I use a ng-repeat in my html file to display filtered items:
<li ng-repeat="item in (filteredItems = (items | filter:query))">
{{ item.name }}
</a>
In the controller, I'd like to get the index of an item based on one of his property.
Precision: I'd like to get the index in the filtered list and not in the whole list.
Here for example, it will be the index of the item witch name is some_item_7.
var app = angular.module('myApp', []);
app.controller('MyCtrl', ['$scope',
function MyCtrl($scope) {
$scope.query = 'some';
$scope.items =
[
{ name: 'some_item_1' },
{ name: 'another_item_2' },
{ name: 'some_item_3' },
{ name: 'another_item_4' },
{ name: 'some_item_5' },
{ name: 'another_item_6' },
{ name: 'some_item_7' },
{ name: 'another_item_8' },
{ name: 'some_item_9' }
];
$scope.itemNext = function (item) {
console.log(item.name);
};
$scope.getIndexFromName = function (name) {
console.log("trying to get the index of the item with name = " + name);
}
$scope.getIndexFromName('some_item_7');
}
]);
http://plnkr.co/edit/C8gL9qV1MyonTwDENO9L?p=preview
Any idea ?
Your ng-repeat expression creates the filteredList array on your scope.
<li ng-repeat="item in (filteredItems = (items | filter:query))">
You can loop through it like any array, checking for the item matching the name parameter.
$scope.filteredItems
Here is a demo: http://plnkr.co/69nnbaZaulgX0odG7g7Y
See this related post: AngularJS - how to get an ngRepeat filtered result reference
Update
Your comments indicate that you don't want to wait for ng-repeat to create the array of filtered items. You can use the $filter service to easily initialize the same array before the page loads. Use:
$scope.filteredItems = $filter('filter')($scope.items, {name: $scope.query}, false)
Doing so does not interfere with ng-repeat saving its filter results to the same filteredItems array during DOM creation.
Here is an updated (and interactive) demo: http://plnkr.co/NSvBz1yWvmeFgXITutZF

How to use a filter in a controller?

I have written a filter function which will return data based on the argument you are passing. I want the same functionality in my controller. Is it possible to reuse the filter function in a controller?
This is what I've tried so far:
function myCtrl($scope,filter1)
{
// i simply used the filter function name, it is not working.
}
Inject $filter to your controller
function myCtrl($scope, $filter)
{
}
Then wherever you want to use that filter, just use it like this:
$filter('filtername');
If you want to pass arguments to that filter, do it using separate parentheses:
function myCtrl($scope, $filter)
{
$filter('filtername')(arg1,arg2);
}
Where arg1 is the array you want to filter on and arg2 is the object used to filter.
Answer provided by #Prashanth is correct, but there is even easier way of doing the same. Basically instead of injecting the $filter dependency and using awkward syntax of invoking it ($filter('filtername')(arg1,arg2);) one can inject dependency being: filter name plus the Filter suffix.
Taking example from the question one could write:
function myCtrl($scope, filter1Filter) {
filter1Filter(input, arg1);
}
It should be noted that you must append Filter to the filter name, no matter what naming convention you're using:
foo is referenced by calling fooFilter
fooFilter is referenced by calling fooFilterFilter
Using following sample code we can filter array in angular controller by name. this is based on following description.
http://docs.angularjs.org/guide/filter
this.filteredArray = filterFilter(this.array, {name:'Igor'});
JS:
angular.module('FilterInControllerModule', []).
controller('FilterController', ['filterFilter', function(filterFilter) {
this.array = [
{name: 'Tobias'},
{name: 'Jeff'},
{name: 'Brian'},
{name: 'Igor'},
{name: 'James'},
{name: 'Brad'}
];
this.filteredArray = filterFilter(this.array, {name:'Igor'});
}]);
HTML
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Example - example-example96-production</title>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.0-beta.3/angular.min.js"></script>
<script src="script.js"></script>
</head>
<body ng-app="FilterInControllerModule">
<div ng-controller="FilterController as ctrl">
<div>
All entries:
<span ng-repeat="entry in ctrl.array">{{entry.name}} </span>
</div>
<div>
Filter By Name in angular controller
<span ng-repeat="entry in ctrl.filteredArray">{{entry.name}} </span>
</div>
</div>
</body>
</html>
Here's another example of using filter in an Angular controller:
$scope.ListOfPeople = [
{ PersonID: 10, FirstName: "John", LastName: "Smith", Sex: "Male" },
{ PersonID: 11, FirstName: "James", LastName: "Last", Sex: "Male" },
{ PersonID: 12, FirstName: "Mary", LastName: "Heart", Sex: "Female" },
{ PersonID: 13, FirstName: "Sandra", LastName: "Goldsmith", Sex: "Female" },
{ PersonID: 14, FirstName: "Shaun", LastName: "Sheep", Sex: "Male" },
{ PersonID: 15, FirstName: "Nicola", LastName: "Smith", Sex: "Male" }
];
$scope.ListOfWomen = $scope.ListOfPeople.filter(function (person) {
return (person.Sex == "Female");
});
// This will display "There are 2 women in our list."
prompt("", "There are " + $scope.ListOfWomen.length + " women in our list.");
Simple, hey ?
There are three possible ways to do this.
Let's assume you have the following simple filter, which converts a string to uppercase, with a parameter for the first character only.
app.filter('uppercase', function() {
return function(string, firstCharOnly) {
return (!firstCharOnly)
? string.toUpperCase()
: string.charAt(0).toUpperCase() + string.slice(1);
}
});
Directly through $filter
app.controller('MyController', function($filter) {
// HELLO
var text = $filter('uppercase')('hello');
// Hello
var text = $filter('uppercase')('hello', true);
});
Note: this gives you access to all your filters.
Assign $filter to a variable
This option allows you to use the $filter like a function.
app.controller('MyController', function($filter) {
var uppercaseFilter = $filter('uppercase');
// HELLO
var text = uppercaseFilter('hello');
// Hello
var text = uppercaseFilter('hello', true);
});
Load only a specific Filter
You can load only a specific filter by appending the filter name with Filter.
app.controller('MyController', function(uppercaseFilter) {
// HELLO
var text = uppercaseFilter('hello');
// Hello
var text = uppercaseFilter('hello', true);
});
Which one you use comes to personal preference, but I recommend using the third, because it's the most readable option.
function ngController($scope,$filter){
$scope.name = "aaaa";
$scope.age = "32";
$scope.result = function(){
return $filter('lowercase')($scope.name);
};
}
The controller method 2nd argument name should be "$filter" then only the filter functionality will work with this example. In this example i have used the "lowercase" Filter.
I have another example, that I made for my process:
I get an Array with value-Description like this
states = [{
status: '1',
desc: '\u2713'
}, {
status: '2',
desc: '\u271B'
}]
in my Filters.js:
.filter('getState', function () {
return function (input, states) {
//console.log(states);
for (var i = 0; i < states.length; i++) {
//console.log(states[i]);
if (states[i].status == input) {
return states[i].desc;
}
}
return '\u2718';
};
})
Then, a test var (controller):
function myCtrl($scope, $filter) {
// ....
var resp = $filter('getState')('1', states);
// ....
}
AngularJs lets you to use filters inside template or inside Controller, Directive etc..
in template you can use this syntax
{{ variable | MyFilter: ... : ... }}
and inside controller you can use injecting the $filter service
angular.module('MyModule').controller('MyCtrl',function($scope, $filter){
$filter('MyFilter')(arg1, arg2);
})
If you need more with Demo example here is a link
AngularJs filter examples and demo
There is another way to evaluate filters that mirrors the syntax from the views. The invocation is hairy but you could build a shortcut to it. I like that the syntax of the string is identical to what you'd have in a view. Looks like this:
function myCtrl($scope, $interpolate) {
$scope.$eval($interpolate( "{{ myvar * 10 | currency }} dollars." ))
}
It seems nobody has mentioned that you can use a function as arg2 in $filter('filtername')(arg1,arg2);
For example:
$scope.filteredItems = $filter('filter')(items, function(item){return item.Price>50;});
Simple date example using $filter in a controller would be:
var myDate = new Date();
$scope.dateAsString = $filter('date')(myDate, "yyyy-MM-dd");
As explained here - https://stackoverflow.com/a/20131782/262140
Use below code if we want to add multiple conditions, instead of single value in javascript angular filter:
var modifiedArray = $filter('filter')(array,function(item){return (item.ColumnName == 'Value1' || item.ColumnName == 'Value2');},true)
First of all inject $filter to your controller, making sure ngSanitize is loaded within your app, later within the controller usage is as follows:
$filter('linky')(text, target, attributes)
Always check out the angularjs docs
if you want to filter object in controller try this
var rateSelected = $filter('filter')($scope.GradeList, function (obj) {
if(obj.GradeId == $scope.contractor_emp.save_modal_data.GradeId)
return obj;
});
This will return filtered object according to if condition
Reusing An Angular.js Filter - View / Controller
This Solution is covering reusing Angular Filters. Which is yet another way to filter data, and Google landed me here when I needed such; and I like to share.
Use Case
If you are already filtering, say in an ng-repeat in your view (as below), then you might have defined a filter in the controller as further follows. And then you can reuse as in the final examples.
Filter Use Example - Filtered Repeat in View
<div ng-app="someApp" ng-controller="someController">
<h2>Duplicates</h2>
<table class="table table-striped table-light table-bordered-light">
<thead>
<tr>
<th>Name</th>
<th>Gender</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="person in data | filter: searchDuplicate:true">
<td>{{person.name}}</td>
<td>{{person.gender}}</td>
</tr>
</tbody>
</table>
</div>
Angular Filter Definition Example
angular.module('someApp',[])
.controller('someController', function($scope, $filter ) {
$scope.people = [{name: 'Bob', gender: 'male' , hasDuplicate: true },
{name: 'Bob', gender: 'male' , hasDuplicate: true },
{name: 'Bob', gender: 'female', hasDuplicate: false}];
$scope.searchDuplicate = { hasDuplicate : true };
})
So, the concept here is that you are already using a filter created for your view, and then realize you would like to use it in your controller also.
Filter Function Use Within Controller Example 1
var dup = $filter('filter')($scope.people, $scope.searchDuplicate, true)
Filter Function Use Within Controller Example 2
Show a Button only if no duplicates are found, using the prior filter.
Html Button
<div ng-if="showButton()"><button class="btn btn-primary" ng-click="doSomething();"></button></div>
Show/Hide Button
$scope.doSomething = function(){ /* ... */ };
$scope.showButton = function(){ return $filter('filter')($scope.people, $scope.searchDuplicate, true).length == 0; };
Some may find this version of filtering easy, and it is an Angular.js option.
The optional comparator parameter "true" used in the view and in the $filter function call specifies you want a strict comparison. If you omit, values can be searched for over multiple columns.
https://docs.angularjs.org/api/ng/filter/filter

Resources