Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Building Evented Single Page Applications

Building Evented Single Page Applications

Given at jQuery conf in Boston.

John Nunemaker

September 13, 2009
Tweet

More Decks by John Nunemaker

Other Decks in Programming

Transcript

  1. $("a[href^='#/']").live('click', function (event) { var $link = $(this); window.location.hash =

    $link.attr('href'); $(document).trigger('hashchange'); return false; }); Sunday, September 13, 2009
  2. 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(); } } }); } }; Sunday, September 13, 2009
  3. 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(); } } }); } }; Sunday, September 13, 2009
  4. 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); Sunday, September 13, 2009
  5. 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(); } }; Sunday, September 13, 2009
  6. 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(); } }; Sunday, September 13, 2009
  7. $(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:loading', function() { $('#harmony_loading').show(); $('#loading_progress').css('width', 0); }); $(document).bind('page:loaded', function() { $('#loading_progress').animate({width:'114px'}, 300, 'linear', function() { $('#harmony_loading').hide(); }); }); Sunday, September 13, 2009
  8. rule #1: abuse events for better code separation and easier

    customization http://orderedlist.com/articles/jquery-evented-programming-primer Sunday, September 13, 2009
  9. $('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 and reset form }, error: function(response, status, error) { // trigger error }, complete: function() { // trigger complete } }); return false; }); Sunday, September 13, 2009
  10. $('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]); $form.resetForm(); } }, error: function(response, status, error) { $form.trigger('form:error', [response, status, error]); }, complete: function() { $form.trigger('form:complete'); } }); return false; }); Sunday, September 13, 2009
  11. 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); Sunday, September 13, 2009
  12. 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); Sunday, September 13, 2009
  13. Layout.applyJSON accepts json like below and “applies” it to the

    layout; gets called with each url load either from click or form Sunday, September 13, 2009
  14. Layout.applyJSON { 'replace': { '#content': '<h1>Heading</h1><p>Yay!</p>' }, 'replaceWith': { '#post_12':

    '<div class="post">...</div>' }, 'remove': ['#post_11', '#comment_12'] } Sunday, September 13, 2009
  15. 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]); } } }; Sunday, September 13, 2009
  16. 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); Sunday, September 13, 2009
  17. $('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); Sunday, September 13, 2009