In the previous tutorial I showed how to configure both the server and the client side parts of Discourse to respond to a request.
We now recommend you to read the Ember component documentation: Introducing Components - Components - Ember Guides
Old tutorial
In this tutorial, I’m going to create a new Ember Component as a way to wrap third party Javascript. This is going to be similar to a YouTube video I made a while back, which you may find informative, only this time it’s specific to Discourse and how we lay out files in our project.
Why Components?
Handlebars is quite a simple tempting language. It’s just regular HTML along with some dynamic parts. This is simple to learn and great for productivity, but not so great for code re-use. If you’re developing a large application like Discourse, you’ll find that you want to re-use some of the same things over and over.
Components are Ember’s solution to this problem. Let’s create a component that will display our snack in a nicer way.
Creating a new Component
Components need to have a dash in their name. I’m going to choose fancy-snack
as the name for this one. Let’s create our template:
app/assets/javascripts/admin/templates/components/fancy-snack.hbs
<div class='fancy-snack-title'>
<h1>{{snack.name}}</h1>
</div>
<div class='fancy-snack-description'>
<p>{{snack.description}}</p>
</div>
Now, to use our component, we will replace our existing admin/snack
template with this:
app/assets/javascripts/admin/templates/snack.hbs
{{fancy-snack snack=model}}
We can now re-use our fancy-snack
component in any other template, just passing in the model as required.
Adding Custom Javascript Code
Besides re-usability, Components in Ember are great for safely adding custom Javascript, jQuery and other external code. It gives you control of when the component is inserted into the page, and when it is removed. To do this, we define an Ember.Component with some code:
app/assets/javascripts/admin/components/fancy-snack.js.es6
export default Ember.Component.extend({
didInsertElement() {
this._super();
this.$().animate({ backgroundColor: "yellow" }, 2000);
},
willDestroyElement() {
this._super();
this.$().stop();
}
});
If you add the above code and refresh the page, you’ll see that our snack has an animation of a slowly fading yellow background.
Let’s explain what’s going on here:
-
When the component is rendered on the page it will call
didInsertElement
-
The first line of
didInsertElement
(andwillDestroyElement
) isthis._super()
which is necessary because we’re subclassing Ember.Component. -
The animation is done using jQuery’s animate function.
-
Finally, the animation is cancelled in the
willDestroyElement
hook, which is called when the component is removed from the page.
You might wonder why we care about willDestroyElement
at all; the reason is in a long lived Javascript application like Discourse it’s important to clean up after ourselves, lest we leak memory or leave things running. In this case we stop the animation, which tells any jQuery timers that they needn’t fire any more as the component is no longer visible on the page.
Where to go from here
The final tutorial in this series covers automated testing.
This document is version controlled - suggest changes on github.
Last edited by @JammyDodger 2024-05-26T08:04:39Z
Check document
Perform check on document: