Ember components is a major feature of Ember.js. Components makes it easy to reuse code, create widgets, tags in or not in the W3C, and much more. Ember's implementation of components is very close to the Web Components specification. This is important since as time passes the Ember implementation will be close to the specification that the web adopts. It's also worth noting that many other projects, like Polymer, X-Tag and Vanilla.js are being used now to help developers create web components as well. However they are not all the same and components made in other frameworks may not work with Ember.
Ember Addons is a website, as the title suggests, that lists Ember CLI specific add-ons that anyone can install via NPM into their projects. In fact when you generate a new project using Ember CLI it adds several Ember add-on packages to your project. Some of the more popular ones are ember-cli-qunit, ember-cli-htmlbars and ember-cli-ic-ajax to name a few. Check out the package.json in your root Ember project folder for more information. Many of these add-ons use Ember components so it's a good resource.
Simple About Page Example
For our first example let's say we are creating a simple about page for our website. We want to create several profiles on this page that displays a picture, name, title, and a link to twitter for each employee. Since we are using bootstrap we also have divs to help position content in a grid.
// app/templates/index.hbs
<div class="col-lg-4 col-sm-6 text-center">
<img class="img-circle img-responsive img-center" src="http://placehold.it/200x200" alt="">
<h3>John Smith
<small>Job Title</small>
</h3>
<a href="http://www.twitter.com/myprofile">Twitter</a>
</div>
We could simply copy and paste this 20 times in our code for each team member however that would violate DRY. So instead we'll create a simple component.
Assuming we already have a Ember project let's use Ember CLI to create the component. (If you're new to Ember check out my Top 5 Best Beginner Tutorials for Ember.js or my Introduction to Ember.js guide to get started)
ember g component about-profile
Be aware all components must have a dash in the name. After it's done generating we'll have a new empty component in our 'app/components/' directory as well as a new template in the 'app/templates/components' directory. Next let's move the html code for our about profile into its own template inside the 'app/templates/components/about-profile.hbs' file. Now we can use the new component inside our index template.
// app/templates/index.hbs
...
{{about-profile}}
Unfortunately this isn't very useful. At this point we might as well just use you a partial template. What we want to do is pass properties into our component.
First let's change our component template so it can receive some properties to display. You can delete the {{yield}} for now.
// app/templates/components/about-profile.hbs
<div class="col-lg-4 col-sm-6 text-center">
<img class="img-circle img-responsive img-center" {{bind-attr src=image}} alt="">
<h3>{{name}}
<small>{{title}}</small>
</h3>
<a {{bind-attr href=twitter}}>Twitter</a>
</div>
The {{name}} and {{title}} are handlebars expressions. They'll be receiving properties from the component. {{bind-attr}} is a handlebars helper. It binds DOM elements with ember attributes.
Note: As of this writing we have to use bind-attr to add attributes. Ember 1.11 will introduce attribute binding. This is a part of the new HTMLBars which is a variant of handlebars. In the above example we'll be able to add in {{twitter}} and {{image}} right in the href and src attributes respectively. I'll update the blog when the new version of Ember arrives.
Next we need to update our index template and pass everything in.
// app/templates/index.hbs
{{about-profile twitter="http://www.twitter.com/erikch" name="John Smith"
title="Engineer I" image="https://placekitten.com/g/200/300"}}
We could always pass properties from our model to our component as well. That should be it. We can just copy and paste this component everywhere in our program and the correct html will appear. All we need to do is change what we pass in.
One other thing worth mentioning is that you can create the component in block form. What that means is that instead of using the simple form we are using now, components can be passed a Handlebars template that is rendered inside a components template wherever the {{yield}} is. Block form is denoted by a '#'.
So in the previous example I could have removed the title tag and put in yield instead.
// app/templates/components/about-profile.hbs
<div class="col-lg-4 col-sm-6 text-center">
<img class="img-circle img-responsive img-center" {{bind-attr src=image}} alt="">
<h3>{{name}}
<small>{{yield}}</small>
</h3>
<a {{bind-attr href=twitter}}>Twitter</a>
</div>
Then I could have wrapped my new about-profile component around some existing html or anything I want.
// app/templates/index.hbs
{{#about-profile twitter="http://www.twitter.com/erikch" name="John Smith"
image="https://placekitten.com/g/200/300"}}
Engineer 20004
{{/about-profile
Now 'Engineer 20004' will be displayed where the {{yield}} was.
The last thing I want to do is add an {{action}} helper. Just for the sake of this example let's say you wanted to display a message any time the user moved their cursor over one of the profiles. Then you wanted the message to disappear after the user cursor was no longer in the profile area.
Let's update the about-profile.hbs file first with the new action helpers.
// app/templates/components/about-profile.hbs
<div class="col-lg-4 col-sm-6 text-center" {{action "over" on="mouseEnter"}} >
<div {{action "out" on="mouseLeave"}}>
{{#if overDiv}}
You are over div now!
{{/if}}
<img class="img-circle img-responsive img-center" {{bind-attr src=image}} alt="">
<h3 >{{name}}
<small>{{yield}}</small>
</h3>
<a {{bind-attr href=twitter}}>Twitter</a>
</div>
</div>
As you can see from above I've added a new {{action "over"}} and {{action "out"}} helpers. To keep it simple I created a second div, since having one div with two action helpers is a little trickier. Normally action helpers are attached to click events, although you can set them to any event. The 'on' signifies what event to listen for and for this case mouseEnter and mouseLeave.
I added a quick if statement using a handlebars expression to display a message if the 'overDiv' property was true.
Next we need to update the 'app/components/about-profile.js' file.
// app/components/about-profile.js
import Ember from 'ember';
export default Ember.Component.extend({
overDiv: false,
actions: {
over: function() {
this.toggleProperty('overDiv');
},
out: function() {
this.toggleProperty('overDiv');
}
}
});
One nice thing about components is that actions will default to the component and won't bubble upwards (although you can send actions back to the controller if needed). I created a new property called overDiv and set it to false. Any action that is sent will toggle that property and that's that.
Twitter Button Example
What if you wanted to create a new tag or widget? Maybe something a little more complicated? Well Ember components allows for that. As you read earlier you can even retrieve actions and either deal with them in the component or send them to your controller to handle.
Let's create a simple twitter tweet button. First per the documentation we need to add this script to our webpage. We can add this script to our index.html or add it to one of our JavaScript files.
//
<script>
window.twttr=(function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],t=window.twttr||{};if(d.getElementById(id))return;js=d.createElement(s);js.id=id;js.src="https://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);t._e=[];t.ready=function(f){t._e.push(f);};return t;}(document,"script","twitter-wjs"));
</script>
To display the tweet button we need an anchor tag with the classname of 'twitter-share-button'. In addition there are several attributes we can add to the anchor tag including data-url, data-text, data-size and data-hashtag which dictates what the tweet will show when someone clicks on it.
Let us begin by generating the component.
ember g component twitter-share
This command will generate the blueprints we need to get started. Next up we'll update the twitter-share.js component file.
// app/components/twitter-share.js
import Ember from 'ember';
export default Ember.Component.extend({
tagName: 'a',
classNames: 'twitter-share-button',
attributeBindings: ['data-size', 'data-url',
'data-text', 'data-hashtags']
});
By default Ember components are div tags if you look in the DOM. There are several properties we can use to change it. In this instance 'tagName' will change the outer element to an anchor tag. We can then add a 'className' css attribute for 'twitter-share-button'. The 'attributeBindings' is used to create more attributes to be applied to the tag.
After this we can now add the twitter-share component into our index template. No need to edit anything else.
// app/templates/index.hbs
...
{{twitter-share data-url='http://www.programwitherik.com'
data-text='Eriks Demo!' data-size='large' data-hashtags='emberjs'}}
There it is. It should render on the page. All the properties should get passed on to the component.
Future
There is a lot more to cover here. I'll be saving the rest for another blog post. If you want some more information sign up for my FREE email newsletter below. For signing up you'll receive a free Ember CLI testing cheat sheet. I'll keep you up to date with my next turoials.