---
title: Using Sammy for changing page state
teaser: 'Introduction to Sammy, a really neat Javascript framework, loosely based
  on Ruby''s Sinatra framework.

  '
tags: javascript,new bamboo,web
author: Max Williams
published_on: 2009-10-14
---

_This post was originally published on the New Bamboo blog, before [New Bamboo
joined thoughtbot in London][new-bamboo-thoughtbot]._

---

In case you haven't seen [Sammy.js] yet, it is a really neat Javascript
framework, loosely based on the very lightweight Ruby web framework, Sinatra.

Sammy allows you to specify 'routes' in your JS which have associated behaviour
attached to them. This means that it is possible to create single-page
applications on the client-side, that can easily switch between different states
depending on what the user is doing. Your Rails application can therefore serve
up json in response to certain requests, and the view state will be handled on
the client.

It took me a while to figure out whether I thought this was useful or not, but I
have now come down firmly on the side of yes (some caveats aside). This is
because it allows you to declaratively specify different states, and quickly
jump between them by using only the window's location (specifically the anchor
bit).

The simplest way of demonstrating how it can be used is a tab-based layout. This
means that given the following html (assuming that Sammy and JQuery have been
loaded up elsewhere):

```html
...
<p>
  <a href="#/tab_1">Tab 1</a>
  <a href="#/tab_2">Tab 2</a>
</p>

<div>
  <div class="section" id="tab_1_section">
    <p>hello from tab 1</p>
  </div>

  <div class="section" id="tab_2_section">
    <p>hello from tab 2</p>
  </div>
</div>
...
```

and the following JS:

```javascript
...
;(function($) {
  var app = new Sammy.Application(function() {
    with(this) {
      get('#/tab_1', function() { with(this) {
        $('.section').hide();
        $('#tab_1_section').show();
      }});

      get('#/tab_2', function() { with(this) {
        $('.section').hide();
        $('#tab_2_section').show();
      }});
    }
  });
  $(function() {
    app.run('#/tab_1')
  });
})(jQuery);
...
```

you will get a couple of tabs, with minimal effort.

Looking at the Sammy routes it is trivial to work out what all the states on the
page are, and you don't get them spattered around in views, and buried in
Javascript files. To change state, you write simple links with anchors, and
there is no need to bind complex event handlers to the page.

This simple app could be made more interesting if we changed the second tab to
be a list of a user's tweets. You wouldn't necessarily want to load them up when
the page first loaded, as they could be stale by the time someone switched tabs
(hypothetically speaking).

You can therefore change the route to:

```javascript
get('#/tab_2', function() { with(this) {
    $('.section').hide();
    // method declared somewhere else which pulls the tweets down and inserts them into the html of the page
    get_and_display_latest_tweets_on_tab_2();
    $('#tab_2_section').show();
}});
```

These are pretty lame examples, where the use of Sammy is rather overkill.
However, in a real application which is more complex, it is easier to see the
benefits.

## Adding modal dialogs with Sammy

I recently encountered a use case where I combined Sammy with a JQuery plugin
called [Block UI].

What I wanted to do was:

* present a user with a list of the projects in the system
* allow them to click a link to add a new project
* allow them to enter the new project details into a modal dialog
* close the dialog, insert the new project into the list and display a message
  to them

Sammy makes it easy to break these different actions into their component parts
and organise them in a meaningful way.

To accomplish this, we first define some routes:

* `#/`
* `#/new\_project`
* `#/create\_project`

These are the various states of the page we will be using. Additionally, you
will notice that create\_project is defined as a 'post' request. This means that
you can easily capture the data from a form in your page without sending people
elsewhere when they hit "submit" (by setting the action to `#/create\_project`).

The html is simple, and consists of:

* a link for the new project dialog, with the href of our route,
* a list of projects
* a form in a div called js\_modal\_dialog, which we will initially hide, but
  then inject into our dialog

When the page loads up we set the default state:

```javascript
...
get('#/', function() { with(this) {
  $('#js_modal_dialog').hide();
  hide_modal_dialog();
  flash_message(null);
}});
...
```

This is also the state we return to after completing any actions, and therefore
also serves to reset any changes in state we may have made (eg. showing flash
messages).

When the user clicks the add project link we show them the dialog box

```javascript
...
get('#/new_project', function() { with(this) {
  show_modal_dialog();
}});
...
```

In this dialog is a button to close (simply linking back to the `#/` route), and
the form.

When the form is submitted to `#/create_project`, we capture this post and show
a flash message saying "loading" inside the dialog. We then take the submitted
parameters and send them off via ajax (to a static file containing json in this
case, which is why the project added to the list will always be the same, no
matter what you type as the name).

We then follow a pattern which feels very familiar if you are used to Rails.

If the response is a success, we show a global flash message saying so, add the
project to the list, and redirect to `#/`.

If the response is not successful, we display a flash message to the user inside
the modal dialog, and do not redirect. This means that the user can correct the
mistakes in the form, and re-submit it.

As with all technologies, there are pros and cons to their usage. In the example
I encountered, this was a neat solution, however this may not always be the
case. One of my main concerns about pure client-side interfaces is
accessibility. In particular, Sammy lets you render partials via ajax into the
page, and I could have injected the form in that way. However, by including the
forms in the page, and hiding them with JS, they are still available if JS is
not available.

Hopefully this can become a powerful tool in our toolboxes, and used in the
right way I believe it can make complicated state-changing pages more
maintainable and understandable.

[Block UI]: http://malsup.com/jquery/block/
[new-bamboo-thoughtbot]: https://thoughtbot.com/blog/new-bamboo-joins-thoughtbot-in-london
[Sammy.js]: http://code.quirkey.com/sammy/
