This course has been updated! We now recommend you take the Ember Octane Fundamentals course.
Transcript from the "Routes" Lesson
[00:00:00]
>> [MUSIC]
[00:00:03]
>> Mike North: So the next topic we're gonna dive into are routes. Now if we think of the router as being a finite state machine. Routers are what manage transitions between the states. And this means they're responsible for managing the act of loading the data. The act itself is deferred to another object we'll talk about later.
[00:00:27] The route's responsible for loading the correct template for the route. And rendering it in the proper context. It can also handle user interactions. So if the user clicks or types something in or hits enter and that's a significant event in your application. The route objects are capable of responding to that event.
[00:00:54] It's important to have an awareness of the life cycle of a route. These hooks here are always called in this particular order. And they represent different important moments that you can inject your own behavior into the default route. You can extend it at some important locations. I wanna draw your attention to before model, model, and after model.
[00:01:21] They're what we call promise aware hooks. And all this means is that if you return a promise from these hooks. The router will actually wait for that promise to resolve before proceeding to the next step. So for example, if you make an Ajax call in the model hook to fetch some data.
[00:01:46] You will not continue on to calling the after model method until that data has returned and until that promise has resolved. BeforeModel is a place where you have access to this transition object. It's a great place for any early checking to see if you should in fact be entering this route in this context.
[00:02:13] A common thing to put here is see if the user's logged in. And if they're not, redirect them out to a login page. It is a promise aware hook. So you can do a sync task here in preparation for loading the model. For example, if you had to load a script, or if you had to potentially make an API call that they didn't represent the actual data that you were using in your rendered template.
[00:02:45] But it was necessary to perform some task before actually retrieving the data. This is where you would do that. By default, this is just a no-op. You pass straight through it. The model hook is the place you want to make your API call and get the object of greatest importance to your template.
[00:03:07] And that is the data, the record that you're looking at. So, to align with the example that we've been working on so far. This is where you'd make an API request to GitHub to say, I want an org, or I want a repository, or I want a list of repositories in this org.
[00:03:27] The default behavior has to do with Ember data in particular. And if you format the names of your dynamic segment parameters in a particular way, and that particular way would be something like post_id. Ember data will try to fetch. A model of type post with whatever, whose ID is whatever the value is in that dynamic segment pulled from the URL.
[00:03:56] So be careful when naming your dynamic segments. Because you may see some unexpected behavior if you align with this convention. Ember will sort of try to help you out. And if you're not expecting it, it can be a little confusing. So whatever the eventual value is that model returns.
[00:04:17] And this could be the actual value returned by the function model in the route, or it could be whatever the promise returned by the function model in route. Whatever that promise resolves to, that will be available in the corresponding template that's matched to that route as this property called content.
[00:04:37] And that's an Ember idiom. You don't want to try to mess around with that because it is what people expect to see.
>> Speaker 2: Content versus model? I always use model instead in templates.
>> Mike North: I believe you can use either at this point.
>> Speaker 2: This one, or this?
>> Mike North: Yep.
[00:04:56]
>> Speaker 2: Is there a definite advantage there?
>> Mike North: I don't know if there's one advantage or another. So there used to be this concept of proxy controllers. But I don't want to go in to that because it's. That is the old way. I'm deliberately steering clear of some of the legacy stuff.
[00:05:16] So in addition to being available in the template for this particular route has the value content. Other routes can get access to the model that you've loaded using this.modelFor, and then the name of the route. So, if for some reason in the route for loading the repost, I needed to get the organization.
[00:05:39] I needed to get the org, which is a different route that's responsible for retrieving that data. I could just within my child route get the model that was resolved in the parent route and have access to it. And potentially maybe build a URL off of it. Because I might need the orgs to ask for the repost for that org sneak preview.
[00:06:09] RSVP hash so if, definitely get used to using RSVP.js it is part of Ember and it comes for free. It's mounted as a global on the Ember object. RSVP hash is very useful when you have particularly an object that may be mixed values and eventual values. So, maybe an object with a bunch of keys and some of the values are numbers and strings.
[00:06:34] But some are promises that eventually will resolve to something. RSVP.hash, gives you a single promise. That one resolved will have all of your data ready to go. AfterModel, this is called, obviously, after the model hook. This is where you want validation that depends on the resolved model itself.
[00:06:59] So you can imagine a case where you retrieve a record. Maybe you've got some, your API makes soft deleted records available for some reason. You can still have access to them. And, you might want to transition to a different view, or you might want to handle something in a particular way.
[00:07:20] If in fact, the actual data that came back has a certain state. This is where you would put that. And again, just like beforeModel, this is a pass through. If you don't extend and implement this message yourself. So that rounds up the promise aware hooks. And we're gonna look at a couple more.
[00:07:45] So redirect, it happens almost at the exact same time as afterModel. The reason that you need this. The reason this is an important one to know about is that if you need to redirect and you do it in afterModel. You're actually interrupting the current transition. And that transition will basically be run again.
[00:08:15] So, imagine the case where we're in org. And then we redirect to like orgrepos, a child route. What'll end up happening is we're gonna run the beforeModel, Model and afterModel again. You basically throw everything out and start over if you redirect from within afterModel. Whereas if you redirect from within redirect, the initial transition is considered to be successful.
[00:08:38] And you won't redo the work of this route that you're actually in right here. And depending on what you need to do. That can be detrimental to run through that process a second time as you're transitioning into a child route. Does everyone understand the difference there? It's a little nuanced.
[00:09:00] All right. So setup controller I want you guys to forget the word controller that I'm trying not to mention controller. Controller is so Ember 1.X. It's not Ember 2.X. This is where you, it's not promise aware. And it's where you can setup additional context available to your template.
[00:09:25] So say that we want in the repos page to have org also available in repos.hps. In setup controller we can use thisdocmodel4 to grab that model from the parent route. And we could actually place it on this controller. And it would be available in the template as a second parameter.
[00:09:50] So we would use content for the list of repos. And then we could use say, org for the organization those repos belong to. And we'd have full access to that model. Make sure you extend this properly. And that means using super to call the default functionality. This does some important stuff in the base class and you do not want to omit that important stuff.
[00:10:22] So common bad pattern that people fall into is, and I have to say a little bit about controllers here. So let's try not to. So controllers are singletons. They're singleton's by default. They stick around for the entire lifetime of that users session, or lower case S session in your app.
[00:10:46] And if you find that you have state on your controllers and you're using setup controller to kind of zero everything out and blank it out and reset your state. This becomes a maintenance challenge. Cuz every time you add state to the controller, you have to make sure you add the appropriate cleanup logic.
[00:11:03] So try not to fall into that trap there. It's fine if you're just making an object available consistently. But if you find that you're setting things back to zero, blanking things out. Take a step back and see if you can find another way to do it. Cuz that doesn't scale up in terms of scaling with complexity very well.