How to sort on input text in Angular? - angularjs

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

Related

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 compare similar objects in ng-repeat

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.

editable with ngrepeat: automatically editing the latest added item

I need to add new items to a collection, that gets rendered with ngrepeat and using xeditable make it automatically editable.
BTW, I'm using the "manual trigger" method for xeditable.
Here it is the HTML
<h4>Angular-xeditable demo</h4>
<div ng-app="app" ng-controller="Ctrl" style="margin: 50px">
<div class="btn btn-default" ng-click="addNew()">+</div>
<ul>
<li ng-repeat="item in array | orderBy:'-value'">
{{ item.field }}
<i ng-show="!itemForm.$visible" ng-click="itemForm.$show()">edit</i>
</li>
</ul>
</div>
and here the controller:
var app = angular.module("app", ["xeditable"]);
app.run(function(editableOptions) {
editableOptions.theme = 'bs3';
});
app.controller('Ctrl', function($scope, $filter) {
$scope.array = [
{value: 1, field: 'status1'},
{value: 2, field: 'status2'},
{value: 3, field: 'status3'},
{value: 4, field: 'status4'}
];
$scope.addNew = function(){
$scope.array.push({value:$scope.array.length+1, field: 'enter text here'});
//MAKE IT EDITABLE????????
}
});
Take a look to the issue in this fiddle: http://jsfiddle.net/dpamio/hD5Kh/1/
Here is a updated fiddle that works. Because of how the directive was written, and how ng-repeat works, it required an extremely hacky solution...
app.controller('Ctrl', function($scope, $filter, $timeout) {
$scope.itemForms = {};
$scope.addNew = function(){
$scope.array.push({value:$scope.array.length+1, field: 'enter text here'});
// Use timeout to force evaluation after the element has rendered
// ensuring that our assignment expression has run
$timeout(function() {
$scope.itemForms[0].$show(); // last index since we sort descending, so the 0 index is always the newest
})
}
Background on how ng-repeat works: ng-repeat will create a new child scope for each element that is repeated. The directive assigns a variable on that scope using the string passed into e-form for its name (in this case itemForm). If it was smarter, it'd allow for expression evaluation for assignment. (Then we could assign it to the parent scope, and access it in the controller, but that's a different matter).
Since we don't have any way to access this child scope outside of the directive, we do something very bad. We use the mustache expression in a span of display none to assign the itemForm variable to the parent scope so that we can use it later. Then inside our controller we use the look up value to call the itemForm.$show() method that we expect.
Abstracting that bit of nastyness into an angular directive, we could write the following:
.directive('assignFromChild', function($parse) {
return {
restrict: 'A',
link: function(scope, el, attrs) {
scope.$watch(function() { return $parse(attrs.assignFromChild)(scope); }, function(val) {
$parse('$parent.' + attrs.toParent).assign(scope, val);
})
}
};
});
Allowing our HTML to go back down to:
<ul>
<li ng-repeat="item in array | orderBy:'-value'" assign-from-child="itemForm" to-parent="itemForms[{{$index}}]">
{{ item.field }}
<i ng-show="!itemForm.$visible" ng-click="itemForm.$show()">edit</i>
</li>
</ul>
Here is a fiddle with my final solution
I found a very simple solution using ng-init="itemForm.$show()", that will activate the xeditable form when the new item is inserted.
Here's the updated jsFiddle answering the question: http://jsfiddle.net/hD5Kh/15/

Saving a checkbox list in Angularjs with node

I want to save and load a checkbox list using binding in angularjs with a node backend. This SO question (How do I bind to list of checkbox values with AngularJS?) answers how I can load the check box from a static javascript object but how can I save the checkbox values after the user selects them?
I want to save the checkbox values in a single field but how can I tell angular to bind the checkbox values into a single field defined in a model to my mongodb? I cant just use ng-model as there are multiple checkboxes.
Needless to say that I am new to angular so any help here is greatly appreciated ...
Thanks for any help you can provide.
kseudo
Just add ng-model to your checkbox. By this way you can update model in controller on any checkbox state change.
Here is example:
HTML
<div ng-controller="Ctrl">
<label ng-repeat="fruit in fruits">
<input
type="checkbox"
name="fruit.name"
ng-model="fruit.value"
> {{fruit.name}}
</label>
<pre>{{fruits| json}}</pre>
</div>
JS
var app = angular.module('app', []);
function Ctrl($scope) {
$scope.fruits = [{
name: 'apple',
value: false
}, {
name: 'orange',
value: false
}, {
name: 'pear',
value: false
}, {
name: 'naartjie',
value: false
}];
}
Demo Fiddle
[EDIT]
BTW we can make the copy by using angular.copy() method. When we press button, the new copy of fruits model will be created (and you should send it to server as json). Old model fruits will stay the same:
$scope.fruitsCopy = [];
$scope.init = function(){
$scope.fruitsCopy = angular.copy($scope.fruits );
}
To convert data to JSon I would use:
var jsonData = JSON.stringify($scope.fruitsCopy);
Demo2 Fiddle
Let's say you defined your model as such:
function Ctrl($scope) {
$scope.items = [{
name: 'A',
checked: false
}, {
name: 'B',
checked: false
}, {
name: 'C',
checked: false
}, {
name: 'D',
checked: false
}];
}
And created a view for the model:
<ul>
<li ng-repeat="item in items">
<label>
<input type="checkbox" ng-model="item.checked">
{{item.name}}
</label>
</li>
</ul>
<button ng-click="save()">save</button>
Next you have to define save function:
$scope.save = function() {
//angular.toJson converts array to string, something like
// '[{"name":"A","checked":false},{"name":"B","checked":true},...]'
var json = angular.toJson($scope.items);
//Service is angular service for your model that makes http requests to your backend
Service.save(json).then(function(){
//here you can notify user that data is persisted
}, function() {
//here you can notify user that there was a problem with request
});
}
And a simple model service:
.service('Service', function($http) {
return new function() {
this.save = function(data) {
return $http.post('url/to/backend', data);
}
}
});

Resources