Showing users

In this section, we’ll take the first steps toward the final profile by making a page to display a user’s name and profile photo. Our eventual goal for the user profile pages is to show the user’s profile image, basic user data, and a list of microposts. We’ll complete this task, and with it the sample application, in “Following users” Chapter.

A Users resource

In order to make a user profile page, we need to have a user in the database. Happily, this problem has already been solved: in previous post, we created a User record by hand using the console

> require('trainjs').initServer()
> User.count().then(function(data){ console.log(data) })
1
> User.findOne().then(function(data){ console.log(data) })
{ dataValues:
   { id: 1,
	 name: 'Dang Thanh',
	 email: '[email protected]',
	 password_digest: '$2a$10$7gVwYayQoKFSEkJfobhAne4EjC52Djt7x1cl9cponFtAn.zVtfP0e',
	 createdAt: Fri Jan 29 2016 12:30:00 GMT+0700 (ICT),
	 updatedAt: Fri Jan 29 2016 12:30:00 GMT+0700 (ICT) },
	 ...

When following REST principles, resources are typically referenced using the resource name and a unique identifier. What this means in the context of users—which we’re now thinking of as a Users resource—is that we should view the user with id 1 by issuing a GET request to the URL /users/1.

We can get the routing for /users/1 to work by running a single command

~/sample_app $ trainjs generate service User show
	  create  app/controllers/users_controller.js
	  create  public/services
	  create  public/services/user.js

The result appears in config/routes.js

module.exports = [
	{ resources: 'users' }
];

We’ll use the standard trainjs location for showing a user, which is public/partials/users/show.html

{{ user.name }}, {{ user.email }}

public/app.js

'use strict';

var sampleApp = angular.module('sampleApp', [
	'ui.router',
	'userService',
	'usersController',
	'staticPagesController',
	'bodyDirective',
	'headDirective'
]);

sampleApp.config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider) {
	$urlRouterProvider.otherwise('/home');
	$stateProvider
	...
	.state('user_detail', {
		url: '/users/:id',
		templateUrl: 'partials/users/show.html',
		resolve: {
			user: ['$q', '$stateParams', 'User', function($q, $stateParams, User){
				var deferred = $q.defer();
				User.get({id: $stateParams.id}, function(user) {
					deferred.resolve(user);
				}, function(error) {
					deferred.reject();
				});
				return deferred.promise;
			}]
		},
		controller: 'UsersDetailCtrl'
	})
}]);
...

In order to get the user show view to work, we need to define an user variable in the corresponding show action in the Users controller.

app/controllers/users_controller.js

function UsersController() {
	this.show = function(req, res, next) {
		var user = ModelSync( User.findById(req.params.id) );
		res.end(JSON.stringify(user));
	};
};

module.exports = UsersController;

public/controllers/users_controller.js

'use strict';

var usersController = angular.module('usersController', []);

usersController.controller(
	'UsersNewCtrl',
	['$scope', function ($scope) {
	}]
);

usersController.controller(
	'UsersDetailCtrl',
	['$scope', '$rootScope', 'user', function ($scope, $rootScope, user) {
		$rootScope.provide_title = user.name;
		$scope.user = user;
	}]
);

A Gravatar image and a sidebar

Gravatar is a free service that allows users to upload images and associate them with email addresses they control. Our plan is to define a gravatar_for directive for a given user.

public/partials/users/show.html

<h1>
	<img gravatar_for="{{ user.email }}" alt="{{ user.name }}" />
	{{ user.name }}
</h1>

public/index.html

...
<script src="directives/body.js"></script>
<script src="directives/head.js"></script>
<script src="directives/gravatar_for.js"></script>
...

public/app.js

'use strict';

var sampleApp = angular.module('sampleApp', [
	'ui.router',
	'userService',
	'usersController',
	'staticPagesController',
	'bodyDirective',
	'headDirective',
	'gravatarForDirective'
]);
...

public/directives/gravatar_for.js

var gravatarForDirective = angular.module('gravatarForDirective', ['angular-md5']);

gravatarForDirective.directive('gravatarFor',['md5', function(md5) {
	return {
		restrict: 'A',
		link: function(scope, elem, attrs) {
			var gravatar_id = md5.createHash(attrs.gravatarFor.toLowerCase());
			var gravatar_url = "https://secure.gravatar.com/avatar/" + gravatar_id;
			elem.attr('src', gravatar_url);
		}
	};
}]);

As noted in the Gravatar documentation, Gravatar URLs are based on an MD5 hash of the user’s email address. In the demo, the MD5 hashing algorithm is implemented using the angular-md5 library

~/sample_app $ npm install angular-md5 --save

public/index.html

...
<script src="libs/angular.min.js"></script>
<script src="libs/angular-ui-router.min.js"></script>
<script src="libs/angular-resource.min.js"></script>
<script src="../node_modules/angular-md5/angular-md5.min.js"></script>
...

The profile page appears above, which shows the default Gravatar image, which appears because [email protected] isn’t a real email address.

showing users 1

To get our application to display a custom Gravatar, we’ll use update to change the user’s email to something I control

> require('trainjs').initServer()
> var user;
> User.findOne().then(function(data){ user = data })
> user.update({ name: "Example User", email: "[email protected]", password: "foobar", password_confirmation: "foobar" })

Here we’ve assigned the user the email address [email protected], which I’ve associated with the Rails Tutorial logo

showing users 2

We include row and col-md-4 classes in the user show page public/partials/users/show.html

<div class="row">
	<aside class="col-md-4">
		<section class="user_info">
			<h1>
				<img gravatar_for="{{ user.email }}" alt="{{ user.name }}" />
				{{ user.name }}
			</h1>
		</section>
	</aside>
</div>

public/assets/stylesheets/custom.css

/* sidebar */
aside section.user_info {
  margin-top: 20px;
}

aside section {
  padding: 10px 0;
  margin-top: 20px;
}

aside section:first-child {
  border: 0;
  padding-top: 0;
}

aside section span {
  display: block;
  margin-bottom: 3px;
  line-height: 1;
}

aside section h1 {
  font-size: 1.4em;
  text-align: left;
  letter-spacing: -1px;
  margin-bottom: 3px;
  margin-top: 0px;
}

.gravatar {
  float: left;
  margin-right: 10px;
}

.gravatar_edit {
  margin-top: 15px;
}

showing users 3