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:


Uh-oh.


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


To test that it works, you can log out, create a new user and go to the profile page. You should then be able to visit both /profile/t4t5 and /profile/bob


Adding a "follow"-button

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):


There's our button!


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


Now the Follow-button should work! (at least the first time)


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.


There's obviously a mis-match at the moment. The stats say 1 follower, but the page shows 2 (all of the existing users)


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


Before and after sideloading. Now we get all of the relational data as well!



That's the right data!


Alright! Now our app is actually more or less finished. We can sign up, log in, post, view profiles and all the right data is loaded!


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

Comments

profile/avatar/default
Andreas

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

Kirk Israel

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?

Kirk Israel

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

Andy Pickler

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

profile/avatar/default
Tim

Nice, thanks Daniel

Daniel Jeffery

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?

Daniel Jeffery

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


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

Daniel Jeffery

Should the argument resp be res in this case?

profile/avatar/default
Nicholas

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.

Tristan Edwards

@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.

Daniel Jeffery

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?

Tristan Edwards

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

profile/avatar/default
Nicholas

what happened to the sessionAccount property?

Tristan Edwards

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

profile/avatar/default
Nicholas

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.