Refactoring to a Component
Published on May 20, 2014
Refactoring our new and edit post pages so their shared functionality is contained within a component.
Hello and welcome back. Today we're going to be refactoring what we learned last time in to a component.
You'll notice that there are lots of similarities. First we have the same four fields and then we have a button that calls the action 'done editing' when you hit it. Here we have an action that saves and then does something else. Just like here we have an action that saves and then does something else.
When you have this pattern of having some template and some code that's connected and similar across two different parts of your app, it might be time for a component because components extract away shared functionality, shared units of functionality. It consists of a class and template, although it will generate the other one of those if you only create one.
If you're familiar with Cocoa, you'll notice the Ember Components are very similar to the Cocoa ViewControllers and if you're familiar with Angular, you'll notice that they are exactly the same as an Angular directive that is isolate-scoped, bidirectionally-bound, transcluded and element-restructured. We'll go one by one over what those mean.
Here we're calling a component called Post Form. We haven't created it yet but let's go ahead and fill out some of what the calling will look like and what variables we're going to give it.
So its isolated which means the scope that is here in this template where we have things like title is not going to carry through to the post form.
Luckily it's also bidirectionally-bound, so it has two-way bindings. So if we do this than it's saying that what's the model here, the thing that we're pulling title and author from because title is just short hand for model.title. We're going to call it post within the component.
Transcluded has some technical meanings but for the purposes of components, it basically means that you could put custom content into the middle of Component so it will look like this. You'll notice that this looks similar to how a Link to is formed. We won't need that today for this feature so we won't talk more about it.
Finally, element restricted means that the component will end up being an HTML element. It's a DIV by default.
Now, let's create the mark-up for our template. I'm putting in a file called postForm.handlebars. It's in the components directory of the templates directory. What I'm going to do first is just cut and paste from the new handlebars and then I'm going to edit to make up for the scope change, because 'model' is now called 'post' and it's no longer implicit.
Let's go ahead and do this in the editing form as well. When we go to edit our post we'll see that all fields are still there but clicking the 'done' button does nothing because nothing is yet handling that action. Yes, there is this 'doneEditing' action in post.js. However since the component is isolate scoped, it's going to throw away the action before it can get to the post controller so we'll create a new file in the components directory called 'post-form.js'. We're going to be creating something that extends an Ember component. For now all we'll have on it is an actions hash with the 'doneEditing' action. Let's go ahead and test this working by logging out a simple and after it reloads we'll go and edit and hit done and it's logged.
So now we need to put in the action that we want. You'll notice that things that this and this have in common are that they save the model and then they do something else. We're going to be copying the form of this because it is more general than the form here. Here the order of this is not important. However, here it is important that controller.transition.route happens only after the post is done saving. So we'll use the more general case rather than the specific case where the order doesn't matter.
So we're going to save it, and then get our promise, and inside the promise what do we do? Well it's different on each of these. On new it's transitioning to the post route, but when you're within the post, it's just changing isEditing to false, so what we'll need to do is here we're going to pass in an 'afterSave' action, a variable, and this is going to be doneEditing, and we'll go ahead and do that here as well.
The 'afterSave' that you're passing in happens to be called the same thing, however, it is doing something different and we'll go ahead and strip these down to their most basic, the things that are unique to them. We'll go ahead and do that with this one as well. And when we go back to post form we will just call 'sendAction' and then we'll give it the 'afterSave' action and pass in the post.
If we go to edit a post, we will change this up and then see that it has, indeed, been changed and it's going back to the non-editing state. Then when we create a new post we will try this as well and it transitions to the post page.
I'm noticing that I cargo-culted the variable name controller from our controller action and that implies that this is the controller scope. It's not. It's the component scope. Of course, Ember didn't care because it's something that we're just using for human readability, but for future users, naming it something like this is very helpful.
So now you know how to change part of your code into a component. You can now put a post-form anywhere in your app and it will function very similarly to how our current two post forms work.
Now have a great week and good luck using this in your own apps.
If you're still watching this on YouTube, I have a site now and you can look at all the Spark casts as well as sort them by topic. I hope that's helpful to you and I'll see you next week.