This course has been updated! We now recommend you take the Ember Octane Fundamentals course.

Check out a free preview of the full Ember 2.x course:
The "Components" Lesson is part of the full, Ember 2.x course featured in this preview video. Here's what you'd learn in this lesson:

Ember components are stored in the /components directory of a project. They are encapsulated elements that have an exposed API. Mike explores the lifecycle of Ember components, how they are consumed, and how they handle actions.

Get Unlimited Access Now

Transcript from the "Components" Lesson

>> [MUSIC]

>> Mike North: You all are already using components. If you use something like this, you're using a component. Under the hood, so this may just seem like a simple dom element. Under the hood, there exists complexity. So the idea behind a component in general is that, or a web component in general is that when you're consuming this in your application.

[00:00:31] When you place a text field, you're dealing with a sort of a single element that has a contract with the outside world. So in this case you have the concept of a place holder and you don't have to worry about the fact that there is a separate div inside the component in its internals where the placeholder is actually rendered right.

[00:00:58] You don't have to worry about that. You just now have this text input. It has this attribute called placeholder. When I set it equal to something it will handle it properly. And Ember components work the same way. So they live in the components folder. They're templates. Are in a components subfolder of the templates directory.

[00:01:26] Each component is associated with a top-level DOM element. By default this is div, but you can customize it. And one of the more powerful aspects of components, is that you have a very strong encapsulation with the outside world. And you have to pass data into it explicitly which is actually a benefit.

[00:01:50] But you have to pass data into it explicitly rather than giving it access to a generalized scope of data and what this insures is that Your component is not gonna have side effects by altering things that maybe it wasn't designed to alter. You have a contract that contract is the means of getting data in and out of sending actions out and this is a powerful concept that lets you build little islands of complexity, and then sort of wrap them up and consider them to be a black box.

[00:02:28] and when you are composing them to build user interfaces and screens. You don't have to worry about the concept of place holder being a div that is positioned in a particular place and when you start typing, this place holder should disappear. The component will manage all of that internally so you build it once and then you can use it over and over and over in your app.

[00:02:53] Additionally, these components have a well-orchestrated life cycle. This is very important in a single-page app. If you are used to writing jQuery. The jQuery ready hook is the go to place for initializing things and setting things up. There is no equivalent in the single page app without this.

[00:03:14] Right, because components come and go at various times, the only concept of the DOM being ready is a pretty meaningless one. it's when your app first starts up. And so this gives you equivalent hooks where you can perform things when the component is instantiated. Perform things right before it's inserted into the DOM and right after.

[00:03:39] So these are the important life cycle hooks to remember.
>> Mike North: This is the equivalent of jQuery.ready document ready. And, so this is where you would set anything up, where you would, if you're using a jQuery ui plugin or you're using d3 or if you need to something, run some code that will initialize some dom based on a configuration this is where you'll do it.

[00:04:11] It's important that you tear things down if you set them up. Usually I parody between these two hooks. Where you'll initialize something and maybe start listening to window.resize and then as you tear the component down, detach that listener, clean up after your self. And the reason is in a single page app you're dealing with the same pool of memory, the same pile of listeners and same events going back and forth or potentially hours and hours and hours.

[00:04:44] You don't get this page load where you boil the ocean every time the user clicks a link and goes to a new page. So if these are designed in a way that they leak and they leave listeners dangling around, you can run into performance problems. So very important to think about cleaning up after yourself here.

>> Mike North: In terms of consuming components, just the fact that a component exists will tell Ember to create a handlebars helper for it automatically. So if I create a component called my-component I can consume it just like this, just like {{my-component}}. You'll notice that I have a dash in there.

[00:05:38] That's required in the name of a component. You must have a dash and that is a cue to ember to tell it I'm not dealing with handle verse helper I'm dealing with the component. You can use block sentax we've already been using block sentax. If you think about each or we begin with a hash tag and we have a closing slash each.

[00:06:03] There is a way Just like you can have an input which is. An HTML input is a single element just like an image. A div or a paragraph tag has an open and a close and you have that flexibility of building components that work in those ways as well.

[00:06:21] And all components that are in your application that are in the application namespace, are available throughout your app. And I phrase it that way because you can install add ons that will make components available for consumption in your app. So it's anything that's in the component folder you can use anywhere and add ons can add to that even more.

>> Mike North: So here's a typical example what you often see is something as simple as this where you have the name of the component and then a series of key value pairs which essentially establish a contract between the inside world and the outside world. So on the left side you have some names which are things that's the components internal schema, right?

[00:07:19] So this would be in the case of our input example this attribute called placeholder. That's something that this component knows about. On the right side, you have either literals or properties or names of actions. But that is sort of what these things are mapped to on the outside world.

[00:07:40] So in this case we've got this concept of mySize which may exist on a controller or another component. But we've created two way data bonding there. And we've also passed a string literal in for title. And we have said that when this component fires this action called "on-favorite", I want this "thingClicked" action to be fired.

[00:08:07] What your route will handle or your controller, potentially. So the idea here is internal to external mapping. So when I talk about an explicit contract, this is it right here. So the naming convention is important and you're probably getting the idea having already worked with routes and templates, and you have to make sure that your files are correctly named.

[00:08:33] The same holds true here, and I just wanna take a moment to explain at a high level how this works. So if you see my component in a template Ember will use something called the resolver, which essentially is a mapping of strings to ES six modules. It will use the resolver to try to find a JavaScript module and a template for this component, and components can exist without having an explicit JavaScript module or without having an explicit template.

[00:09:10] Just like with routes and with templates you sort of fall back to a generic base which comes for free. But just keep in mind that if you name things correctly, things will just work. But if you have a spelling error or if you've put something in the wrong folder, you may see a result you don't expect.

>> Mike North: So when it comes to customizing components, we have a lot of options here. So, we can see that tagname is what can be used to deviate from the default outer wrapper element type, the default being div, so here this component is going to a an li element.

[00:09:54] We can add class names to that outer root element, and we can establish attribute bindings. The way attribute bindings work, you can have them in one of two ways. If we look at a line and we have a property called a line here and what's gonna happen is if you look at the resulting HTML and you can see we've got this DOM attribute called align, that's gonna be mapped to the property in JavaScript.

[00:10:33] So the difference between the age attribute binding and the align attribute bindings is that we're customizing the name of the DOM attribute in this case. By default the name of your property and the name of the attribute will be the same, if you don't have this notation with the colon in it.

[00:10:54] The notation with the colon in it let's you kind of change that mapping. In this case, we end up with data dash age is 32 and a line equals left. Because we have said that when this binding this age property, we want it to use this name. And you can also see that we're able to use defaults, so here I'm passing in an age for the first instance of this component and in the second one I'm not.

[00:11:29] And it's falling back to whatever the default value which is sort of how it define it at how I describe the class itself so it make sense.
>> Mike North: So components have actions internally right if you use the action handle bars helper in this components template the component will have the ability to handle actions in the same way that we've been using routes.

[00:12:03] You have these actions object and then function names and that's how you create your action binding. Anything that you wanna communicate to the outside world to consumers of this component, you have to do through an action binding and use this.sendAction.
>> Mike North: It is okay to send an action when there is nothing bound to it.

[00:12:30] So the idea here is if we were making some text based components where we have a bunch of key up and key down and where can correct and all of things available for consumption It's totally fine to be sending those out and to have nothing listening for them on the other end.

[00:12:47] It ends up being a no op. So I just wanted to draw a clear distinction between that situation and the situation where yesterday we saw unhandled action outside of a component. Will actually throw an error message telling you nothing is dealing with this.