diff --git a/README.md b/README.md
index 7e1c63b..aca1295 100644
--- a/README.md
+++ b/README.md
@@ -239,6 +239,21 @@ A service you can inject in your controller to show the filter bar
The filter object used to filter the items array. The default value is $filter('filter'), however you can also
pass in a custom filter.
+ - `{function=}` `search`
+
+ A custom search functionthat returns a filteredItems array. Return value of this function could be promise, which
+ is useful for backend API filtering with the text entered in the filter bar.
+ function (filterText){
+ var deferred = $q.defer();
+ $timeout(function(){
+ // imagine items are return value from you api
+ var result = items.filter(function(item, index){
+ return item.text.indexOf(filterText) != -1;
+ })
+ deferred.resolve(result);
+ }, 500);
+ }
+
- `{function=}` `expression`
The predicate to be used for selecting items from the `items` array. This is similar to the angular filter
diff --git a/demo/app/scripts/app.js b/demo/app/scripts/app.js
index 182fd43..5d50a2b 100644
--- a/demo/app/scripts/app.js
+++ b/demo/app/scripts/app.js
@@ -42,7 +42,7 @@ angular.module('Demo', ['ionic', 'jett.ionic.filter.bar'])
});
})
- .controller('MainController', function($scope, $timeout, $ionicFilterBar) {
+ .controller('MainController', function($scope, $timeout, $q, $ionicFilterBar) {
var filterBarInstance;
@@ -59,6 +59,16 @@ angular.module('Demo', ['ionic', 'jett.ionic.filter.bar'])
$scope.showFilterBar = function () {
filterBarInstance = $ionicFilterBar.show({
items: $scope.items,
+ search: function(filterText){
+ var deferred = $q.defer();
+ $timeout(function(){
+ var result = $scope.items.filter(function(item, index){
+ return item.text.indexOf(filterText) != -1;
+ })
+ deferred.resolve(result);
+ }, 500);
+ return deferred.promise;
+ },
update: function (filteredItems, filterText) {
$scope.items = filteredItems;
if (filterText) {
diff --git a/dist/ionic.filter.bar.js b/dist/ionic.filter.bar.js
index b7e9e87..2f07dba 100644
--- a/dist/ionic.filter.bar.js
+++ b/dist/ionic.filter.bar.js
@@ -365,12 +365,13 @@ angular.module('jett.ionic.filter.bar', ['ionic']);
'$compile',
'$timeout',
'$filter',
+ '$q',
'$ionicPlatform',
'$ionicFilterBarConfig',
'$ionicConfig',
'$ionicModal',
'$ionicScrollDelegate',
- function ($document, $rootScope, $compile, $timeout, $filter, $ionicPlatform, $ionicFilterBarConfig, $ionicConfig, $ionicModal, $ionicScrollDelegate) {
+ function ($document, $rootScope, $compile, $timeout, $filter, $q, $ionicPlatform, $ionicFilterBarConfig, $ionicConfig, $ionicModal, $ionicScrollDelegate) {
var isShown = false;
var $body = $document[0].body;
var templateConfig = {
@@ -435,6 +436,7 @@ angular.module('jett.ionic.filter.bar', ['ionic']);
cancelText: 'Cancel',
cancelOnStateChange: true,
container: $body,
+ search: null,
favoritesTitle: 'Favorite Searches',
favoritesAddPlaceholder: 'Add a search term',
favoritesEnabled: false,
@@ -534,9 +536,11 @@ angular.module('jett.ionic.filter.bar', ['ionic']);
var filterExp, filteredItems;
// pass back original list if filterText is empty.
- // Otherwise filter by expression, supplied properties, or filterText.
+ // Otherwise perform a search or filter by expression, supplied properties, or filterText.
if (!filterText.length) {
filteredItems = scope.items;
+ } else if (angular.isFunction(scope.search)) {
+ filteredItems = scope.search(filterText);
} else {
if (scope.expression) {
filterExp = angular.bind(this, scope.expression, filterText);
@@ -554,11 +558,14 @@ angular.module('jett.ionic.filter.bar', ['ionic']);
filteredItems = scope.filter(scope.items, filterExp, scope.comparator);
}
-
- $timeout(function() {
- scope.update(filteredItems, filterText);
- scope.scrollItemsTop();
- });
+ // turn filteredItems into a promise for consistent handling logic
+ $q.when(filteredItems).then(function(items){
+ $timeout(function() {
+ scope.update(items, filterText);
+ scope.scrollItemsTop();
+ });
+ })
+
};
// registerBackButtonAction returns a callback to deregister the action
@@ -657,6 +664,9 @@ angular.module('jett.ionic.filter.bar', ['ionic']);
})(angular, ionic);
+
+
+
/* global angular */
(function (angular) {
'use strict';
diff --git a/dist/ionic.filter.bar.min.js b/dist/ionic.filter.bar.min.js
index a49c5a5..2d068aa 100644
--- a/dist/ionic.filter.bar.min.js
+++ b/dist/ionic.filter.bar.min.js
@@ -1 +1 @@
-angular.module("jett.ionic.filter.bar",["ionic"]),function(e,t){"use strict";e.module("jett.ionic.filter.bar").directive("ionFilterBar",["$timeout","$ionicGesture","$ionicPlatform",function(o,r,n){var i;return i=n.is("android")?'
':'',{restrict:"E",scope:!0,link:function(n,i){var a,l,c,s,d,f=i[0],u=f.querySelector(".filter-bar-clear"),m=f.querySelector(".filter-bar-cancel"),p=f.querySelector(".filter-bar-search"),h=function(){n.cancelFilterBar()};n.config.backdrop&&(c=e.element(''),i.append(c),s=function(e){e.target==c[0]&&h()},c.bind("click",s),l=r.on("swipe",s,c)),n.favoritesEnabled?n.getClearButtonClass=function(){return n.data.filterText.length?n.config.clear:n.config.favorite}:n.getClearButtonClass=function(){return n.data.filterText.length?n.config.clear:"filter-bar-element-hide"};var b=function(){u.classList.contains(n.config.favorite)?n.showModal():o(function(){n.data.filterText="",ionic.requestAnimationFrame(function(){n.showBackdrop(),n.scrollItemsTop(),n.focusInput()})})},g=function(){n.scrollItemsTop(),n.focusInput()},v=function(e){27==e.which?h():n.data.filterText&&n.data.filterText.length?n.hideBackdrop():n.showBackdrop()};m.addEventListener("click",h),u.addEventListener("touchstart",b),u.addEventListener("mousedown",b),p.addEventListener("touchstart",g),p.addEventListener("mousedown",g),t.addEventListener("keyup",v);var $=function(){n.filterItems(n.data.filterText)};n.$on("$destroy",function(){i.remove(),t.removeEventListener("keyup",v),c&&r.off(l,"swipe",s),d()}),d=n.$watch("data.filterText",function(e,t){var r;a&&o.cancel(a),e!==t&&(r=e.length&&n.debounce?n.delay:0,a=o($,r,!1))})},template:i}}])}(angular,document),function(e){"use strict";e.module("jett.ionic.filter.bar").provider("$ionicFilterBarConfig",function(){function t(e,t){l.platform[e]=t,i.platform[e]={},o(l,l.platform[e]),r(l.platform[e],i.platform[e],"")}function o(t,r){for(var n in t)n!=a&&t.hasOwnProperty(n)&&(e.isObject(t[n])?(e.isDefined(r[n])||(r[n]={}),o(t[n],r[n])):e.isDefined(r[n])||(r[n]=null))}function r(t,o,i){e.forEach(t,function(c,s){e.isObject(t[s])?(o[s]={},r(t[s],o[s],i+"."+s)):o[s]=function(e){if(arguments.length)return t[s]=e,o;if(t[s]==a){var r=n(l.platform,ionic.Platform.platform()+i+"."+s);return r||r===!1?r:n(l.platform,"default"+i+"."+s)}return t[s]}})}function n(t,o){o=o.split(".");for(var r=0;r')(d),$=v.children().eq(0),B=$.find("input")[0],w=v.children().eq(1),k=d.scrollDelegate.getScrollView(),y=!!k,x=y?k.__container:null,I=d.cancelOnStateChange?i.$on("$stateChangeSuccess",function(){d.cancelFilterBar()}):e.noop,T=function(){p||(p=!0,B&&B.focus())},C=function(){p&&(p=!1,B&&B.blur())},F=function(){k.__scrollTop>0&&C()};return d.scrollItemsTop=function(){y&&k.__scrollTop>0&&d.scrollDelegate.scrollTop&&d.scrollDelegate.scrollTop()},d.focusInput=function(){p=!1,T()},d.hideBackdrop=function(){w.length&&f&&(f=!1,w.removeClass("active").css("display","none"))},d.showBackdrop=function(){w.length&&!f&&(f=!0,w.css("display","block").addClass("active"))},d.showModal=function(){d.modal=u.fromTemplate(o,{scope:d}),d.modal.show()},d.filterItems=function(t){var o,r;t.length?(d.expression?o=e.bind(this,d.expression,t):e.isArray(d.filterProperties)?(o={},e.forEach(d.filterProperties,function(e){o[e]=t})):d.filterProperties?(o={},o[d.filterProperties]=t):o=t,r=d.filter(d.items,o,d.comparator)):r=d.items,l(function(){d.update(r,t),d.scrollItemsTop()})},d.$deregisterBackButton=s.registerBackButtonAction(function(){l(d.cancelFilterBar)},300),d.removeFilterBar=function(o){d.removed||(d.removed=!0,t.requestAnimationFrame(function(){$.removeClass("filter-bar-in"),C(),d.hideBackdrop(),l(function(){d.scrollItemsTop(),d.update(d.items),d.$destroy(),v.remove(),d.cancelFilterBar.$scope=d.modal=x=k=$=w=B=null,h=!1,(o||e.noop)()},350)}),l(function(){d.container.classList.remove("filter-bar-open")},400),d.$deregisterBackButton(),I(),x&&x.removeEventListener("scroll",F))},d.showFilterBar=function(o){d.removed||(d.container.appendChild(v[0]),d.container.classList.add("filter-bar-open"),d.scrollItemsTop(),t.requestAnimationFrame(function(){d.removed||l(function(){$.addClass("filter-bar-in"),d.focusInput(),d.showBackdrop(),(o||e.noop)()},20,!1)}),x&&x.addEventListener("scroll",F))},d.cancelFilterBar=function(){d.removeFilterBar(d.cancel)},d.showFilterBar(d.done),d.cancelFilterBar.$scope=d,d.cancelFilterBar}}var h=!1,b=n[0].body,g={theme:d.theme(),transition:d.transition(),back:f.backButton.icon(),clear:d.clear(),favorite:d.favorite(),search:d.search(),backdrop:d.backdrop(),placeholder:d.placeholder(),close:d.close(),done:d.done(),reorder:d.reorder(),remove:d.remove(),add:d.add()};return{show:p}}])}(angular,ionic),function(e){"use strict";e.module("jett.ionic.filter.bar").controller("$ionicFilterBarModalCtrl",["$window","$scope","$timeout","$ionicListDelegate",function(t,o,r,n){var i=o.$parent.favoritesKey;o.displayData={showReorder:!1},o.searches=e.fromJson(t.localStorage.getItem(i))||[],o.newItem={text:""},o.moveItem=function(e,t,n){e.reordered=!0,o.searches.splice(t,1),o.searches.splice(n,0,e),r(function(){delete e.reordered},500)},o.deleteItem=function(e){var t=o.searches.indexOf(e);o.searches.splice(t,1)},o.addItem=function(){o.newItem.text&&(o.searches.push({text:o.newItem.text}),o.newItem.text="")},o.closeModal=function(){t.localStorage.setItem(i,e.toJson(o.searches)),o.$parent.modal.remove()},o.itemClicked=function(e,t){var r=!!t.currentTarget.querySelector(".item-options.invisible");r?(o.closeModal(),o.$parent.hideBackdrop(),o.$parent.data.filterText=e,o.$parent.filterItems(e)):n.$getByHandle("searches-list").closeOptionButtons()}}])}(angular);
\ No newline at end of file
+angular.module("jett.ionic.filter.bar",["ionic"]),function(e,t){"use strict";e.module("jett.ionic.filter.bar").directive("ionFilterBar",["$timeout","$ionicGesture","$ionicPlatform",function(o,r,n){var i;return i=n.is("android")?'':'',{restrict:"E",scope:!0,link:function(n,i){var a,l,c,s,d,f=i[0],u=f.querySelector(".filter-bar-clear"),m=f.querySelector(".filter-bar-cancel"),p=f.querySelector(".filter-bar-search"),h=function(){n.cancelFilterBar()};n.config.backdrop&&(c=e.element(''),i.append(c),s=function(e){e.target==c[0]&&h()},c.bind("click",s),l=r.on("swipe",s,c)),n.favoritesEnabled?n.getClearButtonClass=function(){return n.data.filterText.length?n.config.clear:n.config.favorite}:n.getClearButtonClass=function(){return n.data.filterText.length?n.config.clear:"filter-bar-element-hide"};var b=function(){u.classList.contains(n.config.favorite)?n.showModal():o(function(){n.data.filterText="",ionic.requestAnimationFrame(function(){n.showBackdrop(),n.scrollItemsTop(),n.focusInput()})})},g=function(){n.scrollItemsTop(),n.focusInput()},v=function(e){27==e.which?h():n.data.filterText&&n.data.filterText.length?n.hideBackdrop():n.showBackdrop()};m.addEventListener("click",h),u.addEventListener("touchstart",b),u.addEventListener("mousedown",b),p.addEventListener("touchstart",g),p.addEventListener("mousedown",g),t.addEventListener("keyup",v);var $=function(){n.filterItems(n.data.filterText)};n.$on("$destroy",function(){i.remove(),t.removeEventListener("keyup",v),c&&r.off(l,"swipe",s),d()}),d=n.$watch("data.filterText",function(e,t){var r;a&&o.cancel(a),e!==t&&(r=e.length&&n.debounce?n.delay:0,a=o($,r,!1))})},template:i}}])}(angular,document),function(e){"use strict";e.module("jett.ionic.filter.bar").provider("$ionicFilterBarConfig",function(){function t(e,t){l.platform[e]=t,i.platform[e]={},o(l,l.platform[e]),r(l.platform[e],i.platform[e],"")}function o(t,r){for(var n in t)n!=a&&t.hasOwnProperty(n)&&(e.isObject(t[n])?(e.isDefined(r[n])||(r[n]={}),o(t[n],r[n])):e.isDefined(r[n])||(r[n]=null))}function r(t,o,i){e.forEach(t,function(c,s){e.isObject(t[s])?(o[s]={},r(t[s],o[s],i+"."+s)):o[s]=function(e){if(arguments.length)return t[s]=e,o;if(t[s]==a){var r=n(l.platform,ionic.Platform.platform()+i+"."+s);return r||r===!1?r:n(l.platform,"default"+i+"."+s)}return t[s]}})}function n(t,o){o=o.split(".");for(var r=0;r')(f),w=$.children().eq(0),B=w.find("input")[0],k=$.children().eq(1),y=f.scrollDelegate.getScrollView(),x=!!y,I=x?y.__container:null,T=f.cancelOnStateChange?i.$on("$stateChangeSuccess",function(){f.cancelFilterBar()}):e.noop,C=function(){h||(h=!0,B&&B.focus())},F=function(){h&&(h=!1,B&&B.blur())},S=function(){y.__scrollTop>0&&F()};return f.scrollItemsTop=function(){x&&y.__scrollTop>0&&f.scrollDelegate.scrollTop&&f.scrollDelegate.scrollTop()},f.focusInput=function(){h=!1,C()},f.hideBackdrop=function(){k.length&&u&&(u=!1,k.removeClass("active").css("display","none"))},f.showBackdrop=function(){k.length&&!u&&(u=!0,k.css("display","block").addClass("active"))},f.showModal=function(){f.modal=m.fromTemplate(o,{scope:f}),f.modal.show()},f.filterItems=function(t){var o,r;t.length?e.isFunction(f.search)?r=f.search(t):(f.expression?o=e.bind(this,f.expression,t):e.isArray(f.filterProperties)?(o={},e.forEach(f.filterProperties,function(e){o[e]=t})):f.filterProperties?(o={},o[f.filterProperties]=t):o=t,r=f.filter(f.items,o,f.comparator)):r=f.items,s.when(r).then(function(e){l(function(){f.update(e,t),f.scrollItemsTop()})})},f.$deregisterBackButton=d.registerBackButtonAction(function(){l(f.cancelFilterBar)},300),f.removeFilterBar=function(o){f.removed||(f.removed=!0,t.requestAnimationFrame(function(){w.removeClass("filter-bar-in"),F(),f.hideBackdrop(),l(function(){f.scrollItemsTop(),f.update(f.items),f.$destroy(),$.remove(),f.cancelFilterBar.$scope=f.modal=I=y=w=k=B=null,b=!1,(o||e.noop)()},350)}),l(function(){f.container.classList.remove("filter-bar-open")},400),f.$deregisterBackButton(),T(),I&&I.removeEventListener("scroll",S))},f.showFilterBar=function(o){f.removed||(f.container.appendChild($[0]),f.container.classList.add("filter-bar-open"),f.scrollItemsTop(),t.requestAnimationFrame(function(){f.removed||l(function(){w.addClass("filter-bar-in"),f.focusInput(),f.showBackdrop(),(o||e.noop)()},20,!1)}),I&&I.addEventListener("scroll",S))},f.cancelFilterBar=function(){f.removeFilterBar(f.cancel)},f.showFilterBar(f.done),f.cancelFilterBar.$scope=f,f.cancelFilterBar}}var b=!1,g=n[0].body,v={theme:f.theme(),transition:f.transition(),back:u.backButton.icon(),clear:f.clear(),favorite:f.favorite(),search:f.search(),backdrop:f.backdrop(),placeholder:f.placeholder(),close:f.close(),done:f.done(),reorder:f.reorder(),remove:f.remove(),add:f.add()};return{show:h}}])}(angular,ionic),function(e){"use strict";e.module("jett.ionic.filter.bar").controller("$ionicFilterBarModalCtrl",["$window","$scope","$timeout","$ionicListDelegate",function(t,o,r,n){var i=o.$parent.favoritesKey;o.displayData={showReorder:!1},o.searches=e.fromJson(t.localStorage.getItem(i))||[],o.newItem={text:""},o.moveItem=function(e,t,n){e.reordered=!0,o.searches.splice(t,1),o.searches.splice(n,0,e),r(function(){delete e.reordered},500)},o.deleteItem=function(e){var t=o.searches.indexOf(e);o.searches.splice(t,1)},o.addItem=function(){o.newItem.text&&(o.searches.push({text:o.newItem.text}),o.newItem.text="")},o.closeModal=function(){t.localStorage.setItem(i,e.toJson(o.searches)),o.$parent.modal.remove()},o.itemClicked=function(e,t){var r=!!t.currentTarget.querySelector(".item-options.invisible");r?(o.closeModal(),o.$parent.hideBackdrop(),o.$parent.data.filterText=e,o.$parent.filterItems(e)):n.$getByHandle("searches-list").closeOptionButtons()}}])}(angular);
\ No newline at end of file
diff --git a/js/ionic.filter.bar.service.js b/js/ionic.filter.bar.service.js
index aca79a1..d6b1f17 100644
--- a/js/ionic.filter.bar.service.js
+++ b/js/ionic.filter.bar.service.js
@@ -52,12 +52,13 @@
'$compile',
'$timeout',
'$filter',
+ '$q',
'$ionicPlatform',
'$ionicFilterBarConfig',
'$ionicConfig',
'$ionicModal',
'$ionicScrollDelegate',
- function ($document, $rootScope, $compile, $timeout, $filter, $ionicPlatform, $ionicFilterBarConfig, $ionicConfig, $ionicModal, $ionicScrollDelegate) {
+ function ($document, $rootScope, $compile, $timeout, $filter, $q, $ionicPlatform, $ionicFilterBarConfig, $ionicConfig, $ionicModal, $ionicScrollDelegate) {
var isShown = false;
var $body = $document[0].body;
var templateConfig = {
@@ -122,6 +123,7 @@
cancelText: 'Cancel',
cancelOnStateChange: true,
container: $body,
+ search: null,
favoritesTitle: 'Favorite Searches',
favoritesAddPlaceholder: 'Add a search term',
favoritesEnabled: false,
@@ -221,9 +223,11 @@
var filterExp, filteredItems;
// pass back original list if filterText is empty.
- // Otherwise filter by expression, supplied properties, or filterText.
+ // Otherwise perform a search or filter by expression, supplied properties, or filterText.
if (!filterText.length) {
filteredItems = scope.items;
+ } else if (angular.isFunction(scope.search)) {
+ filteredItems = scope.search(filterText);
} else {
if (scope.expression) {
filterExp = angular.bind(this, scope.expression, filterText);
@@ -241,11 +245,14 @@
filteredItems = scope.filter(scope.items, filterExp, scope.comparator);
}
-
- $timeout(function() {
- scope.update(filteredItems, filterText);
- scope.scrollItemsTop();
- });
+ // turn filteredItems into a promise for consistent handling logic
+ $q.when(filteredItems).then(function(items){
+ $timeout(function() {
+ scope.update(items, filterText);
+ scope.scrollItemsTop();
+ });
+ })
+
};
// registerBackButtonAction returns a callback to deregister the action
@@ -343,3 +350,6 @@
})(angular, ionic);
+
+
+