summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Davis <thomasalwyndavis@gmail.com>2012-04-28 13:42:57 +1000
committerThomas Davis <thomasalwyndavis@gmail.com>2012-04-28 13:42:57 +1000
commit3cda528890f9cb440a260f355d781cc4c3c8ec56 (patch)
treef62a608b189aa9550fd74bc892f7e46322ef01b4
parent663d44f98d52a8153d1259e90f009d5f0a6d4845 (diff)
downloadbackbonetutorials-3cda528890f9cb440a260f355d781cc4c3c8ec56.zip
backbonetutorials-3cda528890f9cb440a260f355d781cc4c3c8ec56.tar.gz
backbonetutorials-3cda528890f9cb440a260f355d781cc4c3c8ec56.tar.bz2
infinite scroll tutorial finished
-rw-r--r--_posts/2011-4-28-infinite-scrolling.md123
-rw-r--r--examples/infinite-scroll/css/theme.css2
-rw-r--r--examples/infinite-scroll/js/collections/twitter.js11
-rw-r--r--examples/infinite-scroll/js/router.js6
-rw-r--r--examples/infinite-scroll/js/views/twitter/widget.js47
-rw-r--r--examples/infinite-scroll/templates/layout.html4
-rw-r--r--examples/infinite-scroll/templates/twitter/list.html4
7 files changed, 181 insertions, 16 deletions
diff --git a/_posts/2011-4-28-infinite-scrolling.md b/_posts/2011-4-28-infinite-scrolling.md
index 738fd72..9dbb91d 100644
--- a/_posts/2011-4-28-infinite-scrolling.md
+++ b/_posts/2011-4-28-infinite-scrolling.md
@@ -8,9 +8,124 @@ posturl: http://backbonetutorials.com/infinite-scroll
# Lightweight Infinite Scrolling using Twitter API
-The first thing to do is require the Restify module. Restify will be in control of handling our restFul end points and returning the appropriate JSON.
+## Getting started
+In this example we are going to build a widget that pulls in tweets and when the user scrolls to the bottom of the widget Backbone.js will resync with the server to bring down the next page of results.
- var restify = require('restify');
- var server = restify.createServer();
- server.use(restify.bodyParser());
+[Example Demo](http://backbonetutorials.com/examples/infinite-scroll/)
+[Example Source](https://github.com/thomasdavis/backbonetutorials/tree/gh-pages/examples/infinite-scroll)
+
+_Note: This tutorial will use [AMD](http://backbonetutorials.com/organizing-backbone-using-modules) for modularity.
+
+## The Twitter Collection
+
+Twitter offers a jsonp API for browsing tweets. The first thing to note is that we have to append '&callback?' to allow cross domain ajax calls which is a feature of [jsonp](http://en.wikipedia.org/wiki/JSONP).
+
+Using the 'q' and 'page' query parameters we can find the results we are after. In the collection definition below we have set some defaults which can be overridden at any point.
+
+Twitter's search API actually returns a whole bunch of meta information alongside the results. Though this is a problem for Backbone.js because a Collection expects to be populated with an array of objects. So in our collection definition we can override the Backbone.js default parse function to instead choose the correct property to populate the collection.
+
+ // collections/twitter.js
+ define([
+ 'jquery',
+ 'underscore',
+ 'backbone'
+ ], function($, _, Backbone){
+ var Tweets = Backbone.Collection.extend({
+ url: function () {
+ return 'http://search.twitter.com/search.json?q=' + this.query + '&page=' + this.page + '&callback=?'
+ },
+ // Because twitter doesn't return an array of models by default we need
+ // to point Backbone.js at the correct property
+ parse: function(resp, xhr) {
+ return resp.results;
+ },
+ page: 1,
+ query: 'backbone.js tutorials'
+ });
+
+ return Tweets;
+ });
+
+
+_Note: Feel free to attach the meta information returned by Twitter to the collection itself e.g._
+
+ parse: function(resp, xhr) {
+ this.completed_in = resp.completed_in
+ return resp.results;
+ },
+
+## Setting up the View
+
+The first thing to do is load our Twitter collection and template into the widget module. We should attach our collection to our view in our `initialize` function. `loadResults` will be responsible for calling fetch on our Twitter collection. On success we will append the latest results to our widget using our template. Our Backbone.js `events` will listen for `scroll` on the current `el` of the view which is '.twitter-widget'. If the current `scrollTop` is at the bottom then we simply increment the Twitter collections current page property and call `loadResults` again.
+
+ // views/twitter/widget.js
+ define([
+ 'jquery',
+ 'underscore',
+ 'backbone',
+ 'vm',
+ 'collections/twitter',
+ 'text!templates/twitter/list.html'
+ ], function($, _, Backbone, Vm, TwitterCollection, TwitterListTemplate){
+ var TwitterWidget = Backbone.View.extend({
+ el: '.twitter-widget',
+ initialize: function () {
+ // isLoading is a useful flag to make sure we don't send off more than
+ // one request at a time
+ this.isLoading = false;
+ this.twitterCollection = new TwitterCollection();
+ },
+ render: function () {
+ this.loadResults();
+ },
+ loadResults: function () {
+ var that = this;
+ // we are starting a new load of results so set isLoading to true
+ this.isLoading = true;
+ // fetch is Backbone.js native function for calling and parsing the collection url
+ this.twitterCollection.fetch({
+ success: function (tweets) {
+ // Once the results are returned lets populate our template
+ $(that.el).append(_.template(TwitterListTemplate, {tweets: tweets.models, _:_}));
+ // Now we have finished loading set isLoading back to false
+ that.isLoading = false;
+ }
+ });
+ },
+ // This will simply listen for scroll events on the current el
+ events: {
+ 'scroll': 'checkScroll'
+ },
+ checkScroll: function () {
+ var triggerPoint = 100; // 100px from the bottom
+ if( !this.isLoading && this.el.scrollTop + this.el.clientHeight + triggerPoint > this.el.scrollHeight ) {
+ this.twitterCollection.page += 1; // Load next page
+ this.loadResults();
+ }
+ }
+ });
+ return TwitterWidget;
+ });
+
+_Note: `triggerPoint` will allow you to set an offset where the user has to scroll to before loading the next page_
+
+## The widget template
+
+Our view above passes into our underscore template the variable tweets which we can simply iterate over with using underscore's `each` method.
+
+ <!-- templates/twitter/list.html -->
+ <ul class="tweets">
+ <% _.each(tweets, function (tweet) { %>
+
+ <li><%= tweet.get('text') %></li>
+
+ <% }); %>
+ </ul>
+
+## Conclusion
+
+This is a very light weight but robust infinite scroll example. There are caveats to using infinite scroll in UI/UX so make sure to use it only when applicable.
+
+[Example Demo](http://backbonetutorials.com/examples/infinite-scroll/)
+[Example Source](https://github.com/thomasdavis/backbonetutorials/tree/gh-pages/examples/infinite-scroll)
diff --git a/examples/infinite-scroll/css/theme.css b/examples/infinite-scroll/css/theme.css
index 57f06f8..e2fb34f 100644
--- a/examples/infinite-scroll/css/theme.css
+++ b/examples/infinite-scroll/css/theme.css
@@ -5,7 +5,7 @@ body {
color: #444;
}
-.page {
+.twitter-widget {
overflow-y: scroll;
overflow-x: hidden;
height: 500px;
diff --git a/examples/infinite-scroll/js/collections/twitter.js b/examples/infinite-scroll/js/collections/twitter.js
index 6024da0..c262e85 100644
--- a/examples/infinite-scroll/js/collections/twitter.js
+++ b/examples/infinite-scroll/js/collections/twitter.js
@@ -3,15 +3,18 @@ define([
'underscore',
'backbone'
], function($, _, Backbone){
- var Messages = Backbone.Collection.extend({
+ var Tweets = Backbone.Collection.extend({
url: function () {
- return 'http://search.twitter.com/search.json?q=blue%20angels&page=' + this.page + '&callback=?'
+ return 'http://search.twitter.com/search.json?q=' + this.query + '&page=' + this.page + '&callback=?'
},
+ // Because twitter doesn't return an array of models by default we need
+ // to point Backbone.js at the correct property
parse: function(resp, xhr) {
return resp.results;
},
- page: 1
+ page: 1,
+ query: 'backbone.js tutorials'
});
- return Messages;
+ return Tweets;
});
diff --git a/examples/infinite-scroll/js/router.js b/examples/infinite-scroll/js/router.js
index 2f65042..7f39706 100644
--- a/examples/infinite-scroll/js/router.js
+++ b/examples/infinite-scroll/js/router.js
@@ -16,9 +16,9 @@ define([
var router = new AppRouter(options);
router.on('route:defaultAction', function (actions) {
- require(['views/dashboard/page'], function (DashboardPage) {
- var dashboardPage = Vm.create(appView, 'DashboardPage', DashboardPage);
- dashboardPage.render();
+ require(['views/twitter/widget'], function (TwitterWidget) {
+ var twitterWidget = Vm.create(appView, 'TwitterWidget', TwitterWidget);
+ twitterWidget.render();
});
});
diff --git a/examples/infinite-scroll/js/views/twitter/widget.js b/examples/infinite-scroll/js/views/twitter/widget.js
new file mode 100644
index 0000000..3d61f20
--- /dev/null
+++ b/examples/infinite-scroll/js/views/twitter/widget.js
@@ -0,0 +1,47 @@
+define([
+ 'jquery',
+ 'underscore',
+ 'backbone',
+ 'vm',
+ 'collections/twitter',
+ 'text!templates/twitter/list.html'
+], function($, _, Backbone, Vm, TwitterCollection, TwitterListTemplate){
+ var TwitterWidget = Backbone.View.extend({
+ el: '.twitter-widget',
+ initialize: function () {
+ // isLoading is a useful flag to make sure we don't send off more than
+ // one request at a time
+ this.isLoading = false;
+ this.twitterCollection = new TwitterCollection();
+ },
+ render: function () {
+ this.loadResults();
+ },
+ loadResults: function () {
+ var that = this;
+ // we are starting a new load of results so set isLoading to true
+ this.isLoading = true;
+ // fetch is Backbone.js native function for calling and parsing the collection url
+ this.twitterCollection.fetch({
+ success: function (tweets) {
+ // Once the results are returned lets populate our template
+ $(that.el).append(_.template(TwitterListTemplate, {tweets: tweets.models, _:_}));
+ // Now we have finished loading set isLoading back to false
+ that.isLoading = false;
+ }
+ });
+ },
+ // This will simply listen for scroll events on the current el
+ events: {
+ 'scroll': 'checkScroll'
+ },
+ checkScroll: function () {
+ var triggerPoint = 100; // 100px from the bottom
+ if( !this.isLoading && this.el.scrollTop + this.el.clientHeight + triggerPoint > this.el.scrollHeight ) {
+ this.twitterCollection.page += 1; // Load next page
+ this.loadResults();
+ }
+ }
+ });
+ return TwitterWidget;
+});
diff --git a/examples/infinite-scroll/templates/layout.html b/examples/infinite-scroll/templates/layout.html
index 1a975ad..5a89b7d 100644
--- a/examples/infinite-scroll/templates/layout.html
+++ b/examples/infinite-scroll/templates/layout.html
@@ -3,6 +3,8 @@
<p>This is a super light weight infinite scroll using twitters search api. Scrolling 100px from the bottom will cause the Twitter Collection to set it's current page + 1 and then call a fetch constructing a new url to the twitter search api.</p>
</div>
<div style="clear: both;"></div>
-<div class="page">Loading</div>
+<div class="page">
+ <div class="twitter-widget"></div>
+</div>
<div style="clear: both;"></div>
<div class="footer"></div>
diff --git a/examples/infinite-scroll/templates/twitter/list.html b/examples/infinite-scroll/templates/twitter/list.html
index bd2d72c..39de6b1 100644
--- a/examples/infinite-scroll/templates/twitter/list.html
+++ b/examples/infinite-scroll/templates/twitter/list.html
@@ -1,7 +1,5 @@
<ul class="tweets">
-<% _.each(tweets, function (tweet) {
- console.log(tweet);
- %>
+<% _.each(tweets, function (tweet) { %>
<li><%= tweet.get('text') %></li>