Ember Octane Fundamentals

Container Components

Ember Octane Fundamentals

Check out a free preview of the full Ember Octane Fundamentals course

The "Container 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 creates a container component and explains why it is necessary to create a new array when loading data for tracked properties.


Transcript from the "Container Components" Lesson

>> Mike North: Let's build one, and we're gonna build one to kind of wrap around the chat
>> Mike North: The chat header messages and footers. So it's just gonna be this white area on the right side of our screen. We're gonna wrap a container around that to facilitate the components it contains being very simple and not having a lot going on.

Let's get started. Generating a new component.
>> Mike North: Just requires that we run ember g component chat-container. All right, so we have as before our new component, our component's template and integration test for that component. So let's go to this JavaScript module here for the chat container.
>> Mike North: I'm gonna show you a technique that you can use if maybe some of your API calls are a little bit slow.

And you don't want your initial page load to necessarily wait on them. So we're gonna make it so that when this chat container is initially rendered, it'll be as if there are no messages. And as soon as this component's dropped into the DOM, we'll kick off a A-SynC fetch.

And when that comes back we'll fill our empty array with some chat messages. This is a great way of making sure that your page load time is fast. Cuz at the very least in our case, if we just think the left side of our UI, if we could land that on the screen really really quickly, it might buy us a little bit more patience from our user where they'll wait for the messages to come in.

So we're going to need, cuz we're to render the messages on the screen, this sounds like a tracked property. It's an array we're going to fill it later. We're going to need to watch this thing it's rendered, right? So we'll import tracking, or tracked from tracking, and we'll say tracked, here's our messages array and it begins empty.

And it's only yelling at us cuz type script is freaking out a little bit. I'm gonna just restart my type script server and we're good to go. So the next thing we want to do is make a function, which when invoked, will result in the messages array being filled.

And it’s gonna get invoked from the template to spoil some of the surprise here. So we’ll need action, right? And we’re gonna make this an async function called load messages, and we can grab some args that we are gonna get past. So if we're given, just trying to think of what data we need to fetch messages, so it's gonna be like team/team ID, we need the channel ID.

If I look at our data.
>> Mike North: And look at a channel. Let's see if that'll do. We've got a team ID and we've got a channel ID, so it seems that as long as we're given a channel, that's complete information in order for us to build the URL properly and fire this request off.

So I expect we're gonna get an ARG channel and we can put that in place right now. When you go to channel hbs. And I wanna,
>> Mike North: Wanna grab this. This is gonna be become part of our component this main element.
>> Mike North: And we're going to up here say channel container.

Chat container, whatever you want to call it, and let's go down here. So it looks like that. We're going to pass it our channel which is this dot model, right? We're already using the title of the channel description. This kind of fits, right?
>> Mike North: Looks good, and I have that main thing.

It's still on my clipboard. Otherwise, you could look at your git dif and recover that. So let's go to our chat container hbs. And I'm just gonna put this, oops wrong file, chat-container hbs. I'm just gonna put this here for now to that main tag, we'll deal with that in a moment.

But I'll just wanna save it for later, it's gonna be part of this component. So given that now we're receiving that channel, we know how to access an arch from our template, we just use at channel. This is the first time that we need to access it from within a piece of JavaScript code and it's pretty simple.

It's just this.args.channel or you could de-structure it,
>> Mike North: Like this.
>> Mike North: If you need multiple arcs this might be a more convenient form to use where all at once you can kind of plug off exactly what you need. And in fact, we just need the channel ID which is the property ID on the channel and we also need channel dot team ID so we can destructure even further.

We need ID and team ID.
>> Mike North: And they're unused, obviously, we'll fix that in a moment. So time to kick off our request.
>> Mike North: There's our response. We're gonna import fetch from fetch,
>> Mike North: And we're gonna await fetch aPI teams. I want to just check our docs here.

Pretty sure I remember it, but I don't want to be sure. All right, get a team channel messages. That's the one we want. So it's API teams. Just copy all this. Sounds good to me.
>> Mike North: So we'll put that in here and we'll replace this with the teamId, and this with the channelId.

>> Mike North: Actually, it's just ID. That's how we destructured it over here. So there is our response coming back. I have a problem in my template so I should go and fix that right away. Chat-container, it self closed for me. Thank you, helpful Edgar. I did not need that.

Okay, so we're in a good state now. Ultimately we want the JSON that this response comes back to us with, we want that to be part of, that should be the new messages, right? There's something thats a little strange that I'm going to do now. I look at it as a very small price to pay for being able to write vanilla JavaScript, but it's not necessarily exactly what you might think you would do is this last step.

So we're gonna say this messages equal this messages and then await
>> Mike North: Actually we don’t even need that. This is a complete solution, I’ll walk through in a second. Response, JSON, so we need parentheses and another spread. So what does this mean, why on Earth have I done this?

Let me put this down a couple lines so we can read it and talk about it easily. So come on Perrier.
>> Mike North: All right, so what we're saying here is number one, we are assigning a new array to this messages. We're not pushing objects into the existing array.

This is necessary or your tracked property will not keep up. So the arrays contents are not being monitored. You need to replace your old array with the new array. So I am taking any messages that might already exist in there, right, say we're calling load messages like every minute or something, to pull new data down.

I don't wanna necessarily lose the existing messages. And this will give us new data here. Now, If we were actually doing this and doing incremental loads we'd have to worry about the message may already be in memory, and it may also be coming across from the API let's not worry about that for now.

But this is kind of what a robust solution might look like before the duping of some kind. We could get away for now with this.
>> Mike North: But you would have a totally different solution. If you wanted to periodically load more. We can leave it like this but but the point I wanna make is do not do something like that.

Don't push, you need to make an assignment. And in fact, sometimes there are situations where you wanna indicate that we have made a property change. You may have to do something. In fact, I think we should experiment here. You could do something like this. Push or wait
>> Mike North: But then you would absolutely have to do a weird thing.

Right, this ensures that the tracking system sees a new assignment of some sort and can evaluate whether something needs to change. Now I would sooner have the solution that I just had here than what you see right now. This looks a little bonkers. And it may confuse your fellow developers but in a very performance constrained situation you might want to avoid the extra array allocation.

>> Speaker 2: Also there's a notified property change API for the old way of doing that, right?
>> Mike North: Correct, and this is the equivalent of notified property change.
>> Speaker 2: So you can't use the old one anymore?
>> Mike North: It is a totally different system.
>> Speaker 2: Okay.
>> Mike North: There are no named properties any more.

>> Speaker 2: That's what they talk about with chains?
>> Mike North: Property chains are part of the old ember, like ember object and the stopped get, they start set and this is a very new thing. So I'm gonna just back this out, so we're replacing the old array with the new array.

But just know that a common snag that people hit is they, when dealing with tracked, mutable values, they forget that you need to have some sort of assignment in order to tell Glimmer, the view layer under Ember. Which is the thing that's determining whether HTML needs to be updated.

You need to, an assignment is what it is listening for, not the mutation of the array. Or adding new things to the array.
>> Speaker 3: So ember object computer had the concept of that dot bracket watcher. Is there no concept like that in Octane?
>> Mike North: We’re trying to encourage immutable data treatment and that would involve creating a new array and using that.

Now when you deal with collections in the 10,000s, then it becomes potentially worth trying other approaches but that is an exception. And you would want to, before going there, you'd wanna prove that this is going to be a measurable performance improvement, and it's not something you just wanna do because one day it might matter.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now