Single page application with Backbone.js on Rails
If you want to start a single page application (SPA) with a heavy front-end interface and you don’t have time to build your own Javascript MVC – you should definitely choose Backbone.js.
We are currently building a SPA that talks to an API written on Ruby on Rails. Choosing the right MVC Javascript framework from start was a crucial thing. From all frameworks released in the wild the most suitable ones for me seem to be Sammy.js and Backbone.js.
Judging by the features of the framework I could have chosen Sammy.js. It has a nice API and useful functions that Backbone.js is lacking. The multiple template type support for Sammy is also a plus.
On the other hand, Backbone.js seemed to be pretty wild and small compared to Sammy. The default templating system is Underscore.js that seemed to be pretty simple, based on micro templates.
After some testing I went with Backbone.js. The fact that it’s light and integrates beautifully with Ruby on Rails accentuated the choice. The missing features can be easily added by some extra programming. The micro templates are faster compared to HAML or Mustache. The only ugly thing is the pour readability of the templates but the new Rails has a cure for this, providing the integration of Jammit templates that are automatically compiled to micro templates on the first app run with the help of the jammit gem. Same thing with the CoffeScript files that are automatically compiled to javascript and glued on the first run.
A very important thing is to keep an intuitive app structure on front-end (which I didn’t do in the beginning). Keep all the models, collections, routers and views in separate files and folders. The most suitable structure for our team was this one:
/assets
/javascripts
/app
/models
user.coffee
profile.coffee
comment.coffee
/collections
users.coffee
comments.coffee
/routers
main.coffee
/views
/users
add.coffee
list.coffee
search.coffee
/profiles
edit.coffee
list.coffee
/comments
add.coffee
list.coffee
app.coffee
index.js
The app.coffee has the main router initialization and also the templates manager configuration and loading. index.js has a list of all the files that have to be loaded and converted to raw javascript.
As for the templates loading and caching I used my custom jQuery plugin that I forked from markdagleish on GitHub and adjusted so it integrates smoothly with Undercore.js.
Backbone.js didn’t disappoint me so far. Seems to be simple, easy, light and fun. Tried it with Rails and NodeJS. Integrates very well with jQuery and Zepto.js. Use it with no fear
jQuery Doom Windows Plugin – Simple Javascript Dialogs
Looking for a tiny modal dialogs library? Then this one is for you.
Doom Windows is easy to style, configure and extend. It also has a couple of shortcut functions that make it easy to use.
Display modal dialogs, confirm windows and alerts in one line of code: dAlert(‘Error!’), dConfirm(‘Are you sure?’), dWindow(‘<p>Text</p>’).
I created this plugin because I wanted to get rid of jQuery UI in my projects as it was too big and slow.
Easy steps to include the plugin:
- Include the doom_windows_style.css style
- Include the jquery.doomWindows.js file
- Make windows: dWindow(‘<img src=”http://maps.google.com/maps/api/staticmap?center=40.714728,-73.998672&zoom=14&size=400×400&sensor=false” alt=”Google Maps” width=”400″ height=”400″ />’)
Implementation examples
Display Google Maps in a separate dialog:
$('#view-map-bt').click(function () {
dWindow('<img src="http://maps.google.com/maps/api/staticmap?center=40.714728,-73.998672&zoom=14&size=400x400&sensor=false"
alt="Google Maps" width="400" height="400" />');
});
//or:
$('#view-map-bt').click(function () {
$('<img src="http://maps.google.com/maps/api/staticmap?center=40.714728,-73.998672&zoom=14&size=400x400&sensor=false"
alt="Google Maps" width="400" height="400" />').doomWindows({buttons:false, buttonClick: function (btType, win) {btType === 'close' && win.close();}});
});
Confirm modal box:
$('#view-map-bt').click(function () {
dConfirm('Are you sure you want to delete this item?', function (btType, win) {
(btType === 'no' || btType === 'close') && win.close();
if (btType === 'yes') {
// some ajax to delete item here
win.close();
dAlert('Item deleted!');
}
});
});
Alert modal:
$('#alert-bt').click(function () {
dAlert('Omg! A fake error occured! Do not panic!');
});
Load remote content with Ajax:
$('#remote-bt').click(function () {
dWindow(false, {
ajaxUrl: $(this).attr('href'),
cacheAjaxResult: true,
wrapperId: 'log-in-window',
minHeight: 317,
minWidth: 430
});
return false;
});
Please check out for more examples on the DEMO page.
Advanced options that can be set:
- styles ({position: ‘absolute’, ‘z-index’: 999, top: false, left: false}):
- Window default styles.
- width (‘auto’):
- Set up a custom width of the window.
- height (‘auto’):
- Set up a custom height of the window.
- minWidth (‘auto’):
- Set up a custom min-width style.
- minHeight (‘auto’):
- Set up a custom min-height style.
- overlay: (true):
- Show or hide window overlay.
- wrapp: (true):
- Buy default the content is wrapped in a div of ‘doom-win’ class.
- wrapperClass (‘doom-win’):
- Set the deafult wrapper class.
- wrapperId (false):
- Give a unique id to your window so it’s easier to style it.
- wrapperHtml (‘<div><div class=”doom-win-content”></div></div>’):
- Set up a custom html window wrapper.
- buttons ({ok:’Ok’}):
- Bottom buttons and button text.
- headerButtons ({close:’Close’}):
- Header buttons and button text.
- buttonsTranslate ([]):
- Text translate for buttons.
- buttonsTranslate (‘<div class=”doom-win-bt-cnt-header”><ul class=”doom-win-bt-list”>{buttons}</ul></div>’):
- Header buttons html wrapper structure.
- buttonsWrapperHtml (‘<div class=”doom-win-bt-cnt”><ul class=”doom-win-bt-list”>{buttons}</ul></div>’):
- Bottom buttons html wrapper structure.
- buttonHtml (‘<li class=”doom-win-bt-{buttonType}”><button data-type=”{buttonType}”><span>{buttonText}</span></button></li>’):
- Message button HTML structure.
- buttonClick (null):
- A callback function when for all the buttons. Get the buttons key (ok, close, yes, no) and the window object as args. I.e.: function (btType, win) {btType === ‘close’ && win.close();}
- closeOnEsc (true):
- Close window on Escape keyboard button click.
- ajaxUrl (null):
- Set the ajax url if you want to load content remotely.
- afterAjax (null):
- After ajax callback function. Gets the ajax response as arg.
- ajaxData (null):
- Set some custome request query params for ajax.
- cacheAjaxResult (false):
- Set this if you want the ajax content to be cached in the browser.
- onShow (null):
- Callback function after the window is created and displayed.
jQuery Novice To Ninja – Book Review
Recently a friend asked me what will be a good book to start learning jQuery with a more practical approach. My answer was ‘jQuery Novice To Ninja’ by Earle Castledine and Craig Sharkie. It was a really fun book to read with a lot of code examples.
You start by learning about the jQuery syntax, objects and chaining then you go deeply into how to build beautiful animations, custom events and plugins. You are introduced to jQuery UI and how to use it properly.
The book contains a lot of info about Ajax requests and how to handle them easily with jQuery.
In the end you can find some code examples about how to build a rating stars mechanism, some UI tools and plugins.
I also found some code snippets saved from the book that I can share.
Effects
Toggle vieweing with isVisible:
$('#toggleButton').click(function() {
if ($('#disclaimer').is(':visible')) {
$('#disclaimer').hide();
} else {
$('#disclaimer').show();
}
});
Animate elements on scroll, make the site header scroll with the page:
$(window).scroll(function() {
$('#header').css('top', $(document).scrollTop());
});
//or
$(window).scroll(function() {
$('#header').stop().animate({top: $(document).scrollTop()},'slow','easeOutBack');
});
Scroll to top animation quirck for all browsers:
$('a[href=#]').click(function() {
$('html, body').animate({scrollTop: 0},'slow');
return false; // Return false to cancel the default link action
}
Switch from fluid to fixed layout:
if ($('body').width() > 900) {
$('')
.appendTo('head');
} else {
$('link[href=wide.css]').remove();
}
Preload images:
$('
');
Remove selective elements:
$('#celebs tr').remove(':contains("Singer")');
Timers:
setInterval(function() {
$('#green').css('left', ++greenLeft);
}, 200);
var redLeft = parseInt($('#red').css('left'));
function moveRed() {
setTimeout(moveRed, 200);
$('#red').css('left', ++redLeft);
}
moveRed();
clearInterval();
clearTimeout();
Ajax
Basic usage:
$('#biography').load('computadors.html div:first');
$('div#results').load('search.php', 'q=jQuery&maxResults=10');
$.ajaxSetup({
type: 'POST'
url: 'send.php',
timeout: 3000
});
Access Ajax global events (ajaxSuccess, ajaxSend, ajaxComplete, ajaxError, ajaxStop):
$("#msg").ajaxError(function(event, request, settings) {
$(this).html("Error requesting page " + settings.url + "!");
});
$('#ajaxInProgress').ajaxStart(function() {
$(this).addClass('progress');
}).ajaxStop(function() {
$(this).removeClass('progress');
});
Get number of ajax requests:
$.active
Load external script in your code:
$.getScript('http://view.jquery.com/trunk/plugins/color/jquery.color.js', function() {
$('body').animate({'background-color': '#fff'}, 'slow');
});
Plugins
Wrapp your plugin:
function($) {
// Shell for your plugin code
$.fn.highlightOnce = function() {
// Plugin code
}
})(jQuery);
Set plugin defaults:
$.fn.highlightOnce.defaults = {
color : '#fff47f',
duration : 'fast'
};
Create your own selectors:
$.extend($.expr[':'], {
abovethefold: function(el) {
return $(el).offset().top < $(window).scrollTop() + $(window).height();
}
});
Events
Create event:
// Home-made event object!
var e = $.Event('click');
e.pageX = 100;
e.pageY = 100;
$('p').trigger(e);
Get events with:
$('element').data('events');
Namespace your events:
$('p').bind('mouseover.colorize', function() {
$(this).css('background-color', 'lemonchiffon')
}).bind('mouseout.colorize', function() {
$(this).css('background-color', '');
}).click(function() {
$(this).trigger('mouseout.colorize').unbind('.colorize');
});
Trigger non-namespaced events:
$('p').trigger('mouseout!');
Handle custom events like mobile touch events:
$(document).bind('touchstart', function(e) {
var original = e.originalEvent;
var x = original.changedTouches[0].pageX;
var y = original.changedTouches[0].pageY;
$('#block').css({top: y, left: x});
});
Define your sepcial events (never used this):
jQuery.event.special.multihover = {
setup: function(data, namespaces) {
// Do when the first event is bound
},
add: function(handler, data, namespaces) {
// Do every time you bind another event
},
remove: function(namespaces) {
// Do when an event is unbound
},
teardown: function(namespaces) {
// Do when the last event is unbound
},
handler: function(e) {
// Do your logic
}
}
Other stuff
Use .one(‘mouseover’, function (){}) to bind the event once.
Call functions with call to pass the scope:
// Fire the setUp callback in a plugin $.isFunction(options.setup) && options.setup.call(this);
Use utility functions:
$.isArray(), $.isFunction(), $.isEmptyObject(), $.isPlainObject()
Use andSelf to add current elemnt to the jQuery selector:
$(this).prevAll().andSelf().addClass('rating-over');
Use attributes functions to filter elements:
$(list).children().attr('selected', function(i, selected) {
return !selected;
});
Use toggleClass with function:
$(this).toggleClass('opened', x == 1);
Use pushStack to add elements to the stack:
jQuery.fn.surrounds = function() {
var prev = this.index() == 0 ?
this.siblings(':last') :
this.prev();
var next = this.index() == this.siblings().length ?
this.siblings(':first') :
this.next();
var newStack = prev.add(next);
return this.pushStack(newStack, 'surrounds', '');
};
$('#categories li:first').surrounds().css('color', 'red').end().css('color', 'blue');
Some knowledge about Javascript bad parts:
JavaScript treats all these values as truthy:
- true
- 1 (because it’s a non-zero number)
- ’0′ (because it’s a non-empty string)
- ‘false’ (because it’s a non-empty string)
- function() {} (any function)
- {} (any object)
- [] (any array)
And these values are falsy:
- false
- 0 (the number zero)
- ” (an empty string)
- null
- undefined
- NaN (a special number value meaning Not a Number)
Nice book to begin learning jQuery. What else can you recommend?
Shuffle Arrays with Javascript
Working with random values in Javascript is like leaving in a cave. The only thing you can work with is Math.random() that returns a random number between 0 and 1. If you need a 0 to 10 value – multiply it by 10 (parseInt(Math.random()) or Match.floor(Math.random() * 10)) … That’s all random stuff you have in Javascript.
I needed a shuffling function to randomize one Array’s elements so the only thing was to build one:
// Add new property to javascript array object
Array.prototype.shuffle = function() {
for (var j, x, i = this.length; i; j = parseInt(Math.random() * i), x = this[--i], this[i] = this[j], this[j] = x);
return this;
};
Attache it to the Array prototype object so you can access it as object’s own method:
alert([1, 2, 3, 4, 5].shuffle()); alert(['one', 'two', 'three', 'four', 'five'].shuffle());
Do you have any other ideas or functions to improve the randomize process in Javascript?
