How to do two-way filtering in AngularJS?
It turns out that there's a very elegant solution to this, but it's not well documented.
Formatting model values for display can be handled by the |
operator and an angular formatter
. It turns out that the ngModel that has not only a list of formatters but also a list of parsers.
1. Use ng-model
to create the two-way data binding
<input type="text" ng-model="foo.bar"></input>
2. Create a directive in your angular module that will be applied to the same element and that depends on the ngModel
controller
module.directive('lowercase', function() {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, element, attr, ngModel) {
...
}
};
});
3. Within the link
method, add your custom converters to the ngModel
controller
function fromUser(text) {
return (text || '').toUpperCase();
}
function toUser(text) {
return (text || '').toLowerCase();
}
ngModel.$parsers.push(fromUser);
ngModel.$formatters.push(toUser);
4. Add your new directive to the same element that already has the ngModel
<input type="text" lowercase ng-model="foo.bar"></input>
Here's a working example that transforms text to lowercase in the input
and back to uppercase in the model
The API Documentation for the Model Controller also has a brief explanation and an overview of the other available methods.
Filtering some data with two kind of filter in AngularJs but not working
Well, based on the excellent answers from FrailWords and PeteBD, I got an idea, and now works !
The trick is in the interpolation. Reviewing the docs and with an excellent fiddle found, the solution came using $interpolate
and $eval
with a non isolated scope
.
var interpolation = $interpolate(element[0].cells[i].innerText);
element[0].cells[i].innerHTML = scope.$eval(interpolation).replace(regex, replacer);
The whole directive's code:
app.directive('highLight', ['$interpolate', function ($interpolate) {
var component = function(scope, element, attrs) {
if (!attrs.highlightClass) {
attrs.highlightClass = 'angular-highlight';
}
var replacer = function(match, item) {
return '<span class="'+attrs.highlightClass+'">'+match+'</span>';
}
var tokenize = function(keywords) {
keywords = keywords.replace(new RegExp(',$','g'), '').split(' ');
var i;
var l = keywords.length;
for (i=0;i<l;i++) {
keywords[i] = keywords[i].replace(new RegExp('^ | $','g'), '');
}
return keywords;
}
scope.$watch(attrs.highLight, function(newValue, oldValue) {
console.log("new: " + newValue + " old " + oldValue);
var tokenized = tokenize(newValue);
var regex = new RegExp(tokenized.join('|'), 'gmi');
if(newValue.length>=1 || oldValue.length>=1){
for(i=0;i<=1;i++){
var interpolation = $interpolate(element[0].cells[i].innerText);
element[0].cells[i].innerHTML = scope.$eval(interpolation).replace(regex, replacer);
}
}
});
}
return {
link: component,
replace: false
};
}]);
And the html always like this:
<tr ng-repeat="expense in Model.filteredlist | pagination: pagination.currentPage : numPerPage" x-high:light="filter.condition">
<td>{{expense.first_name}} {{expense.middle_name}}</td>
<td>{{expense.first_surname}} {{expense.second_surname}}</td>
<td>{{expense.age}}</td>
</tr>
Now everything works like a charm. Amazing, but really true.
AngularJS multiple filters
<li ng-repeat="cross in filteredItems = (items | filter:filter1 | filter:filter2 | filter:filter3)">
If you notice, I have created a new scope variable there called filteredItems
. This makes it easy to check if there are any items left after filtering. You may wish to display a message such as "No items found" if !filteredItems.length
.
E.g.
<div ng-hide="filteredItems.length" class="row">
<div class="col-xs-10 col-sm-11 col-md-11">No items found</div>
</div>
how to filter list by using two way binding input text in angularjs
Here is a working DEMO
Basically your code had a couple issues:
1) Your HTML code had some badly mismatched tags
2) You were not binding your model value on your input field to your detailCtrl
3) As a result of 1), your comments section was placed outside of the div with the ng-controller on it, so you had no access to the detailCtrl.dish
object
New Code:
HTML
<body>
<div class="container">
<div class="row row-content" ng-controller="dishDetailController as detailCtrl">
<div class="col-xs-12">
<ul class="media-list tab-pane fade in active">
<li class="media" ng-repeat="dish in detailCtrl.dish">
<h2 class="media-heading">
{{dish.name}}
<span class="lable">{{dish.lable}}</span>
<span class="badge">{{dish.price | currency}}</span>
</h2>
<p>{{dish.description}}</p>
<br>
<span class="design">Customer Comments </span>
<span class="sort">Sort by: </span>
<input type="text" ng-model="detailCtrl.search"></input>
{{detailCtrl.search}}
</li>
</ul>
</div>
<div class="col-xs-9 col-xs-ofsfset-1">
<div ng-repeat="c in detailCtrl.dish[0].comments | orderBy:detailCtrl.search">
<blockquote>
<p ng-bind="c.rating + ' stars'"></p>
<p ng-bind="c.comment"></p>
<!-- <footer>{{c.author}}, {{c.date | date: 'MMM. dd,yyyy' }}</footer> -->
<footer>{{c.author}}, {{c.date | date: mediumDate }}</footer>
</blockquote>
</div>
</div>
</div>
</div>
</body>
JS
var app = angular.module('confusionApp', []);
app.controller('dishDetailController', function() {
this.search = "rating";
this.dish = [{
name: 'Uthapizza',
image: 'images/uthapizza.png',
category: 'mains',
lable: 'Hot',
price: '4.99',
description: 'A unique combination of Indian Uthappam (pancake) and Italian pizza, topped with Cerignola olives, ripe vine cherry tomatoes, Vidalia onion, Guntur chillies and Buffalo Paneer.',
comments: [
{
rating: 5,
comment: "Imagine all the eatables, living in conFusion!",
author: "John Lemon",
date: "2012-10-16T17:57:28.556094Z"
},
{
rating: 4,
comment: "Sends anyone to heaven, I wish I could get my mother-in-law to eat it!",
author: "Paul McVites",
date: "2014-09-05T17:57:28.556094Z"
},
{
rating: 3,
comment: "Eat it, just eat it!",
author: "Michael Jaikishan",
date: "2015-02-13T17:57:28.556094Z"
},
{
rating: 4,
comment: "Ultimate, Reaching for the stars!",
author: "Ringo Starry",
date: "2013-12-02T17:57:28.556094Z"
},
{
rating: 2,
comment: "It's your birthday, we're gonna party!",
author: "25 Cent",
date: "2011-12-02T17:57:28.556094Z"
}
]
}];
});
Filtering by Multiple Specific Model Properties in AngularJS (in OR relationship)
Here is the plunker
New plunker with cleaner code & where both the query and search list items are case insensitive
Main idea is create a filter function to achieve this purpose.
From official doc
function: A predicate function can be used to write arbitrary filters.
The function is called for each element of array. The final result is
an array of those elements that the predicate returned true for.
<input ng-model="query">
<tr ng-repeat="smartphone in smartphones | filter: search ">
$scope.search = function(item) {
if (!$scope.query || (item.brand.toLowerCase().indexOf($scope.query) != -1) || (item.model.toLowerCase().indexOf($scope.query.toLowerCase()) != -1) ){
return true;
}
return false;
};
Update
Some people might have a concern on performance in real world, which is correct.
In real world, we probably would do this kinda filter from controller.
Here is the detail post showing how to do it.
in short, we add ng-change
to input for monitoring new search change
and then trigger filter function.
Angular directive with two way binding, filter and ng-repeat
Also you can pass filtered data to directive like a string:
<rockers items="{{ scopedItems | filter:{name:'Bonny'} }}"></rockers>
and parse it value to object it directive:
app.directive("rockers", function(){
return {
restrict : "E",
replace : true,
scope : {},
link:function(scope, elem, attr){
scope.items = JSON.parse(attr.items);
},
template : '<div>'+
'<span ng-repeat="item in items">{{item.name}} Rocks!</span>'+
'</div>'
};
});
http://jsfiddle.net/34ag7/4/
How to filter multiple values (OR operation) in angularJS
I would just create a custom filter. They are not that hard.
angular.module('myFilters', []).
filter('bygenre', function() {
return function(movies,genres) {
var out = [];
// Filter logic here, adding matches to the out var.
return out;
}
});
template:
<h1>Movies</h1>
<div ng-init="movies = [
{title:'Man on the Moon', genre:'action'},
{title:'Meet the Robinsons', genre:'family'},
{title:'Sphere', genre:'action'}
];" />
<input type="checkbox" ng-model="genrefilters.action" />Action
<br />
<input type="checkbox" ng-model="genrefilters.family" />Family
<br />{{genrefilters.action}}::{{genrefilters.family}}
<ul>
<li ng-repeat="movie in movies | bygenre:genrefilters">{{movie.title}}: {{movie.genre}}</li>
</ul>
Edit here is the link: Creating Angular Filters
UPDATE: Here is a fiddle that has an exact demo of my suggestion.
How to make format filter for two way binding using AngularJS
You can use scope.$watch
scope.$watch("model", function (newValue) {
scope.formattedModel = convertToWcfFormat(newValue);
});
Related Topics
JavaScript Can't Access Private Properties
Garbage Collection with Node.Js
Sort an Array of Objects Based on Another Array of Ids
How to Redefine a JavaScript Class's Method
Google Maps JavaScript API Referernotallowedmaperror
How to Flatten Nested Array in JavaScript
Extending Built-In Natives in Es6 with Babel
Access-Control-Allow-Origin Denied Spotify API
What Is the Dollar Sign in JavaScript, If Not Jquery
Why Is Immutability So Important (Or Needed) in JavaScript
Utf-8 Word Boundary Regex in JavaScript
Indirect Function Call in JavaScript
How to Suppress the Browser's Authentication Dialog
Injecting a Mock into an Angularjs Service