• Meteor

Hooks and Plugins

From the class:  Using Iron Router

Hooks are functions that get called at different points in the life cycle of a route controller. We can provide them as options to the global router, to the route itself, or as methods on the route controller. In this video, I'm going to show you how to use these hooks by building a custom authorization hook. And then I'll also show you a new feature of Iron Router called plug-ins that make it easier to distribute your functionality to other users through packages.

First, using the guide here, let me give you a quick overview of all the various hooks and some of the changes in this newer version of Iron Router. The first hook here is called onRun. And as the name suggests, it gets called the first time your controller is run. And it'll only get called once. So it's a good place to put things like mix panel metric tracking, or things that you don't want to rerun if for whatever reason your computation for the route invalidates.

The next hook is called onRerun, and this will get called only if your functions rerun because a reactive data source changes, for example. The onBefore action hook gets called before the action function gets called, and the onAfterAction gets called after the action function gets called. And finally, here the onStop one gets called right before we navigate away from this route. So when we stop the controller itself, this function gives us a chance to perform some logic before we go somewhere else.

Now, one of the cool things about hooks is that we can have multiple functions. So we can provide an array of functions for each one of these hooks. So we have a chance to run multiple functions at different points of the life cycle of a controller. So let me show you an example by building out an authorization hook with you.

In the app I've gone ahead and added the accounts password and accounts UI packages so that we can get login. What I'd like to have happen is when I click on New Article, or go to that New Article URL, if I'm not logged in as a user, I should be redirected back here to the home page. So over on the left, I'm inside of the article New Controller.

And if I want to, I can create a hook right on this controller that will run before the action function runs. All right. I added an onBefore action option here or property in its value is a function, and right now I'm just printing to the console that we're in the onBefore action hook. So when I click on this new article now and we navigate to this route, notice that nothing is rendering to the page, but down in the console is a message that says I'm in the onBefore action hook.

Now this behavior has changed from previous versions of Iron Router, so let me explain what's happening here. What's happened is this function is getting called, and then no further functions downstream are called automatically. So our action function, which we haven't defined explicitly, but which by default looks like this, would render our page normally to the screen. But what's happening here is we're not proceeding down to the action function. We're just printing out this message to the console log.

So that allows us to control whether or not we continue on with the rest of these functions or not. So in order to continue on, I can say this and then call the next function. So it's just like using Connect middleware. Now previously there was a method that was passed in here called pause, and you would call that to pause. But we decided to make this more like the Connect middleware API.

So when you call, that will proceed down the chain. And now you can see our page is getting rendered in addition to this console log that's getting put it into the debugger. So we can make use of this fact to make sure that the user is logged in, and if they're not, to send them back to the home page.

So what I'll do here is to say if the user is logging in, so if the login process is under way, just wait. Otherwise, if the user is not logged in, then we'll call the redirect method of the controller. This is basically just like calling router.go. And we'll send them back to the home page.

Otherwise, go ahead and proceed along with the route as we normally would. Now, when I refresh this, you'll see that we'll get redirected back to the home page, and that's because we're not actually logged in. I've gone ahead and created an account and logged in. And so now that we're logged in, if I click on the New Article link, I stay on the page.

Now one of the cool things about hooks is that they're reactive. And so if my login status changes, the hook will rerun, and notice now I'll get redirected automatically back to the home page. Now, so far we've put this onBeforeAction function directly onto our article new controller. But if our application was large enough, chances are we'd want to reuse this functionality throughout other routes as well.

So what I want to do next is to move this over to a global router onBeforeAction hook. So I'll delete the hook here. And then we'll just go back to our application. And I can apply hooks globally by saying router and then calling the name of the hook. So in this case, onBeforeAction. And I can either provide a function, or I can use a string value for hooks that come out of the box. And then I can pass along any options that I'd like.

One of the options that's useful is the only option. So I can specify that this hook should only apply to particular routes. In this case, the article new route. And then I'll go ahead and paste in the logic that I had before. And it looks like it still works. But this is generally a better pattern because I can take this hook now and apply it to multiple routes at once.

Now let's say you want to create a package to distribute some functionality to other users of Iron Router. You can use a new feature called plug-ins, which makes it easier for people to use a reuse functionality. To use plug-ins, we can define a plug-in like this. So I say iron.router.plugins, and then I give it a name. So in this case, I might just call it authorize.

And this function will take the router instance and a set of options. Inside this function, I can call whatever before or after hooks or whatever hooks I want to on the router itself. And this makes it easier because the user doesn't have to think about which hook to use. They can just use the plug-in.

So in this case, what I'll do is copy all of this. But instead of using the global router, we'll use the one that's passed in here. And then I'll just pass along any options that were passed in directly onto the onBeforeAction hook. Now, to use this new authorize plug-in, I can simply say router.plugin, and then as a string just pass along the name of it. So I want to use the authorize plug-in, and I only want to use it for the article new route.

Plug-ins and hooks can also accept or use options, just like these options that we provided here. For example, this plug-in-- we might not want to require that everyone have a route called home. Instead, we could just look up an option for the not authorized route. To do that, I can say this.lookup option and then just pass the key for the option. So I might call it not authorized route.

Then what we can do is provide that option anywhere that we could provide options normally, either at the router level, the specific route, or even in the controller. So in this case, we'll provide it here in the plug-in definition, and I'll just say the not authorized route is to go home. So now when I click on New Article, we can make sure that this still works, and it does.

So being able to pass options like this I think is a really powerful new feature, and then being able to look them up from within a plug-in or a hook. The other powerful feature here is being able to use plug-ins and to specify them by name instead of having to worry about the specific life cycle hook to use. So in this video we reviewed all of the hooks available on your routes and route controllers, and then we looked at how to actually implement one for authorization. And finally, I showed you how to use the new plug-in interface to distribute and reuse functionality, most likely as packages.