• Meteor

Subscribing to Data

From the class:  Using Iron Router

So far we've been getting our published data for free by using the auto publish package. In this video we'll remove that package, and I'll show you how you can use Iron Router to help manage your subscriptions based on the URL or the page that you're on. So to start off, I'll say Meteor remove auto publish. And we should see when the application refreshes that we lose our data on the screen.

So over in our JavaScript in the server block here, the first thing I'm going to do is to publish out the articles. And this will just return a cursor of all the articles. So we'll just say articles.find. And in a normal simple application, what we could do is on the client side subscribe to this globally. So without using any kind of routing, we just say meteor.subscribe, and we'll subscribe to you all the articles. And that should be enough for us to get all of the articles back on the page.

Now how you design your subscriptions is somewhat app dependent. And it will be based on things like how much memory you want to use in the browser, and also on the performance of the page. So for example, if we go to a particular blog article, you might want the data for that blog article to come off the wire first before anything else.

So I'm going to show you a couple ways that we can manage this using the router itself. Now again, it's perfectly acceptable to have global subscriptions. But in this case, I'm going to move this subscription call into the route where it's needed. So I'll navigate over to the home controller, and I want to subscribe to all the articles only when we're on this home page.

To do that, I can add a property to the controller, or to the route if I'm just using route options, called subscriptions. And the value will be a function. Inside of this function, I can subscribe to any subscriptions that are relevant for this particular route. And when you navigate away from this route, the subscriptions will be automatically torn down and all the data associated with them removed from the client.

So I've added the meteor.subscribe call here and subscribed to articles, and we're going to do that when we're on this home route and the home controller is running. So to demonstrate this, over in the right I've opened up the JavaScript console and navigated to the Network tab. And you can see the initial added messages here for this subscription.

Now, if we navigate away-- for example, into a particular blog article-- and we look at the web sockets again, you'll see that each one of these documents has been removed, and we've unsubscribed from this article subscription. And that's because we navigated away from this home route, and this home controller has been stopped. So organizing subscriptions by route like this can help us optimize for memory use as we navigate around the application. In other words, you may not always want to have all of your data loaded into memory at once.

There's two important points to keep in mind about the subscriptions option. The first is that this function will get called before any of your hooks or your action function. So that gives the controller a chance to subscribe before we run any other major logic for the route.

The second thing it does is allows us to accomplish a common use case. And that is we sometimes want to be able to know whether or not a subscription is ready. In other words, we've received all of the initial data for the subscription. And if we're subscribing to multiple things, we might want to be able to track the readiness of each one of those subscriptions together.

So to do that, we can return one or more subscription handles from this function. So I can either return the handle or an array of handles. To show you how this works, I've overridden the action function so that we can put some logic in there. And I open up the Network tab again so you can see all these initial added messages getting sent.

What we want to be able to do is to know when we get this ready message from this particular subscription, or for all the subscriptions for this particular route. And by returning those subscription handles in this function, we can just call one method on the controller to decide whether all of these subscriptions are ready or not. We can just say if this.ready and call the ready method. And that means all the subscriptions that are returned here are ready, or we've gotten the ready message for them.

So what we can do here is to say if we're ready, then go ahead and render the normal template and regions. Otherwise, we can render something else, like maybe a loading template. Now, we initially got a flash there of an error, and that's because we don't have a loading template to find. So let's go back to our HTML and fix that. So I created a new loading template, and we'll just say hang tight. I'm loading.

And then let's go back to our JavaScript and put a little bit more of a delay in there so we can see this actually working. In the server side publish function, don't worry too much about this code. But I've added in some code here that will introduce about a 5 second delay before we return these articles. And so about 5 seconds from when we subscribe, these added messages will get sent along with the ready message.

So for the time that we're waiting for the subscription to be ready, this will be false. And so we'll render this loading template. And then once we get this ready message, we'll go ahead and render everything else. So let's give that a try. I'll just refresh this page.

And notice we're loading the loading template here. And then five seconds later, it goes away and we rerender the right template. And that's because this actually invalidates the outer computation, causing this whole function to rerun when the state of this result changes. And so when it goes from false to true, this whole function actually reruns.

Now you can even make use of this ready state from with inside of your template helpers. For example, if we wanted to show something specific to this template, we could create a helper on the blog template. And we can access the current controller by using the iron controller method and then calling the ready method on it from with inside of a helper.

So next up, let's look at the specific blog show pages. When I click on a particular blog article, it's now blank because the data has been removed since we navigated away to a different route. And so now that we're on the blog show page, we need to subscribe to this particular blog article with this ID.

So let's navigate over to the blog show controller. And actually, it's called the article show controller. But inside of this controller, which we'll run when we navigate to this particular article, I'll create a subscriptions option here. And again the value will be a function. And we'll go ahead and return meteor.subscribe. And this time we'll subscribe to a particular article. And that's because we want this article to load right away very quickly. We don't want to have to wait for all the other articles to load first.

But we'll pass up an ID, and that ID we can get directly from the parameters object. So just like in our data function down here, what we'll do here is grab the ID from the URL and subscribe for that particular article. Now in our Network tab, you'll see that we get an error no subscription because we haven't actually created this yet. So let's go over to our server code and provide the subscription.

I've gone ahead and pasted in this publish function, and notice that it takes a parameter of an ID. And what we want to do is to return the result for this article. Now be careful that when you do publish functions like this, we want to use the find method so that changes get published automatically. If we use find one instead, then we won't get changes.

So what we're going to do here is to say find, and we're going to find the article with the ID that was provided up as a parameter. When I do that now, I navigate to this page, and we get the data for this article. And down on the Network tab, you can see the actual article documents sent down, and then we get the ready message for the subscription. Just like we did before, if we wanted to we could show a loading indicator or something else while we're waiting for the subscriptions to be ready for this particular route.

In this video I showed you how we can subscribe to a publish function globally, but we can also subscribe inside of our routes by using the subscriptions option. This provides us two benefits. The first is that we can subscribe before calling any of the other hooks or the action function in our controller. And the second is we can return the subscription handles from this function and then tell all at once whether all the subscriptions are ready or not.