A third option for using jQuery templates
Dave Ward has a great post about defining jQuery templates. There's a third method that he doesn't mention in his post. The "embedd-and-grab/clone" method. I've used this method before for simple element cloning of templates.
We can create a div, or really any element you want, to hold our templates. What does this gain us? Well if we are using a design tool, we can see what the template will look like before we have to render it. That may make it easier for a designer on your project. We don't have to make an AJAX call out to retrieve the external template, although Dave talks about how this really isn't an issue if you have caching set up correctly on your server. And frankly, for the amount of bytes that are in a typical template I can't imagine any successful AJAX request taking very long.
To use them, you simply grab the templates div and detach it from the DOM. If you assign the detached elements to a var, you can just use jQuery selectors to find the one you want to use. Because, remember most of the jQuery methods return the jQuery object itself.
-
var templates = $("#templates").detach();
-
$.tmpl(templates.find("#hello").text(), person);
Why do you want to use the detach method rather than the remove method? The detach method removes the elements from the DOM but keeps any jQuery data associated with them intact. Meaning you can use the $.data() method to add data to your templates and access the data before you compile your templates.
Dave Ward points out that I need to use "tempates.find()" rather than the default selector method on the jQuery object. Noted and updated
The hardest part of software development has nothing to do with code
People who complain about how much "more" code they have to write in an MVC project versus a Webforms project, or really any project, prove to me that they have absolutely no idea where the REAL work is in ANY development project.
The main work in any software development project is FIGURING OUT WHAT TO BUILD. How you build it is trivial compared to the amount of time and effort you SHOULD put into discovering the users needs and working with them to solve their problems and make their life better.
Remember, that's the number one purpose for any piece of computer hardware or software. This cannot be stressed and repeated enough.
COMPUTERS SHOULD MAKE OUR LIVES BETTER!
How do we write programs that make lives better? By writing programs that fulfill their needs and ease the pain of their work. We still aren't at a point where we have a common, easy to understand vocabulary when it comes to build software. We often get it wrong the first, second, and third times. That's where the discipline and engineering comes into play.
Benchmarking a simple DOM based cloning template
Sara Chipps recently posted a simple DOM based clone template method she uses in one of her apps. "Easy HTML Templating with JQuery"
My template looks like this:
CODE:
<script id="ItemTemplate" type="text/html" <li class="item" value="|rowNumber|"> <input type=”text” id=”input|rowNumber|” /> </li> </script>
Now within my code I need to put a place holder where I want my HTML to go. I have my unordered list called url_list.CODE:
<ul id="url_list"></ul>Now, you see that most of my items look like this “|rowNumber|” I have a variable in my code called nextUniqueItemID (I believe in extremely descriptive variable names). Here is my “addItem” function.
JAVASCRIPT:
function addItem() { var list = $('#url_list'), items = list.find('li'); list.append($('#ItemTemplate’) .html().replace(/\|rowNumber\|/gi, nextUniqueItemID++)) }
The use of global variables aside (cough,cough),I looked at it and, having used something like this myself, thought that it would work find for data sets containing a very small number of items. The problem is these kinds of clone based templates are VERY slow compared to the templating engines that are available for various JavaScript libraries.
I happened to read a post by Brian Landau called "Benchmarking Javascript Templating Libraries" this morning and wondered just HOW MUCH slower is the naive template method than a good template library?
I grabbed the benchmarking code and modified it to run the new clone based template method.
-
var nextUniqueItemID = 0;
-
function addItem() {
-
var list = $('#url_list'),
-
items = list.find('li');
-
list.append($('#ItemTemplate').html().replace(/\|rowNumber\|/gi, nextUniqueItemID++));
-
};
-
-
$(document).ready(function(){
-
var output = $('#output');
-
$.benchmarks = {};
-
-
$.benchmarks.test_simple = function(){
-
addItem();
-
};
-
-
$.benchmarks.loop_test = function(){
-
for (var i=0; i <5; i++){
-
addItem();
-
}
-
};
-
-
// use these lines to run the benchmark tests in your browsers JS console
-
// $.benchmark(1000, '#simple_test', $.benchmarks.test_simple);
-
// $.benchmark(1000, '#loop_test', $.benchmarks.loop_test);
-
});
Since the template Sara provided contains an input tag you get a different benchmark if you run the simple_test and the loop_test separately after refreshing your browser. You can run the tests for yourself here, the loop test *may* cause your browser to give you a "script is running slow" message, hit continue as the loop will eventually end. You may also get different numbers if you run the tests in IE, Chrome, and Safari.
results: using FF 3.5.5
Simple Test: 1.71s
Loop test: 31.534s
When you consider that the slowest loop test using a template library was just around 4.5s, you get a better idea of just how slow this method is when you have an input in your template.
So that's fine, but it's known that dynamically adding text inputs is slow in just about every browser and the original tests don't use inputs at all, just divs. So let's modify the template and see what the results are.
-
<script id="ItemTemplate" type="text/html">
-
<div class="test"><h2>This is a test of |name|</h2><p>The homepage is <a href="|url|">|url|</a>.</p><p>The sources is: |source|</p></div>
-
</script>
I modified the addItem function to account for the new data. n.b. The data I'm using is static, if you wanted to use a data source you would just modify this method to take in your data parameters.
-
function addItem() {
-
var list = $('#url_list'),
-
items = list.find('li');
-
list.append($('#ItemTemplate').html()
-
.replace(/\|name\|/gi, "Clone template method")
-
.replace(/\|source\|/gi, "http://girldeveloper.com/waxing-dev/easy-html-templating-with-jquery/")
-
.replace(/\|url\|/gi, "http://girldeveloper.com/waxing-dev/easy-html-templating-with-jquery/"));
-
};
results using FF 3.5.5 - refresh between each test
simple test: 1.285s
loop test: 3.771
results using ff 3.5.5 with no refresh between tests
simple test: 1.434
loop test: 4.227
So that's looking a little bit better. Not too much slower than the template libraries.
So what do the template libraries give you? Well the replace method works find provides your data is escaped properly. But say instead of a url in the "source" replacement, you use a file path like "file:\\foodrive\source.txt". Well it still gets replaced, but the text looks like this "file:\foodrivesource.txt". So in addition to the replacement, you have to make sure your data is properly escaped. A lot of template libraries will do this for you. Also notice that the addItem method has to do a DOM lookup on every iteration of the loop to get the template. If you have a large DOM, this could impact the performance.
Mocks versus stubs and fakes
I dislike using mocks I dislike using dynamic mocking/stubbing frameworks. because it means my tests have an extra dependency beyond just the SUT (System Under Test). I often find myself spending more time getting the mock to work correctly rather than my app code. The lambada + generics based Mock suites (Moq, RhinoMocks, etc), IMO, complicate the test and make them unreadable in some situations.
Compare the two examples in this post. One uses RhinoMocks to create a stub of IDataReader and the other uses the DataTableReader to create a stub for the test. Which example is simpler and has less chance to fail due to the stub?
http://www.lazycoder.com/weblog/2008/12/12/mocking-idatareader-using-rhinomocks-35/
Using RhinoMocks
-
IDataReader reader = MockRepository.GenerateStub<IDataReader>();
-
reader.Stub(x => x.Read()).Return(true).Repeat.Times(1);
-
reader.Stub(x => x.Read()).Return(false);
-
reader.Stub(x => x["ID"]).Return(Guid.Empty);
-
reader.Stub(x => x["FullName"]).Return("Test User");
Using DataTableReader
Stubs/Fakes allow me more control over HOW the test fails and results in a test/fixture that is easier to read. I'm not saying that mocks aren't useful in certain situations, but I would favor a stub over a mock IMO, your test should only fail because of the code it is testing, not because of a mock.
Although it is fun to say "Mock ME? No mock YOU!".
update: I forgot to link to Rob's post that inspired this post. "Using Dependency Injection and Mocking For Testability"
update to the update: Jeremy Miller and Nikola Malovic both pointed out that I'm using the terminology incorrectly. It turns out I don't specifically hate mocks themselves, I dislike use dynamic mocking/stubbing frameworks due to the extra dependency they introduce into my tests. Thanks for the corrections. Back to reading Fowler for me!



