• Meteor
  • Tutorial

How To: Use an iOS7 Style Loading Indicator with Iron Router

Recently on the iron router project, we were discussing different ways of showing loading indicators. And Sasha recommended using a JavaScript library called NProgress that lets you show an iOS 7 style loading indicator at the top of the page while you're waiting for your data. I really liked this idea, and I wanted to, in this episode, show you how you can do that. In doing so, we'll get a better sense of how before hooks work in the iron router.

Here's an example of our final application. You can see it's pretty straightforward. I'm using iron router for routing, and the root path just shows the title of the first item in the list. If I refresh the page, keep your eyes trained at the top, and you can see the iOS 7 style loading indicator that shows while we're waiting for the data. And then, our template is rendered as usual.

To get us started, let me click over on the NProgress GitHub repository. This lets me get a sense of the project, and I can come over to the SSH URL and copy it so that I can clone the repository locally. I've created a new Meteor project, and I'm showing you the packages that I have. You can see that I've removed auto-publish, and I've added the iron router package. I've also updated my project directory structure a little bit. So, you can see that I have the main app.js file in the root, and I've created a client folder where I'll put all of the client files, like the CSS and any libraries that I might use.

So, what we need to do is to clone the NProgress repository, and grab its CSS file and JavaScript file, and put it into our project. Over in my GitHub repository folder, I'll clone the NProgress repository and then change into it, and take a look. The two files we need to grab are the nprogress.css file and the nprogress.js file.

Back in my project, inside of the client folder, if you take a look at the CSS directory, you can see that I've copied over the nprogress.css file into that folder, along with my applications CSS file. And if I look at the lib directory, you can see that I've copied over the NProgress JavaScript file.

In my JavaScript file on the left, you can see I've created one route called page, which is mapped to the root path. And down in my server publication section, I've created a publish function called items with latency, and it simulates taking a long time to get the data off the wire. So, in about two seconds, it returns this cursor object, which is all of the items in the list. On the right, I just have my one template and a layout, and the layout just yields. And inside of the page template, what I'll do is just show the title of the first item.

Now let's modify our page route to use the loading indicator. To do that, I'll create a before hook using the before option. In this case, I only have one before hook, so I'll assign this to a function. And the first thing I'll do is subscribe to the items with latency subscription, and assign the result to this handle variable.

Next, we'll check whether or not the handle is ready. This means that we've gotten all of the data, the initial data, from the subscription off the wire. Remember that before hooks are reactive. And so, if this is a reactive data source, and it invalidates the current computation, then this route will be run again, including all of the before hooks.

But you might ask, if this entire function is run again, won't we re-subscribe to the same subscription over and over again, causing, essentially, an infinite loop? The answer is no, because Meteor is smart about how it handles subscriptions. If it sees that we've already subscribed to items with latency, it won't set up a new subscription. It'll just return the existing handle. And that's why this code works.

So, if the handle's not ready, what I'll do is call NProgress, which is the library that we just downloaded. And I'll call this start method. And then I'll stop all downstream hooks from running, including our action function, which renders the page template. And if the data is ready, I'll call NProgress done. And I don't need to do anything else. The rest of my before hooks will run, and my action function will run automatically.

The next thing we need to do is to create a data option on the route. That'll set a global data context that will be used when the templates are rendered to the page. So, over in the HTML file, you can see we're using the title property of the data context. And what we need to do is to set that data context object. So, let's create a data option, and we'll set it to a function that returns the first item in the list.

Now, it turns out that, in iron router, this data function is implemented as a before hook as well. It's just the last one in the list. So, this function won't get called, and this page template won't get rendered, until all of our before hooks have executed. Inside of our before hook, we call stop if the data's not ready. And so, that means the data function won't execute until this subscription is ready.

And now, if we reload the page in the browser, you can see the loading indicator up at the top. And then, when the data comes off the wire, we render the rest of the page, and we show the title at the top.

So, in this video, we looked at how we can integrate a third party user interface library and use it with iron router. We also saw how before hooks are reactive. So, we can call a reactive method that would cause the route to rerun if this current computation is invalidated. We use that knowledge in combination with the third party library to show a loading indicator at the top of the page while we're waiting for data from our subscription. And we also saw how the data function, or the data option, is a downstream before hook itself. So, we can prevent it from running until we're ready by calling the stop method in one of the upstream before hooks.