Using Isotope with Knockout.js

Knockout.js is a JavaScript library for writing MVVM style HTML applications. Isotope is a super cool jQuery plugin for fluid list animation – go play around with it here, it’s really impressive.

A question from a colleague prompted me to look at the Knockout.js documentation for the first time in a while and I noticed that there’s now a ‘afterAdd’ option available for the foreach binding. This allows us to hook in some code for manipulating an element once it’s been added to the list, intended for animation. I wondered if it was possible to insert Isotope into this process and it turns out it’s really easy – take a look at it working together here.

The code to do it was also really simple and demonstrates quite how handy Knockout is. I’m sure there’s some debate to have about whether the function for manipulating the element in the view really belongs on the ViewModel, but I’ll leave that for another day.

var $wordList = $('#word-list'),
    wordsViewModel = {
        words: ko.observableArray([]),
        newWord: ko.observable(''),
        add: function() {
            this.words.push( this.newWord() );
            this.newWord('');
        },
        wordAdded: function(el) {
            $wordList.isotope( 'appended', $(el) );
        }
    };

ko.applyBindings(wordsViewModel);

$wordList.isotope({
    layoutMode: 'fitRows',
    itemSelector: '.word'
});
<form data-bind="submit: add">
    <input placeholder="New Word" data-bind="value: newWord" autofocus />
</form>
<ul id="word-list" data-bind="template: { name: 'word-item-tmpl', foreach: words, afterAdd: wordAdded }">
</ul>
<script id="word-item-tmpl" type="text/x-jquery-tmpl">
    <li class="word">${ $data }</li>
</script>
Advertisements

BDD Testing of jQuery plugins using Jasmine

Jasmine is a simple, straightforward behavior-driven development framework for JavaScript. One of its core principles is that it “should not be tied to any browser, framework, platform, or host language.” This makes it excellent for testing bare JavaScript code. But this does mean, it’s not entirely obvious how one would would test code that is tied to a browser and framework. Like for example, a jQuery plugin.

To demonstrate this, I’ve put together a super simple (read: completely useless) jQuery plugin which will make any list, a slightly editable list. Check it out on jsFiddle.net – just put something in the text box then hit enter. It’ll add items to the list and provide a link on each item to remove it.

Jasmine couldn’t be easier to explain. You have suites, and you have specifications. Suites describe scenarios; specifications describe what it should be doing in those scenarios. Those two things are the only two methods you have to know, describe and it.

It’s best demonstrated by example. I feel the only bit of this that requires an explanation is the beforeEach method. This is run before each specification, and I use it to initialize my jQuery plugin. I’ve extended Jasmine with jasmine-jquery, a smart little library that helps us initialize jQuery related bits. It’s what allows us to setup the HTML which the fixture runs against, and then in our beforeEach initialize our plugin.

describe("jquery.list", function() {
	var textbox, list;

	beforeEach(function() {
		jasmine.getFixtures().set('<input id="textbox" type="text" />
<ul id="list">');
		textbox = $('#textbox');
		list = $('#list').list({
			input: textbox
		});
	});

	describe("pressing enter in input when there's no value", function() {
		beforeEach(function() {
			var keypress = $.Event('keypress');
			keypress.which = $.ui.keyCode.ENTER;

			textbox.val('');

			textbox.trigger( keypress );
		});

		it("should not add an item", function() {
			expect( list ).toBeEmpty();
		});
	});
});

Hopefully you found that clear. In the preceding code sample, we set up the scenario that the text box is empty and that the enter key had been pressed. Then using the it method, we expect that the list should be empty.

What I really like about Jasmine, is something anyone who is familiar with RSpec will appreciated – nested specifications. It’s something I feel that particularly makes sense in UI testing. In our case, we have a scenario to add an item and write some specifications to verify that. Once that item’s been added, we can then have another nested scenario that we can remove it.

describe("pressing enter in input when it's got a value", function() {
	var testText = 'THIS IS SOME TEXT';

	beforeEach(function() {
		// omitted code to set test text and press enter key
	});

	it("should add item to list", function() {
		expect( list.children().length ).toEqual( 1 );
	});

	it("item should contain entered text", function() {
		expect( list.find( 'li' ).text() ).toContainText( testText )
	});

	describe("clicking remove link", function() {
		it("should remove item", function() {
			var click = $.Event('click');
			list.find('li a').trigger( click );

			expect( list ).toBeEmpty();
		});
	});
});

Note how we have a describe method nested inside a describe. The first adds the item, the second then removes it. It’s using techniques like this that I find writing BDD style specifications are much more effective at testing front-end code than traditional unit-testing.

Run the specifications yourself here. Download all the code here.