Build Facebook style infinite scroll with knockout.js and Last.fm API - StrathWeb

Strath

Build Facebook style infinite scroll with knockout.js and Last.fm API

Because with knockout.js, you shall fear UI no more

I’m convinced that most of us web developers often have to struggle with some sophisticated Javascript driven UI that tend to get out hand with their complexity, dependencies and relationships.

With that in mind, I wanted to show you an example of a knockout.js code in action.

Since knockout.js is one of the most amazing and innovative pieces of front-end code I have seen in recent years, I hope this is going to help you a bit in your everday battles. In conjuction with Last.FM API, we are going to create an infinitely scrollable history of your music records – just like the infinite scroll used on Facebook or on Twitter.

More after the jump.

What is it all about?

So what exatcly will we be trying to achieve here?

  • – display all the tracks that a user scrobbled to last.fm
  • – load them in batches – load next batch automagically as soon as user scrolls to the bottom of the page
  • – provide a possibility to change user data

ViewModel – the heart and soul

Alright, so let’s go straight to business. I will not go into the very basics of knockout.js, as the basics are very well described on the knockout.js site. I’ll just say that knockout.js provides the possibility to set observables and observableArrays on the viewModel. And if you are still feeling confused, well, the best way to learn is to follow the example.

Our model will look like this:

Let’s spend a minute and look at it. The tracks property is going to hold the tracks retrieved from last.fm. totalTracks is going to hold the value for all tracks that the user has ever listened to. This is going to be useful to tell the page to stop trying to load new tracks when we reach the end of available data. Page property is going to be used for indicating on which page (or batch) of the last.fm results we are currently on. Each new page will be corresponding to a new $.ajax (XHR) call. username is rather self explanatory (I hope).

Now that we have tha basic viewModel planned, we need to extend it with a changing the user functionality. But before that, let’s think of how we might read info from Last.fm using its API.

Last.fm API giveth and not taketh away

The Last.fm API is documented here. It is a fantastic resource, with a wide variety of methods. In this sample we’ll use just one – user.getrecenttracks, which contrary to what the name suggests, allows us to retrieve all the tracks user has ever listened to – from the latest to the oldest one.

Last.fm API is available in both JSON and XML format. You’ll need a free API key to communicate with the API, but in this tutorial we will get a public key (stolen from last.fm API documentation ;-) .
We’ll use the function below to read data from the API.

As you see, the request is rather simple, we pass the methodname (in our case it’s always going to be “user.getrecentracks”), username, api_key, page (since as I already mentioned, results returned by last.fm are paged, or batched) and a limit (amount of tracks within a page).

The majority of interesting things happen in the processing of the response. First of all, we set the viewModel’s totalTracks and page properties based on the data returned. Then we iterate through the list of tracks, and push the last.fm track object into the observableArray – viewModel.tracks. At this point in time you can probably imagine that these observables are going to do some heaving lifting for us. Also, notice that we add tracks by using push() method, which means we keep adding tracks to the already loaded and shown ones.

OK, now that we have defined the function to be used to call last.fm API, we need to extend the viewModel with the aforementioned support for switching user.

The method reads the user value from a textbox, clears the existing tracks collection, and starts loading data for the new user. The reason why we declare it as viewModel method, rather then a standalone function is that we will use knockout.js to bind it to click event.

Infinite scroll is not infinite headache

Now let’s implement the final piece of our puzzle, the infinite scroll. As complex as it sounds, it’s actually very simple.

We bind an anoymous function to the scroll event of the window object, and if we reach the bottom of the page (calculated based on scrollTop http://api.jquery.com/scrollTop/), we check whether we have reached the end of users tracks yet (expressed by page number * page size – in our case 20 – and compared to total tracks on user’s last.fm profile). If there is still stuff to load, we call our last.fm retrieval method, this time pasing page+1 as an argument, since we are interested in the next batch.

Where the heck is the View?

Now pretty much all the JS is in place, except for binding to the View and the initial population of data, but that in a minute. For now let’s shift our attention to the View. So we have this nice viewModel ready to be bound to the UI, which we have completely disregarded up to this point. Well, actually turns out this is going to be super easy to do. The way knockout.js is built, once you have the viewModel, the view can be set up (almost) in a heartbeat.
Let’s start with including all the necessary libraries:

Our main data (tracks) will be included in a div, and we’ll loop through the viewModel.tracks to render all available tracks.

The HTML5 data-bind attribute, is a way for us to tell knockout.js “hey there is something for you to look at”. In this case we are saying “loop through all of viewModel.tracks objects, and render each in the context of trackTemplate”. But where is that template? Here:

If you are familiar with jQuery templates, there is nothing surprising here, all properties are determined by the individual track object returned by last.fm API. There is not much to explain here, except for maybe mentioning that some of the properties returned by last.fm are named with “#” at the beginning, and thus are not accessible using the normal “dot”syntax (object.property), so we have to use the brackets (or magic strings) syntax (object["property"]) instead.

New user and some side effects

Now let’s just a add a little header with username, and a textbox to enter new username.

We bind a click event to our reloadUser method of viewModel (as explained later). We also added two bindings for showing how many tracks have been loaded so far (by doing a count on the viewModel.tracks observableArray) and how many tracks the user has in total.

Putting it all together and hoping it works

So the view is pretty much ready – let’s bind the two together.

We tell knockout.js to apply the bindings by calling, well, surprisingly, applyBindings method and passing the viewModel to it. We also call the method to get tracks from last.fm once, to populate the page on 1st load.

By that time the page should look somehow like this:

It works but it’s nothing exciting. Well, at this point it’s just a matter to add a bit of CSS.

And you should now get to something like this.

And that’s about it. Links to live demo and source files below.

Source & Demo

Be Sociable, Share!

  • http://none Gilson

    Hi Filip, it’s a great article!
    I’m thinking in use it in my next projects. I’d like to know how to implement the “more stories…” Facebook style. Any idea ?

  • Typi

    good stuff

  • Kurt

    This really answered my problem, thank you!

  • Reed Hammack

    This really answered my problem, thank you!

  • Louis

    Nice post…

  • Dominic

    Keep functioning ,great job!

  • Arek

    WONDERFUL Post.thanks for sharing.

  • Ezekiel

    Very interesting topic , appreciate it for posting .

  • Raymond Izard

    An extremely helpful Blog you have got here with lots of good information. Thanks

  • Joel Schleider

    Fantastic web site. A lot of useful info here. I’m sending it to some buddies ans additionally sharing in delicious. And of course, thank you on your effort!

  • Jake

    Thank you for sharing with us

  • Pingback: web | Pearltrees

  • HokiePerogi

    Any chance you can update the source link as it’s now broken. Would love to try this out in my environment.