Ember 2.x Loading & Error Substates
This course has been updated! We now recommend you take the Ember Octane Fundamentals course.
Transcript from the "Loading & Error Substates" Lesson
>> Mike North: Loading and error substates have to do with what's happening as you transition from one route to another route and in your model hook or your before model hook or your after model hook you've returned a promise. So what's happening while we're waiting? So what ends up happening is you're in this intermediate state.
[00:00:26] You're in this loading state that is part of the state machine but it does not correspond to a url. It is sort of a waiting state that you're in until that promise resolves and then you pop out of it. So similarly there is an error sub state and the idea here is that if you requested something and your promise rejected instead of resolving.
[00:01:02] You'd end up in an error.
>> Mike North: Make sense so far? So in addition, when you error, when you reach this error state, it is usually the result of either firing the error action explicitly or a promise in your model hook rejecting. This error action on the route in question where the error happened will fire.
[00:01:33] And you usually will want to handle something specific like maybe a 404, in a way that informs the user that they've typed something in that's wrong or they're lost and click here to search and find whatever you really need versus like a 500 which is sort of a catchall server blew up.
[00:01:55] And those you would sort of let fall through to a generic error page of some sort. So I'm gonna show you how to set up loading in error substates, and then we're gonna really quickly add it to this app that we've been working on.
>> Mike North: So this is quiet easy.
[00:02:23] All you have to do,
>> Mike North: Is add a template called loading.hbs.
>> Mike North: And this will give you some style for your loading substate. So if we go loading dot dot dot dot dot and we don't really even see anything. And the reason is there's no waiting yet, it's fast and especially on this page we're literally returning an array literal.
>> Mike North: But if we go in here and for no good reason at all add a promise that resolves in a couple seconds.
>> Mike North: All right, and I will explain what all this is doing. It's not incredibly important but essentially, one thing I forgot. So ember run later, there's this concept of a run loop in ember and it's a prioritized work queue and it is sort of the magic behind efficient DOM updates, where if you set 17 properties in an action and it will result in 17 DOM changes, potentially things changing back and forth and back and forth.
[00:04:10] Ultimately, all of that is sort of lumped together and only the ultimate changed state will be what drives an update to the DOM. And so, what we're doing here is instead of using set time out, which will just happen at an arbitrary time, we're scheduling a task to be put into this work queue no earlier than 3,000 milliseconds from now.
[00:04:34] And here we're creating our own promise and we're resolving with the same array literal but we're gonna resolve three seconds later.
>> Mike North: There you go. That's it, loading.hbs is all you need. All the rest of this was just to make it so you could see it. But loading.hbs.
[00:05:01] Now we know that these routes load in a hierarchy. So I'm gonna add some more delay. That was a real wait by the way. Let's see. Wow, that's getting frustratingly long. [LAUGH] We're gonna go back to, one second.
>> Mike North: So here, this is actually the API request just for a moment.
[00:05:28] Taking a second. So we can put another loading.hbs inside a route. So you remember when I was looking at the ember inspector earlier and we peeked through and saw that there were a ton of routes that we hadn't talked about? Right, so we've go the application loading, which we've actually supplied a template for, but we've got orgs loading and we've got org error.
[00:05:59] So error and loading substates can exist at many different levels. So if you wanted to have a different spinner or something, while you're looking for an org or more than likely an error page that says, this org was not found. That can be sort of a localized specific thing that pertains only to that, only to pivots that are on that particular node in this hierarchy of routes
>> Mike North: So let me see if I can illustrate that with an example. So if we make loading.hbs, and there it is.
>> Mike North: There it is.
>> Mike North: Okay I gotta slow that down but [LAUGH] it's unfortunate that GitHub's API is sub performing it's screwing my demo up.
>> Mike North: Promise, resolve, reject, arrow.
>> Mike North: Then.
>> Mike North: All right so there's our initial load. What's going on here.
>> Mike North: org.js.
>> Mike North: I'm going to remove this at our loading state. I'm not sure what all is going on there.
>> Mike North: I have to admit, this is not working how I expected.
>> Mike North: You know what, I'm just going to try this.
[00:08:46] We'll just go with the initial thing.
>> Mike North: So it's very hard to see. I know what I can do. Chrome to the rescue. I'm gonna throttle my network speed way down. So we're gonna simulate Edge network. GPRS, the good stuff.
>> Mike North: Maybe we'll recap this tomorrow. I have to investigate what's going wrong here but for those of you who can see the live view, we were seeing a loading state blink briefly
>> Mike North: When that API call is being made. Maybe if we delete this.
>> Mike North: Come on.
>> Mike North: Yeah, I think I'll demo this tomorrow, after I figure out what's going on here since we're at the end of the day here.
>> Speaker 2: There were couple of questions.
>> Mike North: Yep.
>> Speaker 2: Do you mind answering them?
[00:10:21] One of them was that kind of what you're doing. If a child route is loading and it doesn't have a loading.hbs. Does it default to the parents loading template?
>> Mike North: Yes, it should. But it will bubble up until it finds a defined loading substate.
>> Speaker 2: Okay.
>> Mike North: So it will not go all the way up to, like if the parent doesn't have one either, it will basically keep traversing upward.
>> Speaker 2: The other question was the model four. Is this a desired way to share data between routes or are services preferred?
>> Mike North: So, our use of a service, so if the data that you're sharing between routes is resolved models from routes, you should be using model four. If you're sharing application state or a singular piece of data that is needed throughout the application like the currently logged in user, that's what a service is for.
[00:11:23] And it's worth noting that you don't have access to model four in things like a component, whereas you can totally use a service to share data with a component. So in the context of routes, it seems like we could do both, but one is extremely general and the other is route specific.
>> Speaker 3: [INAUDIBLE]
>> Mike North: You're asking me to speculate on something that's currently in a polar class and like it could turn out that way. Controllers right now have direct access to the store. And I think the paradigm is going to be, provide a mechanism for injecting behavior onto components that might need it.
[00:12:20] Like inject the store as a service onto component. Rather than just a blanket access because many components don't need any knowledge of that, only the top level routed components.
>> Speaker 3: So when you're talking about the store that's the [INAUDIBLE]
>> Mike North: The store?
>> Speaker 3: It's however data is exposed, is it gets injected [INAUDIBLE]
>> Mike North: Actually all of the frameworks use the same thing. In Flux you have a store. In Ember data you have a store. In Angular data you have a store. And the idea is, especially if you have some sort of caching solution, you want a central place to be able to request records of a given type to create new records that originate on the client.
[00:13:08] We'll get into all of that tomorrow when we dive into Ember data.