Although SammyJS is a router that provides you with file loading of data and templates. You load templates and data using Sammy’s plugins.
In this tutorial, you will learn how you can use sammy.load to load JSON data, and then use LoDash (or Underscore) to _.find()
to retrieve the item based on the value provided in the sammy route. And you will combine the template and data using a custom Sammy plugin.
Why LoDash?
LoDash or Underscore provide great methods for working with collections and arrays. There are subtle differences in these two libraries. But for this tutorial, they provide the same functionality.
Use these libraries to “slice and dice” your data. In the case of this tutorial, you will use _.find()
. In your real life applications, there will be more complex ways of manipulating your data, that LoDash can provide.
LoDash includes _.template()
. The template method compiles a set of HTML code and turns it into JavaScript. The templates can include _ and complex JavaScript functions.
For a tutorial on how to build LoDash templates, see HTML Templates With Logic Using Underscore, LoDash.
Getting Started
You should already know how to build LoDash templates and how load templates and data into Sammy.
For this tutorial, you will need:
- You will need jQuery, LoDash, Sammy. The links take you to where get the libraries.
- Or jQuery, LoDash, and Sammy.js are available on NuGet.
Place the libraries in a folder named Scripts
And you will need starter code that includes data and templates and a starter page for your HTML.
Data/products.txt
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
{ | |
"products": [ | |
{ | |
"id": 1, | |
"name": "Squirt gun", | |
"category": "Toy", | |
"price": 45.05, | |
"description": | |
"<p>This is a <strong>squirt</strong> gun. Shoots water.</p>" | |
}, | |
{ | |
"id": 2, | |
"name": "Action figure", | |
"category": "Toy", | |
"price": 65.96, | |
"description": "<p>This is an extraordinary action figure.</p>" | |
}, | |
{ | |
"id": 3, | |
"name": "Doll", | |
"category": "Toy", | |
"price": 35.68, | |
"description": | |
"<p>This is a <emphasis>doll</emphasis> for your doll house.</p>" | |
}, | |
{ | |
"id": 4, | |
"name": "Lettuce", | |
"category": "Grocery", | |
"price": 3.49, | |
"description": "<p>This is a vegetable.</p>" | |
} | |
] | |
} |
Templates/product.html
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
<div><strong><%- product.name %></strong> | |
<span>( Cateogry: <span class="value"><%- product.category %></span> )</span></div> | |
<div>Description: <% product.description %></div> | |
<div>Equal Description: <%= product.description %></div> | |
<div>Price: <%- product.price %></div> |
Templates/products.html
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
<ul> | |
<% _.forEach(products, function(product) { %> | |
<li> | |
<strong><%- product.name %></strong> | |
<span>( Cateogry: <span class="value"><%- product.category %></span> )</span> | |
<span>( Link: <a href='"#/products/<%- product.id %>'> | |
<%- product.name %> Details</a> )</span> | |
<div>Description: <%= product.description %></div> | |
<div>Price: <%- product.price %></div> | |
</li> | |
<% }); %> | |
</ul> |
The starter HTML
The code loads the Sammy plugin that you will write, adds the plugin so Sammy can use it, and then runs the routes.
The code in yellow, loads the Scripts/sammy.lodash.js file that you will write, and uses the sammy.use method to make it available inside Sammy. The Sammy.use() use
takes the plugin function you defined and evaluates it within the context of the current application.
<!DOCTYPE html> <html> <head> <title>Products Using Custom LoDash Plug-in for Sammy</title> </head> <body> <nav> <ul> <li><a href="#/">Home</a></li> <li><a href="#/products">Products</a></li> <li><a href="#/products/1">Product 1</a></li> </ul> </nav> <div id='content'></div> <script src="Scripts/jquery-1.9.1.js"></script> http://span http://span http://span <script> (function () { "use strict"; console.log("initializing sammy"); var app = $.sammy('#content', function () { this.use('LoDash','html); // the callback is the entire route wrapped in a closure this.around(function (callback) { var context = this; this.load('data/products.txt', { json: true }) .then(function (items) { context.items = items; }) .then(callback); }); this.get('#/', function (context) { context.log('Yo yo yo'); context.app.swap(''); // clear the content area before loading the partials context.$element().append('<h1>Main page</h1>'); }); this.get('#/data', function (context) { context.app.swap(''); // clear the content area before loading the partials context.$element().append(JSON.stringify(context.items)); }); this.get('#/products/:id', function (context) { // Insert your code here }); this.get('#/products', function (context) { // Transform the data using a LoDash template }); }); $(function () { app.run('#/'); }); })(); </script> </body> </html>
Add the Products Route Code
The implementation of the routes this.get(‘#/products‘, is actually the same code that you used in the previous post, SPA 5–Deep Dive into Loading Templates Using Sammy, Mustache, RequireJS.
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('#/products', function (context) { | |
var products = context.items; | |
// clear out the $element in this case '#content' | |
context.app.swap(''); | |
// Uses the plugin to transform the template with the products data | |
// then appends it to the $element | |
context.render('templates/4-products.html', products) | |
.appendTo(context.$element()); | |
}); |
The difference is that because Sammy’s render method will the Sammy.LoDash method in our new sammy.lodash.js plugin because we specified that using the sammy.use() where you pass in the function name(Sammy.LoDash) and the extension type (html).
Add the Product Route Code Using LoDash/Underscore Find
Use LoDash (or Underscore) to find the right product based on the route. _.find uses a function to determine which of the product matches the id in the route.
this.get('#/products/:id', function (context) { var param = context.params['id']; var products = context.items.products // find the product based on the value of :id var productData = _.find(products, function (product) { return product.id.toString() === param; }); if (!productData) { return context.notFound(); } // partial() internally calls render and swap // creates the html and puts it into $element context.partial('Templates/4-product.html', { product: productData }); });
Build the LoDash Plugin
A Sammy plugin is really just a Sammy app definition that isn’t evaluated until needed. You can use Sammy plugins for code that you can reuse across applications. Sammy users have provided several plugins. You can find them at Sammy Plugins. And you can model your plugin from that.
Sammy uses plugins to automatically transform the data using templates.
The code uses a factory pattern to create the plugin using define method when the module is used for AMD, or uses window global values.
The object exposes the Sammy.LoDash function that uses the _.template method.
Sammy loads the template from the file and passes it into Sammy.LoDash, which then calls _.template().
You can use the plugin as a helper function too. The code sets up the plugin so you can use it as a help function too.
Scripts/sammy.lodash.js
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
(function (factory) { | |
if (typeof define === 'function' && define.amd) { | |
define(['jquery', 'sammy', 'underscore'], factory); | |
} else { | |
(window.Sammy = window.Sammy || {}).Tmpl = | |
factory(window.jQuery, window.Sammy, window._); | |
} | |
}(function ($, Sammy, _) { | |
// `Sammy.LoDash` is a wrapper around the underscore/lodash templating engine. // http://lodash.com/docs#template | |
Sammy.LoDash = function (app, method_alias) { | |
// *Helper:* Uses _.template to parse a template and interpolate // and work with the passed data | |
// * `template` A String template that will be compiled by _.template() | |
// * `data` An Object containing the replacement values for the template. | |
// data is extended with the <tt>EventContext</tt> allowing you to call // its methods within the template. | |
// | |
var template = function (template, data) { | |
data = $.extend({}, this, data); | |
var results = _.template(template, data); | |
return results; | |
}; | |
// set the default method name/extension | |
if (!method_alias) { method_alias = 'lodash'; } | |
// create the helper at the method alias | |
app.helper(method_alias, template); | |
}; | |
return Sammy.LoDash; | |
})); |
(Note that in the define method, by convention I am using underscore as the name of the require.js module.)
Currently the tests for all plugins are contained in the single file test_sammy_plugins.js
Sample Code
This sample code is available on the DevDay repository on GitHub: https://github.com/devdays/single-page-app/tree/master/Sammy
See 7-LodashEngine.html
References
Sammy Plugins source code