summaryrefslogtreecommitdiff
path: root/_posts/2012-12-13-your-ember-app-is-a-click-away-from-home.markdown
blob: 70a63e3f92011ff948ad3d4770b9f051ec237dc1 (plain)
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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
---
layout: post
title: Your Ember app is a click away from home
tags: [ ember, development ]
published: true
---
I've been having some time to explore Ember.js lately and I wanted to learn by
doing instead of just reading about it. So bear with me as this might probably
require some updating in the future. This is a result of trial and error and I
thought of making the Ember learning curve a little easier and share this tip.

So I've been trying to build a simple application that manages pictures. I
started the good ol' fashioned way of manually calling all the scripts and
declaring all the templates from the index page, for argument's sake. I would
much rather have something like
[Iridium](http://github.com/radiumsoftware/iridium) to help with my development
process but I had faced some trouble with it before.

My first step: list of pictures
-------------------------------

The first thing that came to my mind was to have a list of pictures and allow
the user to add a new one by clicking on a link that would transition to
something like `/pictures/new`. In the pictures list template I would have
something like:

{% highlight html %}
<h2>Images</h2>
{%raw%}{{#each image in controller}}
  <li>{{image.title}}</li>
{{/each}}
<p>
  <a {{action newImage href="true"}}>New image</a>
</p>
{%endraw%}
{% endhighlight %}

The link in the paragraph goes to the route transition in "root". Let me give
you the router so you can better understand what I'm saying:

{% highlight javascript %}
Exhibit.Router = Ember.Router.extend({
  root: Ember.Route.extend({
  , index: Ember.Route.extend({
      route: "/"
    , connectOutlets: function(router, context) {
        router.get("applicationController").connectOutlet("images", Exhibit.Image.all());
      }
    , newImage: Ember.Route.transitionTo("newImage")
    })
    , newImage: Ember.Route.extend({
        // ...
      })
  })
});
{% endhighlight %}

Inside the `index` route, the `newImage` path points to the `newImage` route
which will print a form.

The challenge: Going back home
------------------------------

At this point in time, I was curious as to the possibility of aborting the
process of creating a new image and simply going back home and start over. As I
was learning, I realized that actions in the templates point to routes in the
router as a way to acknowledge an application state change. I faced the
challenge of wanting to leave the form and go back and not being able to
do it.

I recalled that actions should be defined in the state (the route) the
application is in. Clicking on the `newImage` link transitions my app to the
`root.newImage` state. So I thought of doing something like this in the router:

{% highlight javascript %}
Exhibit.Router = Ember.Router.extend({
  root: Ember.Route.extend({
  , index: Ember.Route.extend({
      // ...
    })
    , newImage: Ember.Route.extend({
        route: "/new"
      , connectOutlets: function(router, context) {
          // connect the outlet
        })
===> , goHome: Ember.Route.transitionTo("root.index")
    })
  })
});
{% endhighlight %}

This looks rather trivial and probably won't make the point alone. What I
thought at the time when I wrote this was _should I really need to write this
transition in every single route?_ After all, going back home should be
possible for every action, every state in the app.

The solution: Use the state machine
-----------------------------------

After some trial and error, I ended up with this:

{% highlight javascript %}
Exhibit.Router = Ember.Router.extend({
  root: Ember.Route.extend({
=>  goHome   : Ember.Route.transitionTo("root.index")
  , index    : Ember.Route.extend({ /* ... */  })
  , newImage : Ember.Route.extend({ /* ... */ })
  })
});
{% endhighlight %}

Since the router is a state machine, when defining the `root` route, I'm able
to add state changes there that will be inherited for every sibling state in
that tree. Both `index` and `newImage` and all the routes that are defined in
`root` will have this state change available.

This was a finding for me as I have never grasped the concept of state machines
like this.