Skip to content

Instantly share code, notes, and snippets.

@bguiz
Last active August 29, 2015 13:56
Show Gist options
  • Save bguiz/9338943 to your computer and use it in GitHub Desktop.
Save bguiz/9338943 to your computer and use it in GitHub Desktop.

Throttling filters in Ember.Controller

Suppose you have an array of complex data that you wish you filter. The property to filter upon is bound to the value of a text input field.

By default, this means that every keystroke will result in the filter being computed and (that particular section of) the template re-rendered.

Throttle it!

However, let's say we do not want this to happen, as we deem it to be too expensive. _.throttle combined with Ember observes using an property on a controller as an intermediary, makes this possible.

<!DOCTYPE html>
<html>
<head>
<meta name="description" content="[simple ember app that displays a list of foos with filtering]" />
<meta name="description-sub" content="[filtering using .obsertves and _.throttle]" />
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/handlebars.js/1.0.0/handlebars.js"></script>
<script src="http://builds.emberjs.com.s3.amazonaws.com/tags/v1.3.2/ember.js"></script>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body>
<script type='text/x-handlebars' data-template-name='application'>
{{#link-to 'foo'}}Go to Foo{{/link-to}}
<div>{{outlet}}</div>
</script>
<script type='text/x-handlebars' data-template-name='foo'>
<div>
{{input type='text' value=filterText}}
</div>
{{#each foo in displayFoos}}
<p>Type: {{foo.type}} Name: {{foo.name}}</p>
{{else}}
<p>There appears to be no Foos on display</p>
{{/each}}
</script>
</body>
</html>
var App = Ember.Application.create({});
App.Router.map(function() {
this.route('foo');
});
App.IndexRoute = Ember.Route.extend({
redirect: function() {
this.transitionTo('foo');
}
});
// _.throttle from http://underscorejs.org
// (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.
// Underscore may be freely distributed under the MIT license.
// https://github.com/documentcloud/underscore/blob/master/underscore.js#L626
// Returns a function, that, when invoked, will only be triggered at most once
// during a given window of time.
var _throttle = function _throttle(func, wait) {
var context, args, timeout, result;
var previous = 0;
var later = function() {
previous = new Date();
timeout = null;
result = func.apply(context, args);
};
return function() {
var now = new Date();
var remaining = wait - (now - previous);
context = this;
args = arguments;
if (remaining <= 0) {
clearTimeout(timeout);
timeout = null;
previous = now;
result = func.apply(context, args);
} else if (!timeout) {
timeout = setTimeout(later, remaining);
}
return result;
};
};
var FILTER_THROTTLE_MS = 5000; //set to a high number to make obvious in demo, adjust according to need!
App.FooController = Ember.Controller.extend({
filterText: '',
filterFoos: _throttle(function() {
//uses _.throttle in combination with observes, instead of property,
//in order to avoid expensive recomputation too frequently
//See: http://stackoverflow.com/questions/18228364/computed-property-not-firing
//and: http://briantford.com/blog/huuuuuge-angular-apps.html
var str = this.get('filterText');
var arr = this.get('model');
if (typeof str !== 'string' || str.length === 0) {
this.set('filteredFoos', arr);
}
else {
var out = Em.A();
arr.forEach(function(foo) {
if (foo.type === str) {
out.push(foo);
}
});
this.set('filteredFoos', out);
}
}, FILTER_THROTTLE_MS).observes('model', 'filterText'),
displayFoos: function() {
var foos = this.get('filteredFoos');
// do some processing here
return foos;
}.property('filteredFoos')
});
App.FooRoute = Ember.Route.extend({
setupController: function(controller, model) {
controller.set('model', [
{type: 'a', name: 'foo1'},
{type: 'a', name: 'bar1'},
{type: 'b', name: 'baz1'},
{type: 'b', name: 'goo'},
{type: 'a', name: 'gar'},
{type: 'b', name: 'gaz'},
{type: 'a', name: 'moo'},
{type: 'b', name: 'maz'}
]);
console.log(controller.get('model'));
controller.set('filteredFoos', controller.get('model'));
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment