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:
- Get Sammy on the Sammy home page.
- You can get SammyJS from GitHub. You will also need a copy of jQuery, at least v. 1.4.1.
- Visual Studio users can get Sammy from NuGet.
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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.
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:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var app = $.sammy('#content', function () { | |
// your Sammy app | |
}); |
The element_selector allows you to have multiple instances of Sammy running in your document, affecting different elements.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<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 file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.
When you respond to #/by_name/this/and/that, your browser will assign /this/and/that to the splat.
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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.
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.