+ All Categories
Transcript
Page 1: Building evented single page applications

Ordered ListJohn NunemakerjQueryConf San Francisco, CA

April 25, 2010

BuildingSingle Page Applications

Page 2: Building evented single page applications

1. Why?2. What?3. How?

Page 3: Building evented single page applications

1. Why?

Page 4: Building evented single page applications

Because?

Page 5: Building evented single page applications

Because? NO!

Page 6: Building evented single page applications

SpeedOnly retrieve what changes

Page 7: Building evented single page applications

Perceived Speed

Page 8: Building evented single page applications

InteractivityDesktop in a browser

Page 9: Building evented single page applications

ExperienceBut the greatest of these is...

Page 10: Building evented single page applications

2. What?

Page 12: Building evented single page applications

3. How?

Page 13: Building evented single page applications

Goals

Page 14: Building evented single page applications

No reloads

Page 15: Building evented single page applications

No reloadsHistory/refresh

Page 16: Building evented single page applications

No reloadsHistory/refreshEasy

Page 17: Building evented single page applications

#

Page 18: Building evented single page applications

No reloadsHistory/refreshEasy

Page 19: Building evented single page applications

No reloadsHistory/refreshEasy

Page 20: Building evented single page applications

No reloadsHistory/refreshEasy

Page 21: Building evented single page applications

No reloadsHistory/refreshEasy

Page 22: Building evented single page applications

The EndI kid...

Page 23: Building evented single page applications

No reloads

Page 24: Building evented single page applications

<a href="#/items">Items</a>

Page 25: Building evented single page applications

$("a[href^='#/']").live('click', function (event) { window.location.hash = $(this).attr('href'); $(document).trigger('hashchange'); return false;});

Page 26: Building evented single page applications

$(document).bind('hashchange', Layout.reload);

Page 27: Building evented single page applications

var Layout = { reload: function() { Layout.load(window.location.hash); }};

Page 28: Building evented single page applications

var Layout = { load: function(path, options) { path = path.replace(/^#/, ''); // trigger path loading events $.ajax({ url : path, dataType : 'json', success : function(json) { Layout.onSuccess(json); // trigger path success events if (options && options.success) { options.success(); } }, complete : function() { if (options && options.complete) { options.complete(); } } }); }};

Page 29: Building evented single page applications

var Layout = { load: function(path, options) { path = path.replace(/^#/, ''); $(document).trigger('path:loading', [path]); $(document).trigger('path:loading:' + path); $.ajax({ url: path, dataType: 'json', success: function(json) { Layout.onSuccess(json); $(document).trigger('path:success', [path, json]); $(document).trigger('path:success:' + path, [json]); if (options && options.success) { options.success(); } }, complete: function() { if (options && options.complete) { options.complete(); } } }); }};

Page 30: Building evented single page applications

var Layout = { onSuccess: function(json) { Layout.applyJSON(json); // trigger layout success },};

Page 31: Building evented single page applications

No reloads

Page 32: Building evented single page applications

History/refresh

Page 33: Building evented single page applications

setInterval(function() { var hash_is_new = window.location.hash && window.currentHash != window.location.hash; if (hash_is_new) { window.currentHash = window.location.hash; Layout.handlePageLoad(); }}, 300);

Page 34: Building evented single page applications

#/org/groups/12/45/new

Page 35: Building evented single page applications

org groups 12 45 new

Page 36: Building evented single page applications

org groups 12 45 new

Page 37: Building evented single page applications

org groups 12 45 new

Page 38: Building evented single page applications

org groups 12 45 new

Page 39: Building evented single page applications

org groups 12 45 new

Page 40: Building evented single page applications

org groups 12 45 new

Page 41: Building evented single page applications

var Layout = { handlePageLoad: function() { var segments = window.location.hash.replace(/^#\//, '').split('/'), total = segments.length, path = ''; function loadSectionsInOrder() { var segment = segments.shift(); path += '/' + segment; var onComplete = function() { var loaded = total - segments.length, finished = loaded == total; if (!finished) { loadSectionsInOrder(); } }; Layout.load(path, {complete: onComplete}); } loadSectionsInOrder(); }};

Page 42: Building evented single page applications

var Layout = { handlePageLoad: function() { var segments = window.location.hash.replace(/^#\//, '').split('/'), total = segments.length, path = ''; $(document).trigger('page:loading'); function loadSectionsInOrder() { var segment = segments.shift(); path += '/' + segment; var onComplete = function() { var loaded = total - segments.length, finished = loaded == total; $(document).trigger('page:progress', [total, loaded]); if (finished) { $(document).trigger('page:loaded'); } else { loadSectionsInOrder(); } }; Layout.load(path, {complete: onComplete}); } loadSectionsInOrder(); }};

Page 43: Building evented single page applications

$(document).bind('page:loading', function() { $('#harmony_loading').show(); $('#loading_progress').css('width', 0);});

$(document).bind('page:progress', function(e, total, loaded) { if (total != loaded) { var final_width = 114, new_width = (loaded/total) * final_width; $('#loading_progress').animate({width: new_width+'px'}, 200); }}); $(document).bind('page:loaded', function() { $('#loading_progress').animate({width:'114px'}, 300, 'linear', function() { $('#harmony_loading').hide(); });});

Page 44: Building evented single page applications

History/refresh

Page 45: Building evented single page applications

Easy

Page 46: Building evented single page applications

Rule #1Abuse events for better code separation and easier customization

Page 47: Building evented single page applications

$('form').live('submit', function(event) { var $form = $(this); $form.ajaxSubmit({ dataType: 'json', beforeSend: function() { // trigger before send }, success: function(json) { // if errors, show them, else apply json }, error: function(response, status, error) { // trigger error }, complete: function() { // trigger complete } }); return false;});

Page 48: Building evented single page applications

$('form').live('submit', function(event) { var $form = $(this); $form.ajaxSubmit({ dataType: 'json', beforeSend: function() { $form.trigger('form:beforeSend'); }, success: function(json) { if (json.errors) { $form.showErrors(json.errors); } else { Layout.onSuccess(json); $form.trigger('form:success', [json]); } }, error: function(response, status, error) { $form.trigger('form:error', [response, status, error]); }, complete: function() { $form.trigger('form:complete'); } }); return false;});

Page 49: Building evented single page applications

var Site = { onCreateSuccess: function(event, json) { Sidebar.highlight($('#site_' + json.id)) Layout.updateHashWithoutLoad(window.location.hash.replace(/new$/, json.id)); }, onUpdateSuccess: function(event, json) { Sidebar.highlight($('#site_' + json.id)) }};

$('form.new_site').live('form:success', Site.onCreateSuccess);$('form.edit_site').live('form:success', Site.onUpdateSuccess);

Page 50: Building evented single page applications

var Field = { onCreateSuccess: function(event, json) { $('#list_section_' + json.section_id) .addSectionField(json.field_title.toLowerCase()); }, onUpdateSuccess: function(event, json) { $('#list_section_' + json.section_id) .removeSectionField(json.old_title.toLowerCase()) .addSectionField(json.new_title.toLowerCase()); }};

$('form.new_field') .live('form:success', Field.onCreateSuccess);$('form.edit_field').live('form:success', Field.onUpdateSuccess);

Page 51: Building evented single page applications

{ 'html': { '#content': '<h1>Heading</h1><p>Yay!</p>' }, 'replaceWith': { '#post_12': '<div class="post">...</div>' }, 'remove': ['#post_11', '#comment_12']}

Page 52: Building evented single page applications

Rule #2The URL dictates everything

Page 53: Building evented single page applications

var Layout = { livePath: function(event, path, callback) { if (typeof(test) === 'string') { $(document).bind('path:' + event + ':' + path, callback); } else { Layout.live_path_regex[event].push([path, callback]); } }};

Page 54: Building evented single page applications

Layout.livePath('loading', /\/org\/([^\/]+)([0-9\/]+).*/, Groups.loading);

Layout.livePath('loading', /\/sections\/([0-9]+)$/, Sections.markCurrent);

Layout.livePath('success', '/org/groups', Groups.setup);

Layout.livePath('success', '/sections', Sections.makeSortable);

Layout.livePath('success', /\/admin\/assets(.*)/, Assets.init);

Layout.livePath('success', /\/admin\/items\/(\d+)/, ItemForm.init);

Layout.livePath('success', /\/admin\/items\/([0-9\/]+)/, Items.manageSidebar);

Page 55: Building evented single page applications

Rule #3

Page 56: Building evented single page applications

$('a.field_form_toggler') .live('click', Fields.toggleAddFieldForm);$('form.new_section') .live('form:success', SectionForm.onCreateSuccess);$('form.edit_section') .live('form:success', SectionForm.onUpdateSuccess);$('form.new_field') .live('form:success', Field.onCreateSuccess);$('form.edit_field') .live('form:success', Field.onUpdateSuccess);$('a.field_destroy') .live('destroy:success', Field.onDestroySuccess);

Page 57: Building evented single page applications

Easy

Page 58: Building evented single page applications

Ordered List

Thank [email protected]

John NunemakerjQueryConf San Francisco, CAApril 25, 2010

@jnunemaker


Top Related