Check out a free preview of the full Ember Octane Fundamentals course
The "Presentational Components" Lesson is part of the full, Ember Octane Fundamentals course featured in this preview video. Here's what you'd learn in this lesson:
Mike introduces the yield helper, the did-insert modifier, and the did-update modifier to expose message data from the container and re-render on update. Then, Mike pipes data into the presentational component and discusses strategies for test creation.
Transcript from the "Presentational Components" Lesson
[00:00:00]
>> Mike North: Now that we have messages, let's get them rendered on the screen, right? I mean, for a while now since the beginning of the course where we refactored messages into a template only component, we've seen these same three repeated messages. So let's see if we can render something different.
[00:00:21]
In order to do that, we have to find a way for this component to sort of expose this messages' value to its contents, right? It's a container component, it needs to show messages to what it contains. And here's how we do that, we use this yield helper. And you can think of this as invoking a callback.
[00:00:44]
Just like when we were using each, or it's like array.for each and you pass it this function and within the function it has access to an item. We can do the same thing here. So what I'm gonna do is yield and pass this yield dislike, invoke the callback and I'm going to pass it an argument.
[00:01:11]
>> Mike North: What this let's us do is if we go back to channel.hbs, we're gonna bring this down here so we can see above and below.
>> Mike North: Our chat container now can use the same syntax we saw with each, which will look like this, as messages.
>> Mike North: And now, we can consume the messages value down here.
[00:01:43]
>> Mike North: And let's just see if they got logged to the console. So there's our empty array. So good sign, that's not null, that's not undefined. And what I want us to do is iterate over that empty array or iterate over the array, whatever it may be,
>> Mike North: Each messages as M.
[00:02:07]
And we're gonna pass,
>> Mike North: We're gonna pass that message, the entire object, to the message component. And all of our messages have disappeared. This makes sense, we're iterating over an empty array, we don't expect any of these components to show up. What's left is we have to go back to our chat container, and think about this load messages function.
[00:02:36]
We didn't do, we defined it, pretty sure it's correct, but we haven't kicked it off yet. So we're gonna do something that is a little different from what you're used to if you're, used Ember for a while, we need a life cycle hook, right? So in React, this would be component didn't mount.
[00:02:55]
In older Ember, this would be did insert element, right? Something that is triggered when the components initially dropped into the DOM and this comes by way of modifiers. And what's cool about this is we're no longer working with the lifecycle of a component. We are dealing with the lifecycle of individual DOM elements, which means if you have a piece of DOM that is it like inside an F, right?
[00:03:23]
And you want whenever this is inserted, it has to run this action, and you're toggling it, shown, hidden, shown, hidden. Now, that hook is gonna be fired every time it's reinserted, right? So this really all along has been an element concern, not necessarily a component concern. So you can have different actions that are firing at different times, each scope and pertaining to whatever DOM element it's attached to.
[00:03:51]
And here's how it looks. Did insert this.load, think they called it load messages, yep, perfect, load messages. And there we go. Now, it's still the same message. It's shown twice here. It's shown twice here. Let me do a reload real quick, three four times here so I'll buy that the array is of a different size as we go through these things.
[00:04:21]
Now, it's not perfect. Cannot read property ID of undefined. Seems like there's still some work to do here.
>> Mike North: We have to consider the case of changing channels. And It's a little bit tricky here, but Ember is trying to change as little of the HTML as possible. And what that means is, this component on the right side of the screen, although we can kind of think of it being torn down and rerendered, it's just actually changing its data.
[00:04:58]
Because as we go from channel to channel to channel within a team, we're not going to a different route. We're just changing the dynamic segments in that route. We call this an in-place transition. So we need to kinda specify that whenever a particular value updates, we should run load messages again, and we can do that with another modifier here.
[00:05:25]
>> Mike North: Did update, call this load messages? And we're gonna pass it the channel. So whenever the channel changes, we're going to rerun this load messages hook. And now we should see four messages here, two messages here four messages here. So I'll buy that and I can see the API calls at the bottom, see that XHR?
[00:05:57]
so looks like we're fetching data, were properly re-rendering creating our new array each time this is happening. This looks good. I'd like to see the content of these messages. Let's looks at the message HBS file and start to take advantage of the fact that we are now passing a message ARG to the component.
[00:06:22]
So what we can do here is use message.user.name. We can use our format timestamp function. Remember that helper we made. I think it's message.createdAt, I'll do a quick save, okay. So now we're getting different names here popping up, in different dates for that matter, different time stamps. This is real data from our back end driving these components.
[00:06:53]
Here's an avatar URL so we can delete that and replace it with, I think it's message user icon URL. Awesome, and just scanning quickly for other things, obviously, the body of the message, Message.body. Look, different messages now. We can change channels and see different stuff, still got this error here.
[00:07:29]
I'm gonna dig into that in just a moment. Yeah, why not dig into it now, so cannot read property id of undefined. It's the before model hook in the team, index route.
>> Mike North: So there's no team, that's interesting. You know what it is? I left in a little, remember our hackery like we could pick the zeroes channel or the first channel.
[00:08:00]
This should be channel 0 so I'm checking for is the length greater than zero. And then here I was assuming that it was 2. Silly mistake, it's what I get for trying different iterations of things. So now it should be working smoothly. You can go to different, smoothly minus a debugger, we should be able to go to different teams.
[00:08:24]
See lots of different messages, different channels within those teams. Very data driven app now, it's making API calls, lot of different stuff showing up now. Our URLs are working nicely. So this app is really taking shape at this point. Go ahead.
>> Speaker 2: I'm making two calls to messages on page load, and seemingly on switches as well.
[00:08:46]
>> Mike North: Interesting, let's see what's going on. You know what I could probably do?
>> Mike North: Let's try removing the did-insert.
>> Mike North: Nope, we need that initial thing to fire off.
>> Mike North: So what we would probably want to do, yeah I-
>> Speaker 2: It's like I did receive errors.
>> Mike North: So I see on the initial load, I'm only making one call here.
[00:09:15]
So let's clear that out and change channels. I see I'm only making one call here, changing channels. Let's try changing teams. I'm gonna clear this out.
>> Mike North: All right, I am not seeing duplicate calls made so I'm happy to take a look at your code at the next break.
[00:09:34]
>> Speaker 2: Okay.
>> Mike North: But this looks right to me. So did-insert, that's only on the first insert and did update is only on subsequent updates not on the insert. So this should handle the first and then second through nth, which is just what we want. The reason we have to pass channel here is the, just has to do with giving it an extra clue that whenever channel changes, we should consider that to be an update as opposed to other things updating as did update is designed to handle.
[00:10:14]
So let's check the test real quick.
>> Mike North: All right, chat container test, we haven't looked at cuz this is the first. This our container component unit here. Let's hide past tests. So that's the only one that's failing. And for time purposes as we have already done quite a bit of test writing, I'm gonna skip writing this test with you.
[00:10:46]
I do wanna make one point before before we skip too hard here. The benefit of testing a container component, the reason it becomes easier once you take the presentation away is you could do channel equals whatever and then as messages. And you could render this as just H1s or an unordered list or whatever, or some very simple, easy to assert against DOM structure that doesn't even closely resemble the way you would use this container for real.
[00:11:18]
Because this thing's job is just to expose that data or to provide you with actions or whatnot. But you can find very nice ways of making your life easy, just, for example, it might yield out six arrays, only one or two of which are visible at any particular time.
[00:11:41]
And instead of worrying about testing this in all of the different states, you can just spit everything out and assert that it's all there and do it all in one shot. So I'll just point that out. But I don't wanna spend too much more time since we've covered a lot of testing ground so far.
[00:12:00]
So all of the other tests are passing.
Learn Straight from the Experts Who Shape the Modern Web
- In-depth Courses
- Industry Leading Experts
- Learning Paths
- Live Interactive Workshops