A web interface for following users

“The Relationship model Section” placed rather heavy demands on our data modeling skills, and it’s fine if it takes a while to soak in. In fact, one of the best ways to understand the associations is to use them in the web interface.

In the introduction to this chapter, we saw a preview of the page flow for user following. In this section, we will implement the basic interface and following/unfollowing functionality shown in those mockups. We will also make separate pages to show the user following and followers arrays.

Sample following data

As in previous chapters, we will find it convenient to use the seed data to fill the database with sample relationships. This will allow us to design the look and feel of the web pages first, deferring the back-end functionality until later in this section.

Stats and a follow form

Now that our sample users have both followed users and followers, we need to update the profile page and Home page to reflect this. We’ll start by making a partial to display the following and follower statistics on the profile and home pages. We’ll next add a follow/unfollow form, and then make dedicated pages for showing “following” (followed users) and “followers”.

Following and followers pages

Our first step is to get the following and followers links to work. We’ll follow Twitter’s lead and have both pages require user login. As with most previous examples of access control, we’ll write the tests first

public/test/e2e_test/controllers/users_controller_test.js

describe('usersControllerTest',function(){...it('should redirect index when not logged in',function(){varcurrent_url='http://localhost:1337/#/users';browser.get(current_url);expect(element.all(by.css('.alert-danger')).count()).toEqual(1);expect(browser.getCurrentUrl()).toContain('#/login');})it('should redirect following when not logged in',function(){varcurrent_url='http://localhost:1337/#/users/1/following';browser.get(current_url);expect(element.all(by.css('.alert-danger')).count()).toEqual(1);expect(browser.getCurrentUrl()).toContain('#/login');})it('should redirect followers when not logged in',function(){varcurrent_url='http://localhost:1337/#/users/1/followers';browser.get(current_url);expect(element.all(by.css('.alert-danger')).count()).toEqual(1);expect(browser.getCurrentUrl()).toContain('#/login');})...});

The only tricky part of the implementation is realizing that we need to add two new actions to the Users controller.

To get the follow and unfollow buttons to work, all we need to do is find the user associated with the followed_id in the corresponding form, and then use the appropriate follow or unfollow method

app/controllers/relationships_controller.js

varsessionHelper=require('../helpers/sessions_helper.js');functionRelationshipsController(){this.before_action=[{action:'logged_in_user'}];this.create=function(req,res,next){varcurrent_user=sessionHelper.current_user(req);varuser=ModelSync(User.findById(req.body.followed_id));if(user){res.end(JSON.stringify(ModelSync(current_user.follow(user))));}else{res.end(JSON.stringify({error:'User not found'}));}};this.destroy=function(req,res,next){varcurrent_user=sessionHelper.current_user(req);varuser=ModelSync(User.findById(req.params.id));if(user){res.end(JSON.stringify(ModelSync(current_user.unfollow(user))));}else{res.end(JSON.stringify({error:'User not found'}));}};}module.exports=RelationshipsController;

Following tests

Now that the follow buttons are working, we’ll write some simple tests to prevent regressions. To follow a user, we post to the relationships path and verify that the number of followed users increases by 1