⚠️ This tutorial uses an old version of Ember. Some things might have changed!
In this chapter, we'll put together all the knowledge that we have gained about templating, routes, components and Ember Data by creating the profile page!
Templating with Handlebars and CSS
Let's start by creating the basic markup and style that we need based on our mockups. We'll use only static data for now.
For the HTML-page, we'll just add some layout-tags and add an
aside-part with the user info.
Let's fix it by sprinkling on some CSS magic. Create a new file called
profile.scss in your
Alright, that was just the basic HTML/CSS stuff. Now, on to the real fun: the Ember part.
Adding the profile data
Just like in "home" we want the user's data to appear on the profile page.
First of all, looking at our mockups, we can see that we lack two attributes in our user-model:
The "about me" description
The date the user joined the service
Let's add these! We'll also add a
chirps-attribute as a relationship that references the ids of the user's chirps, so that we can easily access them from the user object.
Of course, we also need to update our mock data in that case:
Okay, now we have the data there. Remember what we need to do in order to fetch the data? That's right, update the route-file!
Next, we update the Handlebars-file to render the data in our template.
Try to do this on your own first, and peek at the code below if you get stuck (note: we only show the parts of the file that need to be updated).
Let's move on to our subroutes: chirps, following and followers.
Loading the referenced chirps
Let's start with the data that we want to load in the chirps-subroute. Here we can do something nifty. Since we can already access the user's chirps through the parent route's model (with
model.user.chirps), we'll just use a method called
modelFor() to fetch the
user route's model, and then get the chirps from there:
Going back to the browser, you'll now see, uh-oh, an error message again!
As you can see, our GET-requests for fetching specific chirps haven't been defined in the
mirage/config.js-file yet, which makes the app crash. No worries, just like we did with our user, we add this line of code to give Mirage the ability to fetch a specific chirp record:
Back to our template. Remember that we made a
chirps-list-component for the Home-page before? One of the great things with components is that they are reusable. And since the list of chirps on the profile-page looks exactly the same as the list of chirps on the home-page, it makes sense to just use the same component to render both!
Here we truly see the power of components. We only had to pass in our new route-data to the component, and the logic, markup and styling just comes for free thanks to our earlier work!
Let's move on to the following and followers subroutes.
Followees and followers
Right now in our Fixture data, we actually don't have anything for linking up a user's followers and followees. Let's add that.
Remember: a user can both follow many users, and be followed by many users. Therefore, we need two
hasMany()-relationships in our user model.
Since we only have one user right now, our mock data user will only follow (and be followed by) himself. Notice that when using
hasMany() we have to use an array with the user-IDs.
Back to the browser. Oh no. Blank page. What happened? Let's check the console.
The inverse relationship between our attributes in this case is pretty simple: the inverse of a follower is a followee, and the inverse of a followee is a follower:
That fixed the error! Now we need to update our two subroutes to fetch that data.
Right now, we'll just make these routes fetch all the users we have in our fixture data (without filtering). Don't worry, we'll improve this once we build our real REST API. The goal at the moment is just to make sure that the templates and styles render well.
Since we're using
findAll(), we also need to add the corresponding REST URL to our Mirage config-file:
And finally we update the route's template. Again, we'll use a component that we've already spent some time creating: the
Final touches with computed properties
One problem we still have with our current user model is that there's no relation between the chirps/followers/following and the number of them that we see in the profile glance.
For example, in our fixture data, we say that our user has 5 followers (in
numberOfFollowers), although the actual relationship-data only contains one single user-ID (in
Wouldn't it be great if our
numberOfFollowers-property was automatically deduced from the length of the
followers array? Well, once again, we can easily achieve that through computed properties.
With computed properties, you can create new attributes for your model based on one or many of the other attributes your model has. So let's replace the attributes
numberOfFollowing with computed properties!
Also, since we're using the
Ember-keyword, we need to import the Ember library at the top of the file with:
The first argument of
Ember.computed() tells Ember what property to observe in order to calculate the computed property. In this case for example, we've decided that as soon as
numberOfFollowers should change as well.
We can actually do the same thing for our
chirps attribute! Since we already have a relationship between our user model and the chirp model, all we need is a computed property there as well.
Your final model-file should now look something like this:
As you can see, we don't have any raw numbers anymore, everything is computed on the fly on the client side.
This has some great advantages. Let's look at what would happen if we were to post a new chirp for example. We'll simulate it by updating the fixture data for our chirp model:
Also make sure that you reference that record (with ID 3) in your user fixture:
You now see all three chirps in the user's profile and, lo and behold, the number of chirps has automatically been updated to 3!
It is important to note that, even though the whole page was refreshed to show the changes in this particular example (because we're using fixture data), this kind of change will be instant and reflected in your UI as soon as the model data changes.
It's really a great feeling not to have to worry about updating all your data in the UI manually as soon as some data changes!
Great, we now have two pages that look pretty good. In the next chapter we're going to take a closer look at how to import different kinds of third-party libraries and use them in our Ember app.