Single Page Apps – Getting Started with SammyJS – Routes

image[4][3]Single Page Applications (SPA) are web sites/applications which are consists of single page and provide smooth user experience in contrast with traditional click and refresh web pages

Even in the world of high speed broadband internet connections, changing a web page when clicked on a link is not desirable and would certainly be appreciated if possible to avoid. SPA’s provide just that.

Sammy.js helps you create RESTful evented JavaScript single page applications. You can maintain the state of your app with the URL without having to refresh or change the page.

You use a route in your URLs that contains #/operation as part of path name. By changing URL to a hash based path #/pathName you avoid page refresh and your page responds to the new user request. It also makes it possible to use browser back/forward button.

About Sammy Routes

Sammy works based on “routes”. In Sammy, route is consists of a

  • Hash (#) based path
  • The HTTP verb (get, post, put, delete)
  • Callback function.

Whenever, URL is changed to http://ServerName/pagename.aspx#/operation

then the route is called which contains “#/operation” as part of path name. By changing URL to a hash based path (“#/pathName”) makes it possible to avoid page refresh at the same time handle the event. It also makes it possible to use browser back/forward button.

How SammyJS Fits in Your Single Page Application

Sammy’s core includes a simple API for defining applications which are made up primarily of routes and events. Sammy also has an ever-growing list of plugins for specific functionality. By driving application development around a small and specific API, Sammy helps you keep your code organized while still allowing a lot of room to define your own style and structure.

Sammy is a light framework, less than 20K, capable of invoking several instances simultaneously, so you can run multiple apps in the same document. It was inspired by inspired by Ruby’s Sinatra. Sammy requires jQuery.

In this post, you’ll learn the basics of Sammy and build a simple page with routes. In our next posts, you’ll learn to build a single page line of business app, and how to Sammy works with other libraries such as Knockout and Durandal.

How to Get SammyJS

You can get Sammy for your project in one of several ways:

image

Sammy in Template and Framework

There are a couple other ways of getting Sammy as part of other libraries and templates.

  • Durandal depends on Sammy for its a cross-device, cross-platform client framework.
  • Hot Towel by John Papa creates a great starting point for building a SPA with ASP.NET or ASP.NET MVC, Knockout for data bindings, Durandal for navigation and UI, and Breeze for data management.

Starting the Page

Begin by starting a new HTML page. Include a div whose id is content and script tags that load jQuery and Sammy.


<!DOCTYPE html>
<html>
<head>
<title>Basic Sammy</title>
</head>
<body>
<div id="content"></div>
<script src="Scripts/jquery-1.4.4.js"></script>
<script src="Scripts/sammy-0.7.4.js"></script>
<script>
// our app goes here
</script>
</body>
</html>

Sammy.Application

The Sammy application begins by assigning it to a variable that we’ll name app. In Visual Studio you can see a tooltip that describes the Sammy as the namespace and as a top-level method for Sammy application instances.

image

element_selector

This is where your content goes.

You assign Sammy’s element_selector property to the selector for our content div or you can assign it as part of the $.sammy initial function call:


var app = $.sammy('#content', function () {
// your Sammy app
});

view raw

spa-sammyApp.js

hosted with ❤ by GitHub

The element_selector allows you to have multiple instances of Sammy running in your document, affecting different elements.


var app = $.sammy(function () {
this.element_selector = '#content';
// routes go here
});

Only one instance of Sammy is allowed per element_selector so when calling Sammy('selector') multiple times, the first time will create the application and the following times will extend the application already added to that selector.

You can get Sammy’s application element by using $selector. The $selector is where Sammy loads its content.

Adding Links

To begin, add a nav section above the content div. The nav will include the links for the user to click on that Sammy will “route” and display our page content.


<nav id="nav">
<ul>
<li><a href="#/bigletters'">Big Letters</a></li>
<li><a href="#/parseme/drafts">Parse Path</a></li>
<li><a href="#/compose">compose</a></li>
<li><a href="#/by_name/this">Alert this</a></li>
<li><a href="#/by_name/this/and/that">Alert This and that</a></li>
</ul>
</nav>

Adding a Route

Sammy.js uses the path, as defined in the URL hash, and the common HTTP methods (get, post, put, delete) to determine a callback function to invoke.

A route is made up of three simple pieces:

  • A verb (get, post, put, delete*)
  • A path (#/, test/path/, #/my_path/:var)
  • A callback (function() {…})

* delete is aliased as del() because delete is a reserved word in JavaScript

Routes can be defined in two ways

  • Using route directly.
  • Using the verb shortcuts.

Using Route Directly

route('get', '#/', function() { 
       //  ...
});

Using Verb Shortcut

get('#/', function() { 
       //  ...
});

 

In this case, you will use the verb shortcut to set the default route of our app that will respond to the URL #/bigletters. Replace the comment in the sammy function with the following code:


this.get('#/bigletters', function(context) {
context.app.swap('');
context.$element().append('<h1>Big Letters</h1>');
});

The context.app.swap('') tells Sammy to replace what’s in my content div, rather than just appending to it.

URL Params

Sammy also parses the path for named and un-named params. This means you can pull things like an ‘id’ or a ‘slug’ out of the path for dynamic parsing.

Any string starting with : in a path will be pulled out and converted to a named param.


this.get('#/parseme/:word', function (context) {
context.app.swap('');
context.$element().append('<h1>' + this.params['word'] + '</h1>');
});

This works with multiple params in a single path.

Starting Up Our Sammy App

Now that a route is defined, you can start the app by pointing it to the default route.

$(function () {
    app.run('#/bigletters');
});

When you run the app, Sammy will automatically route the app to bigletters and display the <h1> content. Click on Parse Path and you will see the word drafts because that was the param passed into the Sammy’s context.

Sammy Verbs

Out of the four verbs, get is the most common. get routes are invoked whenever the URL or URL hash changes. If the URL or hash matches one of the route paths, the callback for that route is invoked.

post, put, and delete are only invoked by submitted forms. At run() Sammy binds events to submit() for all the forms. When a form is submitted, Sammy looks for a route that matches the path of the form (the action) and the verb (the method). If a route is found, the callback is executed.

To set up our form, create a get method that will respond to the #/compose route. This is similar to our previous get methods.


this.get('#/compose', function (context) {
context.app.swap('');
context.$element().append('<h1>say hello to?</h1>'
+ '<form action="#/compose" method="post">'
+ '<input type="text" name="to" />'
+ '<input type="submit" name="submit" />'
+ '</form>');
});

Next set up a post method that will respond when the form is submitted.


this.post('#/compose', function(context) {
context.app.swap('');
var to = this.params['to'];
context.$element().append('<h1>hi ' + to + '</h1>');
return false;
});

If you do not want to actually submit the form (allow its default behavior), you must return false from the matching routes callback.

$element(selector) returns a jQuery object of the Applications bound element.

More on Paths

Paths can be either full URIs (eg. /this/and/that) or just the end hash (eg. #/mypath). The benefit of using the hash is that you can define a single page application (no full refreshes) that preserves the back button/history.

Paths can be defined as strings or regular expressions. In fact, internally, all paths are converted to regular expressions.

You can use the following code to respond to #by_name using RegEx:

this.get(/#/by_name/(.*)/, function () {
    alert(this.params['splat']);
});

When you respond to #/by_name/this, your browser will alert this.

image

When you respond to #/by_name/this/and/that, your browser will assign /this/and/that to the splat.

image

The Basic Sammy App Sample

The following code represents our completed Sammy app. I have added a couple more links to demonstrate when you might need to use regular expressions and have added comments.


<!DOCTYPE html>
<html>
<head>
<title>Basic Sammy</title>
</head>
<body>
<nav id="nav">
<ul>
<li><a href="#/bigletters'">Big Letters</a></li>
<li><a href="#/parseme/drafts">Parse Path</a></li>
<li><a href="#/parseme/id=123&name=john">Get my params</a></li>
<li><a href="#/compose">compose</a></li>
<li><a href="#/by_name/this">Alert this</a></li>
<li><a href="#/parseme/this/and/that">Does not work</a></li>
<li><a href="#/by_name/this/and/that">Alert This and that</a></li>
</ul>
</nav>
<div id="content"></div>
<script src="Scripts/jquery-1.4.4.js"></script>
<script src="Scripts/sammy-0.7.4.js"></script>
<script>
var app = $.sammy(function () {
// set up a Sammy Application by passing a Function to the $.sammy
// (which is a shortcut for the Sammy.Application constructor).
this.element_selector = '#content';
// respond to the #/bigletters route
this.get('#/bigletters', function (context) {
context.app.swap('');
context.$element().append('<h1>Big Letters</h1>');
});
// respond to the parseme route and pass in word paramater
this.get('#/parseme/:word', function (context) {
context.app.swap('');
context.$element().append('<h1>' + this.params['word'] + '</h1>');
});
// respond to the #/compose get verb by creating a form
this.get('#/compose', function (context) {
context.app.swap('');
context.$element().append('<h1>say hello to?</h1>'
+ '<form action="#/compose" method="post">'
+ '<input type="text" name="to" />'
+ '<input type="submit" name="submit" />'
+ '</form>');
});
// respond to the #/compose post verb by taking the to param
// and displaying it in a heading
this.post('#/compose', function (context) {
context.app.swap('');
var to = this.params['to'];
context.$element().append('<h1>hi ' + to + '</h1>');
// do not actually submit the form
return false;
});
// get the params with slashes
this.get(/\#\/by_name\/(.*)/, function () {
alert(this.params['splat']);
});
$(function () {
app.run('#/bigletters');
});
});
</script>
</body>
</html>

When you run the app, try out the back button.

Sample Code

Sample code for the Sammy tutorial can be found in the DevDays GitHub:  https://github.com/devdays/single-page-app/tree/master/Sammy

References

The official Sammy tutorial walks through the building of an Ecommerce front-end using Sammy called the JSON store.

Sammy documentation

Quick Tip: An Introduction to Sammy.js

Simple app using Sammy.JS from Mike Hadlow


2 thoughts on “Single Page Apps – Getting Started with SammyJS – Routes

Comments are closed.