The jQuery Fuzzytoast plugin aims to easily combine data from standard REST calls with "templates" and insert the results back into the HTML layout.
This goal removes xSP processing from the server to the client, making the server work significantly more straight-forward and easier. However, to help with the added complexity of the client, we've created this project.
The easiest way to use this plugin is to place a fuzzytoast aspect on a button or some other object and have it grab an online template, some data from a REST call and insert it as the child of some element, as in:
$('#some-buttom').fuzzytoast ({
template: 'templates/about.html',
data : 'rest/some-data.json'
});
This call makes many assumptions, but the
fuzzytoast
parameters are the following:
template
- The URL of the template to use
data
- The URL to a REST interface that responds with a data
model used to substitute in the template.
destination
- The jQuery selector to use when adding the results (as a
child element). If not specified, it defaults to
$.fuzzytoast.default_destination
(see below).
method
- The HTTP method used when retrieving the data. Defaults to
$.fuzzytoast.default_method
(see below).
append
- Should the results be inserted as the only child of the
destination (
false
) or appended to the end of the existing children (
true
). Defaults to
$.fuzzytoast.default_append
(see below).
success
- A callback function called after the template and data have been
successfully inserted into the document. Defaults to
$.fuzzytoast.default_success
(see below).
error
- A function to call if either the template or data could
not be downloaded. Defaults to
$.fuzzytoast.default_error
(see below).
complete
- A function called after either the success
or
error
callbacks. Defaults to
$.fuzzytoast.default_complete
before
- A callback function called before the requests for the template
and data have been made. It is passed the destination
,
which allows a spinner to be inserted while the data is being
acquired. Defaults to
$.fuzzytoast.default_before
If this function returns a function, that is passed to all
$.ajax()
calls as a beforeSend
parameter.
beforeSend
- A function called by $.ajax()
before each AJAX
request. See the AJAX documentation
for details.
You can also set up defaults for all requests. These defaults include:
$.fuzzytoast.default_destination
- The jQuery selector that references an HTML element where
the results will be place. Defaults to
#main
.
$.fuzzytoast.default_method
- The HTTP method to use when retrieving the data from a web
service. Defaults to
GET
(didn't see that coming, did you?)
$.fuzzytoast.default_append
- Should the results of the request be appended to the
destination. Defaults to
false
$.fuzzytoast.default_success
- A function name to be used for all
successful
requests.
$.fuzzytoast.default_error
- A function name to be used for all
requests that failed... for whatever reason.
$.fuzzytoast.default_complete
- A function name to be used for all
complete
d requests. Called after
any success
or error
calls.
$.fuzzytoast.debug
- Specifies the extra
console.log
ging features of the system. Defaults to
false
.
Templates themselves may contain data that needs to call the fuzzytoast system, and in this case, you can build up the dynamic aspects by first creating the action, and then calling it, as in:
var id = $.fuzzytoast.create({
template : 'templates/profile.html',
data : 'data/user/'+$(this).attr('id')+'.json',
destination : '#main'
});
$.fuzzytoast(id);
Or doing this with a single call to
$.fuzzytoast()
as in:
$.fuzzytoast({
template : 'templates/profile.html',
data : 'data/user/'+$(this).attr('id')+'.json',
destination : '#main'
});
A couple of reasons why you would want to use the utility
function form of $.fuzzytoast()
instead of the initial selector method usage described
above, e.g. $('#selector').fuzzytoast()
:
First, the $(this)
is not available without being inside a function call.
Second, you might want to kick off the call with something other
than a click
, as in:
$('#user-list li').
hover(
function() {
$(this).addClass('ui-state-hover');
$.fuzzytoast({
template : 'templates/profile.html',
data : 'data/user/'+$(this).attr('id')+'.json',
destination : '#main'
});
},
function() { $(this).removeClass('ui-state-hover'); }
);
The data
parameter can either be a string containing the URL to access
for the data, or an object containing multiple sources. For example:
$.fuzzytoast({
template : 'templates/dashboard.html',
data : {
'apps' : '/apps/v1/details',
'services': '/srvs/v1/details',
'profile' : '/profile/' + id
},
destination : '#main'
});
In this example, the plugin will retrieve the JSON document from each of
the three URLs, and make them available to the template. Each JSON object
can then be referenced by the key given in the data
.
For example:
{
'apps' : [ { 'id':42, 'name':'mongrue' },
{ 'id':34, 'name':'fuzzytoes' }
],
'services': [ { 'id':3, 'name':'mongodb-1.8', 'type':'mongodb' },
{ 'id':4, 'name':'redis-cep', 'type':'redis' }
],
'profile' : { 'name': 'Mickey Mouse',
'email': 'gloves@disney.com'
}
}
If you are using Mustache, your template could access that data in this way:
{{apps[1].name}}
{{services[0].type}}
{{profile.email}}
Note: If any of the data sources fail, then error()
callback function is called, and the success()
function will
not... Even if two of the three succeed.
While FuzzyToast needs a template engine, it doesn't supply one. You use whatever system you want, by simply including a system. For example:
<script type="text/javascript" src="js/libs/mustache.js"></script>
If it is one of the ones listed below, FuzzyToast will recognize it, and use it:
Whatever template engine you use, we've added a feature to allow
partial templates, where a template can include the contents
of another template.
For instance, if you were using jQuery Templates, and
foo.html
contains:
<h1>Hello, ${user.name}</h1>
<div>
{{INCLUDE bar.html}}
</div>
And bar.html
contains:
Books read:
<ul>
{{each(book) user.books}}
<li> ${book.name} </li>
{{/each}}
</ul>
The template that is processed with the model would look like this:
<h1>Hello, ${user.name}</h1>
<div>
Books read:
<ul>
{{each(book) user.books}}
<li> ${book.name} </li>
{{/each}}
</ul>
</div>
Included templates can include other templates.
Oh yeah, the {{INCLUDE ...}}
must be in uppercase. Didn't
want to risk conflicts with the template engine.
We have chosen to support a few popular template engines, but you can use others... (see this presentation) you just have to tell us how to call it, by setting the following variables:
$.fuzzytoast.template.engine
- (String)
The name of the template system. Used only in debug messages.
$.fuzzytoast.template.type
- (String)
Tell us about the behavior of the system, by setting this variable
to one of the following:
functional
-
This approach is the easiest (for us anyway), as this function takes
both the template and the model at once, and returns the
generated results.
global-storage
-
Global storage is obnoxious, because it isn't functional. We'll
store all of our templates with the key: fuzzytoast-template
variable
-
The "variable" approach compiles the template and returns it.
We then use that to process the "model"...
$.fuzzytoast.template.render
- (Function)
A single function that is given both the template and the data model.
The results from this function should be HTML, as this is what is
embedded back into the page. Mustache behaves this way.
$.fuzzytoast.template.compiler
- (Function)
Called with just the template, and if the type
is "variable", then the output from this function should be
a function that can be given the data model for rendering.
Handlebars behaves this way.
type
is "global-storage", then this function doesn't
return anything, but stores the compiled template in a global variable,
and we'll call the processor
with the name of this variable.
jQuery's template system behaves this way.
$.fuzzytoast.template.processor
- (Function)
Called only if type
is set to "global-storage" in
order to process the data model through the compiled template.
If you have a template system that doesn't behave in one of the ways listed
above, you can easily create your own render()
function, and
do the processing yourself. If Handlebars wasn't so damn cool, we probably
would have only supported that approach.
Of course, you get errors... especially when dealing with a lot of downloaded files and web services. We've attempted to make this part simple.
You can either add a
error
function reference to a link, or you can specify the function
reference for all links by setting the
$.fuzzytoast.default_error
value.
Assuming we have the following modal dialog template embedded in our page (instead of attempting to download it at run-time from our server):
<script type="text/x-jquery-tmpl" id="errorTemplate">
The request we made to the web service returned an error.
<table>
<tr>
<th align="left">Response:</th>
<td>${status}</td>
</tr>
<tr>
<th align="left">Request:</th>
<td>${url}</td>
</tr>
</table>
</script>
We could create the following error handler:
function access_error( errorDetails )
{
$('#error-dialog').empty();
$( "#errorTemplate" ).template( "errorTemplate" );
$.tmpl( "errorTemplate", errorDetails ).appendTo( "#error-dialog" );
$('#error-dialog').dialog('open');
}
Notice that the error handler is passed a
errorDetails
object that contains lots of different values about the error:
type
: The type of request that failed,
either "template" or "model".url
: The URL associated with the error. This
might either be the URL to the template or to the web service
call (depending on the type
).link
: A reference to the fuzzytoast link
associated with the error. The link.id
might be
useful in debugging the problem.status
: The HTTP error code, i.e. 404
.
statusText
: The error text associated with
the error. This is usually just "error".jqXHR
: A reference to the jQuery
XMLHttpRequest object.Now we can associate the error handler with all errors, with:
$.fuzzytoast.default_error = access_error;
loadWithCache()
jQuery has a nice function,
.load()
which
loads some static HTML data and inserts in the page. The problem is if
you do this process again, it re-downloads it.
We've extended jQuery with a .loadWithCache()
function that
acts just like .load()
the first time it is executed.
If it is called again, it just pulls the results from an in-memory
cache, and doesn't bother hitting the server.
This function is useful for static HTML files that you know won't change very often.
The behavioral differences between the first and *second load* include:
data
value (if any) is ignored.
textStatus
or
the XMLHttpRequest
object.
Note: The cache is cleared when the browser page is refreshed.