Lesson 16

Enhancing the profile page

5

⚠️ This tutorial uses an old version of Ember. Some things might have changed!

Multiple profiles

You might have noticed that our profile page still has a major problem: it shows only one profile!

Obviously we want to have multiple users on the site and be able to view their profiles as well, not just our own. In this chapter we're going to add a dynamic part to our profile URL (/profile/{username}), and from that username, we'll load the requested user.

Let's first specify the new path in our Ember router-file with :username (the colon specifies that the part is dynamic).

app/router.js

Then, we pick up the username-parameter in our route. Remember that when sending a query-request, Ember will expect an array of values (even it if only contains 1 object), so we need to specify that we want to get only the first object in the array, which should be the only user that matches our username parameter.

app/user/route.js

Finally we need our REST API to support username querying, so let's just add that to our November API:

app/controllers/user/list.js

Boom! Now, instead of having your profile URL as localhost:4200/profile, it will be localhost:4200/profile/t4t5 (or whatever username you chose).

One thing left to fix is that we need to update all the link-to tags that point to the profile page (you now need to supply them with the new username argument). Otherwise, you will get an error like this one when you try to click on any of the profile links:

Below are the changes you need to make to your markup files:

app/application/template.hbs
app/components/profile-glance/template.hbs
app/components/chirps-list/chirp-message/template.hbs

Now that we have multiple users, who each have their own profile page, we can finally add some real following functionality! Let's make a component for our follow-button:

The template for our follow-button will be very simple: if you're not yet following the user, we display a generic "Follow"-button, otherwise we show a red "Unfollow"-button.

app/components/follow-button/template.hbs

The isFollowing property will be set by checking if any of the users in profile.followers matches the logged-in user's id:

app/components/follow-button/component.js

Let's now actually add this component to our profile page and specify that it should not be visible on your own page (because you can't follow yourself dummy)!

app/user/template.hbs
app/user/controller.js

You'll notice now that, if you're logged in as bob, you won't see the follow-button on your own profile page, but you will see it if you visit t4t5's (or any other user's):

To wrap it up, we need to code the follow and unfollow-actions for our button. Note that these request won't be fired through Ember Data, but are just standard jQuery AJAX calls. Therefore, we have to add some of the adapter functionality that we specified earlier manually (e.g. making sure the API host is localhost:9000 and appending the Bearer token as a request header).

app/components/follow-button/component.js

As you can see, we use a custom AJAX URL in the same way as with the signup-action we created a few chapters earlier.

When clicking on the button, a request will either be sent to localhost:9000/follow or localhost:9000/unfollow.

Let's quickly generate the November actions for these requests:

app/actions/follow.js
app/actions/unfollow.js

Fixing the "following" and "followers" subroutes

The routes user.following and user.followers are right now always returning all of the existing users (and not just the followers or followees) because we're using this.store.findAll('user').

We want to change this to a request with a query that only returns the followers and followees.

We'll fix the queries by updating the route-files:

app/user/following/route.js
app/user/followers/route.js

Now we just need to add a few lines to our REST endpoint in November, right after the rest of the if-statements. We'll also update all of our if-statements to make sure that we sideload the chirp, followees and followers relationships whenever we retrieve a user (so that their number of followers and followings gets computed correctly from the start).

app/controllers/user/list.js

In the next chapter, we're going to look at another important best practice when developing an application, namely testing!

I experience some odd behavior: when I reload the profile page the chirps disappear. Navigating via link keeps the chirps list visible.

So, I'm getting a lot of Uncaught Error: Assertion Failed: You looked up the 'followees' relationship on a 'user' with id 2 but some of the associated records were not loaded. Either make sure they are all loaded together with the parent record, or specify that the relationship is async (DS.hasMany({ async: true }) ) style issues (had the same thing for chirps) -- did I miss something in terms of async issues?

Your top of codeblock path has been very helpful, but maybe for the final set of lessons should specify ember vs november...

You have a "dangling" comma there at the end of line 10.

Nice, thanks Daniel

Just an observation here. I have found that many of these useful methods such as isAny() and some others are not in the guides on the emberjs.com/api page.

Anyone know why this is? Or am I just looking in the wrong place?

I had another look - here it is if anyone else is interested:

http://emberjs.com/api/classes/Ember.Enumerable.html#method_isAny

Should the argument resp be res in this case?

If you check the mysql logs, this actually builds a statement that only checks on followee_id. To fix this, you can replace the "follow.find" with "follow.destroy" and remove the following .then() block.

@danieljeffery: Actually, that argument is the response of follow.destroy() (I believe it's a simple boolean that indicates whether the destroy was successful or not). We're not actually using it though, so you might as well omit it.

Not really sure where this session.currentUser.username is coming from? Presumably it relates to November but isn't explicitly gone over in the tutorial?

@danieljeffery: session.currentUser comes from the Simple Auth library. It's set in app/initializers/session.js

what happened to the sessionAccount property?

@nicholas: My bad, I forgot to update that part of the code. It's fixed now! :)

I believe you are also missing the isFollowing property from before. In addition, since you added the authorizer in the last section which appends the token to all requests, you don't need the session and accessToken properties and the ajax headers.