holy moly

pangratz prattles

GitHub Dashboard #11

Goal of this iteration: Add Dashboard.User

By know no information about the user is shown. Let’s change this. First I created a new model Dashboard.User and added some attributes:

app/lib/model.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Dashboard.User = DS.Model.extend({
  primaryKey: 'login',
  login: DS.attr('string'),
  avatar_url: DS.attr('string'),
  name: DS.attr('string'),
  blog: DS.attr('string'),
  email: DS.attr('string'),
  bio: DS.attr('string'),
  public_repos: DS.attr('number'),
  followers: DS.attr('number'),
  following: DS.attr('number'),
  html_url: DS.attr('string'),
  created_at: DS.attr('string')
});

There is already a Dashboard.UserController. But at the moment it extends Ember.Controller. I change this to Ember.ObjectController so we can set the content of the controller to a specific Dashboard.User instance and everything is proxied to the content. To get a specific user the find method on the GitHubAdapter needs to be modified accordingly:

app/lib/github_adapter.js
1
2
3
4
5
6
7
8
9
10
11
find: function(store, type, id) {
  if (Dashboard.Repository.detect(type)) {
    this.ajax('/repos/%@'.fmt(id), function(data) {
      store.load(type, id, data);
    });
  } else if (Dashboard.User.detect(type)) {
    this.ajax('/users/%@'.fmt(id), function(data) {
      store.load(type, id, data);
    });
  }
}

Alright, everything is basically set up to retrieve a user: let’s use it in the router and adapt the templates accordingly. Currently the route to a user is specified as /:username, and in the connectOutlets it is retrieved via context.username. If we change this to /:user_id Ember.js assumes that we are referencing a Dashboard.User model. It will automatically find this user with the passed id via Dashboard.User.find() and pass this model as context to the connectOutlets. The router therefore can be adjusted like this:

app/lib/router.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Dashboard.Roouter = Ember.Router.extend({
...
  user: Ember.Route.extend({
    route: '/:user_id',
    connectOutlets: function(router, user) {
      router.set('userController.content', user);
    },
    index: Ember.Route.extend({
      route: '/',
      connectOutlets: function(router) {
        var username = router.get('userController.id');
        ...
    }),
    repository: Ember.Route.extend({
      route: '/:repository',
      connectOutlets: function(router, context) {
        var username = router.get('userController.id');
        var repository = context.repository;
        ...
      }
    })
    ...
  })
...

Because the user is fetched asynchronously, it may not be available already in the sub-routes index and repository. So router.get('userController.login') may be null. To circumvent this we get the username via router.get('userController.id'). This works since the primaryKey in the Dashboard.User model has been set to login and it seems that id is set when Ember.js resolves the /:user_id route. I’m honestly not sure if this is the intended solution and I will investigate in the correct way of doing this and update the code.

Last step to show the user: add and modify the existing templates. I created a user template which shows the information for a specific user. It also has a watchedRepositories outlet which get’s connected with the watchedRepositories:

lib/templates/user.handlebars (user.handlebars) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<div class="well" >
    <div class="profilePic" >
        <img width="40" height="40" {{bindAttr src="avatar_url"}} ></img>
    </div>
    <div>
        <div>
            <strong>{{login}}</strong> - {{name}}
            {{#if blog}}
                - <a {{bindAttr href="blog"}} >{{blog}}</a>
            {{/if}}
        </div>
        <div>
            <span class="badge" ><i class="icon-inbox" ></i> {{public_repos}} public repos</span>
        </div>
    </div>
    <div style="clear: both;" ></div>
</div>
<div>
    <h3>Watched repositories</h3>
    {{outlet "watchedRepositories"}}
</div>

This post concludes with the final implementation of the router:

app/lib/router.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
Dashboard.Router = Ember.Router.extend({
  root: Ember.Route.extend({

    index: Ember.Route.extend({
      route: '/',
      connectOutlets: function(router) {
        router.transitionTo('user.index', Dashboard.User.find('pangratz'));
      }
    }),

    user: Ember.Route.extend({
      route: '/:user_id',
      connectOutlets: function(router, user) {
        router.set('userController.content', user);
      },

      index: Ember.Route.extend({
        route: '/',
        connectOutlets: function(router) {
          var username = router.get('userController.id');
          var store = router.get('store');

          // get watched repositories for given username
          var watchedRepositories = store.findQuery(Dashboard.Repository, {
            username: username,
            type: 'watched'
          });

          // set watched repositories on repositoriesController
          router.set('repositoriesController.content', watchedRepositories);

          // connect user and watched repositories
          router.get('applicationController').connectOutlet('user');
          router.get('userController').connectOutlet('watchedRepositories', 'repositories', watchedRepositories);
        }
      }),

      repository: Ember.Route.extend({
        route: '/:repository',
        connectOutlets: function(router, context) {
          var username = router.get('userController.id');
          var repoName = context.repository;

          // fetch repo for current user
          var repo = router.get('store').find(Dashboard.Repository, '%@/%@'.fmt(username, repoName));
          router.set('repositoryController.content', repo);

          var events = router.get('store').findQuery(Dashboard.Event, {
            username: username,
            repository: repoName
          });

          // connect repository and events
          router.get('applicationController').connectOutlet('repository');
          router.get('repositoryController').connectOutlet('events', 'events', events);
        }
      }),

      showUser: function(router, evt) {
        var username;
        var context = evt.context;

        // context is a Dashboard.RepositoryController if this action
        // is called from repository template --> this needs to be fixed
        if (Dashboard.RepositoryController.detectInstance(context)) {
          username = context.get('owner.login');
        } else {
          username = context;
        }

        router.transitionTo('user.index', Dashboard.User.find(username));
      },

      showRepository: function(router, evt) {
        // context is the full_name of a repository: username/repository
        var split = evt.context.split('/'),
            username = split[0],
            repository = split[1];

        router.transitionTo('user.repository', Dashboard.User.find(username), {repository: repository});
      }
    })
  })
});

Roundup

A new model Dashboard.User has been introduced. The existing /:username route has been modified to /:user_id so Ember.js can derive which model shall be fetched and automatically set it as context in the route’s connectOutlets method.

The result of this post’s changes are available at tag v0.0.12 (changes). As always, the result is deployed at code418.com/dashboard.

Comments