AngularJS: Iterators and Filters
by
at 2013-06-13 07:00:00
original http://feedproxy.google.com/~r/dailyjs/~3/9A6Te4aeg8Y/angularjs-8
- Part 1: Google, Twitter, and AngularJS
- Part 2: Let's Make a Feed Reader
- Part 3: Rendering Feeds
- Part 4: Managing Feeds
- Part 5: Tests
- Part 6: Adding Dependencies
- Part 7: Form Validation
- Part 8: Iterators and Data
AngularJS has a rich expression-based system for filtering and ordering data based on predicates. The orderBy filter can be used with the ng-repeat
directive:
<ul>
<li ng-repeat="item in stories | orderBy:predicate:date"><a href=""></a></li>
</ul>
Today we’re going to use orderBy
inside a controller using dependency injection to organise multiple feeds into a river of news sorted by date.
Iterating in Controllers
Before sorting and displaying stories, we need to collect them into a suitable data structure. An array will suffice (app/scripts/controllers/main.js
):
$scope.stories = [];
Next we need to append stories to this collection, but only if they haven’t already been added. Let’s use a function to encapsulate that away from fetching stories:
$scope.fetchFeed = function(feed) {
feed.items = [];
var apiUrl = "http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20xml%20where%20url%3D'";
apiUrl += encodeURIComponent(feed.url);
apiUrl += "'%20and%20itemPath%3D'feed.entry'&format=json&diagnostics=true&callback=JSON_CALLBACK";
$http.jsonp(apiUrl).
success(function(data) {
if (data.query.results) {
feed.items = data.query.results.entry;
}
addStories(feed.items);
The addStories
function just needs to loop over each feed item to determine if it’s already been added to $scope.stories
. The angular.forEach API in module ng
is the perfect way to do this:
function addStories(stories) {
var changed = false;
angular.forEach(stories, function(story, key) {
if (!storyInCollection(story)) {
$scope.stories.push(story);
changed = true;
}
});
}
As you can see, forEach
accepts an array and a function to call for each item. The storyInCollection
function now needs to loop over each existing story to see if it’s already been added. Figuring out which story is unique is easy because feeds have an id
value:
function storyInCollection(story) {
for (var i = 0; i < $scope.stories.length; i++) {
if ($scope.stories[i].id === story.id) {
return true;
}
}
return false;
}
Storing Data
Now we’ve got a list of stories in our scope, we need to sort them by date just like a real feed reader. Whenever addStories
changes the list of stories we should sort it. AngularJS doesn’t really have any fancy functional methods like map
or some
, which you can find in ECMAScript 5 anyway, but it does provide API access to the filtering and sorting modules that are typically used in templates.
To access this functionality you’ll need to load $filter
:
angular.module('djsreaderApp')
.controller('MainCtrl', function($scope, $http, $timeout, $filter) {
$filter
will return a function that knows how to sort or filter arrays. That means you need to call it with the name of the desired method, then call the value returned with an array and an expression: $filter(filter)(array, expression)
. To add sorting to our feeds, call $filter()()
and update the $scope.stories
array:
// At the end of addStories
if (changed) {
$scope.stories = $filter('orderBy')($scope.stories, 'date');
}
The only thing left to do is update the template in app/views/mail.html
:
<ul>
<li ng-repeat="item in stories"><a href=""></a></li>
</ul>
If you add multiple feeds using the app’s web interface you should see them combined into a river of news.
Conclusion
You can find this code in commit ff4d6a6.