How to Do Two-Way Filtering in Angularjs

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



Leave a reply



Submit