+ All Categories
Home > Documents > Building Evented Single Page Applications

Building Evented Single Page Applications

Date post: 08-Dec-2014
Category:
Upload: steve-smith
View: 2,305 times
Download: 2 times
Share this document with a friend
Description:
Building single page applications has always seemed like a dark art. Guess what? It's not. The key is really simple—let the URL dictate everything. I'll show how to use the window location's hash in combination with jQuery's event system to drive your entire application, from link clicks to form submissions to history management and beyond.
Popular Tags:
58
Ordered List John Nunemaker jQueryConf San Francisco, CA April 25, 2010 Building Single Page Applications
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


Recommended