Functionality Focused Code Organization

49
Functionality-Focused Code Organization Rebecca Murphey • @rmurphey • rebeccamurphey.com jQuery Boston 2010 Saturday, October 16, 2010

description

The magic of jQuery's CSS-based selection makes it easy to think about our code in terms of the DOM, and sometimes that approach is exactly right. Other times, though, what we're trying to accomplish is only tangentially related to our nodes, and opting for an approach where we think in terms of functionality -- not how that functionality is manifested on our page -- can pay big dividends in terms of flexibility. In this talk, we'll look at a small sample application where the DOM takes a back seat to functionality-focused modules, and see how the approach can change the way we write and organize our code.

Transcript of Functionality Focused Code Organization

Page 1: Functionality Focused Code Organization

Functionality-Focused Code OrganizationRebecca Murphey • @rmurphey • rebeccamurphey.comjQuery Boston 2010

Saturday, October 16, 2010

Page 2: Functionality Focused Code Organization

Saturday, October 16, 2010

Page 3: Functionality Focused Code Organization

Saturday, October 16, 2010

Page 4: Functionality Focused Code Organization

object laterals literals

module pattern

revealing module pattern

etc.

Saturday, October 16, 2010

Page 5: Functionality Focused Code Organization

Saturday, October 16, 2010

Page 6: Functionality Focused Code Organization

$('#search').submit(function(e) { e.preventDefault(); resultsContainer.empty();

var term = $(this).find('input').val(); if (!$.trim(term)) { return; }

$.each(['search.news', 'search.web'], function(i, svcName) { $.getJSON( searchUrl, { q : buildQuery(term, svcName), format : 'json' },

function(resp) { $('<li><h3>' + svcName + '</h3><ul></ul></li>') .appendTo(resultsContainer) .find('ul') .html( $.map(resp.query.results.result, function(r) { return Mustache.to_html(resultsTpl, r); }).join('') ); } ); }); });

a “traditional” jQuery way of solving the problem

Saturday, October 16, 2010

Page 7: Functionality Focused Code Organization

what happens when the spec evolves?

Saturday, October 16, 2010

Page 8: Functionality Focused Code Organization

Saturday, October 16, 2010

Page 9: Functionality Focused Code Organization

Saturday, October 16, 2010

Page 10: Functionality Focused Code Organization

is the codebase suitable for collaboration?

Saturday, October 16, 2010

Page 11: Functionality Focused Code Organization

$('#search').submit(function(e) { e.preventDefault(); resultsContainer.empty();

var term = $(this).find('input').val(); if (!$.trim(term)) { return; }

$.each(['search.news', 'search.web'], function(i, svcName) { $.getJSON( searchUrl, { q : buildQuery(term, svcName), format : 'json' },

function(resp) { $('<li><h3>' + svcName + '</h3><ul></ul></li>') .appendTo(resultsContainer) .find('ul') .html( $.map(resp.query.results.result, function(r) { return Mustache.to_html(resultsTpl, r); }).join('') ); } ); }); });

Saturday, October 16, 2010

Page 12: Functionality Focused Code Organization

is it testable?

Saturday, October 16, 2010

Page 13: Functionality Focused Code Organization

$('#search').submit(function(e) { e.preventDefault(); resultsContainer.empty();

var term = $(this).find('input').val(); if (!$.trim(term)) { return; }

$.each(['search.news', 'search.web'], function(i, svcName) { $.getJSON( searchUrl, { q : buildQuery(term, svcName), format : 'json' },

function(resp) { $('<li><h3>' + svcName + '</h3><ul></ul></li>') .appendTo(resultsContainer) .find('ul') .html( $.map(resp.query.results.result, function(r) { return Mustache.to_html(resultsTpl, r); }).join('') ); } ); }); });

how to test all of the important pieces of this?

Saturday, October 16, 2010

Page 14: Functionality Focused Code Organization

is it readable & maintainable?

Saturday, October 16, 2010

Page 15: Functionality Focused Code Organization

“Writing to be read means writing code ... with the idea that someone else will read it. is fact alone will make you edit and think of better ways to solve the problem you have at hand.”

Stoyan Stefanov, “JavaScript Patterns”

Saturday, October 16, 2010

Page 16: Functionality Focused Code Organization

maintainable code is ...

readable

consistent

predictable

looks like one person wrote it

documented

Saturday, October 16, 2010

Page 17: Functionality Focused Code Organization

$('#search').submit(function(e) { e.preventDefault(); resultsContainer.empty();

var term = $(this).find('input').val(); if (!$.trim(term)) { return; }

$.each(['search.news', 'search.web'], function(i, svcName) { $.getJSON( searchUrl, { q : buildQuery(term, svcName), format : 'json' },

function(resp) { $('<li><h3>' + svcName + '</h3><ul></ul></li>') .appendTo(resultsContainer) .find('ul') .html( $.map(resp.query.results.result, function(r) { return Mustache.to_html(resultsTpl, r); }).join('') ); } ); }); });

Saturday, October 16, 2010

Page 18: Functionality Focused Code Organization

// NAVIGATIONfunction togglePage(section) { // if the clicked section is already the current section AND we're in full page mode // minimize the current tab if (jQuery('#md_tab_'+ section).hasClass('current') && jQuery('#md_tab_'+ section + ' a').hasClass('md_fullpage')) { // alert('clicked section is current section AND fullpage mode is active; teaser should load'); // Minimize jQuery('#md_tabs_navigation a').removeClass('md_fullpage'); jQuery('.md_body').hide(); jQuery('#md_feature').slideDown('normal',function(){ var bodyContent = jQuery('#md_body_'+ section); bodyContent.fadeOut('normal',function(){ jQuery('#md_tabs_navigation a').each(function(){ var thisSection = jQuery(this).html().replace('<span<','').replace('<\/span<',''); var thisSection_comp = thisSection.toLowerCase().replace(' ','_'); jQuery('#md_body_'+ thisSection_comp).load( '/app/modules/info/loadTeaser.php?sect='+ thisSection_comp, function(){ tb_init('.md_body a.thickbox, .md_body area.thickbox, .md_body input.thickbox'); bodyContent.animate({ height: 'toggle', opacity: 'toggle' },"slow"); } ); }); }); }); jQuery('#expandtabs span').empty().append('Expand Tabs'); } else { // if the clicked section is NOT the current section OR we're NOT in full page mode // then let's go to full page mode and show the whole tab // Maximize // alert('clicked section is not the current section OR full page mode is not active; full section should load'); jQuery('#md_tabs_navigation li').removeClass('current'); jQuery('#md_tab_'+ section).addClass('current'); jQuery('#md_tabs_navigation a').addClass('md_fullpage'); jQuery('.md_body').hide(); jQuery('#md_feature').slideUp('normal',function(){ var bodyContent = jQuery('#md_body_'+ section); bodyContent.fadeOut('normal',function(){ bodyContent.empty(); var pageLoader = 'info/loadSection.php?sect='+ section; if (section == 'contact_us') { pageLoader = 'contact/loadContactForm.php?form_id=1'; } bodyContent.load('/app/modules/'+ pageLoader,function(){ // ADD THICKBOXES tb_init('.md_body a.thickbox, .md_body area.thickbox, .md_body input.thickbox'); $recent_news_links = jQuery('ul.md_news li a.recent_news_link'); $recent_news_links .unbind('click') .each(function(){ var hrefMod = this.href; hrefMod = hrefMod.replace(/article/,'loadNews').replace(/storyid/,'id'); this.href = hrefMod; }) .click(function(){ var t = this.title || this.name || null; var a = this.href || this.alt; var g = this.rel || false; tb_show(t,a,g); this.blur(); return false; }); // ACCORDION jQuery('div.applemenu div.submenu').hide(); jQuery('div.applemenu div.silverheader < a').click( function(){ jQuery('div.silverheader').removeClass('selected'); jQuery(this).parent().addClass('selected'); var $nextDiv = jQuery(this).parent().next('div.submenu'); var $visibleSiblings = $nextDiv.siblings('div:visible.submenu');

Saturday, October 16, 2010

Page 19: Functionality Focused Code Organization

(By the way, buy this book.)

Saturday, October 16, 2010

Page 20: Functionality Focused Code Organization

Saturday, October 16, 2010

Page 21: Functionality Focused Code Organization

When the heavy lifting of manipulating, displaying, and interacting with data falls to the browser, it makes sense to reconsider the typical DOM-centric approach.

Saturday, October 16, 2010

Page 22: Functionality Focused Code Organization

there’s a better way**lots of them, in fact. this is one.

Saturday, October 16, 2010

Page 23: Functionality Focused Code Organization

Functionality-focused code organization means identifying the pieces of functionality in your application and teasing them apart.

Saturday, October 16, 2010

Page 24: Functionality Focused Code Organization

diversion: pubsub 101like custom events, but without the overhead!

Saturday, October 16, 2010

Page 25: Functionality Focused Code Organization

$('input.magic').click(function(e) { // publish a "topic" when something happens $.publish('/something/interesting', [ e.target.value ]);});

// register our interest in knowing when something happens$.subscribe('/something/interesting', function(val) { alert(val);});

a simple pubsub example

Saturday, October 16, 2010

Page 26: Functionality Focused Code Organization

$('input.magic').click(function(e) { $(document).trigger('/something/interesting', [ this.value ]);});

$(document).bind('/something/interesting', function(e, val) { alert(val);});

pubsub with custom events works too

Saturday, October 16, 2010

Page 27: Functionality Focused Code Organization

In jQuery itself, $.fn.ajaxStart and $.fn.ajaxStop basically let you subscribe to topics published by jQuery’s underlying Ajax code.

Saturday, October 16, 2010

Page 28: Functionality Focused Code Organization

diversion: require.defpossibly my new favorite thing

(also a great tool for this modularization stuff)

Saturday, October 16, 2010

Page 29: Functionality Focused Code Organization

require.def() De#nes a function to be run when the module is included. e function can return a value, but it doesn’t have to. If dependencies are speci#ed, they’re available to the function as arguments.

Saturday, October 16, 2010

Page 30: Functionality Focused Code Organization

pattern: object Returns an object, though the de#nition function can close other variables that won’t be visible outside the function.

Saturday, October 16, 2010

Page 31: Functionality Focused Code Organization

require.def(function() { var privateThing = 'myPrivateThing',

privateObj = { maxLength : 5,

setPrivateThing : function(val) { if (val.length > this.maxLength) { console.log('TOO MUCH'); return; }

privateThing = val; },

otherMethod : function() { console.log(privateThing); } };

return { setPrivateThing : $.proxy(privateObj, 'setPrivateThing'), publicMethod : $.proxy(privateObj, 'otherMethod') };});

closes private vars, returns a public API

Saturday, October 16, 2010

Page 32: Functionality Focused Code Organization

pattern: factory Returns a function, that, when called, returns an instance of an object that is de#ned inside the module. e factory function may optionally bake in instance property options or overrides. e base object is not exposed publicly.

Saturday, October 16, 2010

Page 33: Functionality Focused Code Organization

require.def(function(){ var Person = { intro : 'My name is ', outro : '. You killed my father. Prepare to die.', speak : function() { console.log( this.intro, this.firstName, this.lastName, this.outro ); } }; return function(config) { return $.extend(Object.create(Person), { firstName : config.firstName, lastName : config.lastName }); };});

returns a “factory” for creating Person instances

Saturday, October 16, 2010

Page 34: Functionality Focused Code Organization

Functionality-focused code organization means identifying the pieces of functionality in your application and teasing them apart.

Saturday, October 16, 2010

Page 35: Functionality Focused Code Organization

mediators

views

services

Saturday, October 16, 2010

Page 36: Functionality Focused Code Organization

Saturday, October 16, 2010

Page 37: Functionality Focused Code Organization

searcher

searcher

search input

results

• user submits search form

• user input is validated

• if input is valid, the search service is contacted

• when the search service returns results, they are displayed in the results container

Saturday, October 16, 2010

Page 38: Functionality Focused Code Organization

search page mediator

searcher service

searcher service

search input view

results view

• sets up search input form• listens for user to submit search form• verifies form data• announces the user’s search if it is valid (non-empty)

• sets up views and services• brokers communication between views and services

• provides an API for mediators to communicate with• performs searches and pre-processes results into a consistent format• accepts callback to allow results to be used by mediator

• provides an API for mediators to add results and clear the results container• listens for user interaction with results and broadcasts information about it to be handled by the mediator

results service

• provides an API for mediators to use to register user interaction with results, and to get information about those interactions later

Saturday, October 16, 2010

Page 39: Functionality Focused Code Organization

mediators set up views and services, and broker communications between them, eliminating the need for direct communication

Saturday, October 16, 2010

Page 40: Functionality Focused Code Organization

views display data, observe user input, and broadcast messages that mediators can react to; may also provide an API for updating data

Saturday, October 16, 2010

Page 41: Functionality Focused Code Organization

views in our sample application

Saturday, October 16, 2010

Page 42: Functionality Focused Code Organization

services manage data & state, exposing a limited public API for mediators

Saturday, October 16, 2010

Page 43: Functionality Focused Code Organization

search page mediator

searcher service

searcher service

search input view

results view

• sets up search input form• listens for user to submit search form• verifies form data• announces the user’s search if it is valid (non-empty)

• sets up views and services• brokers communication between views and services

• provides an API for mediators to communicate with• performs searches and pre-processes results into a consistent format• accepts callback to allow results to be used by mediator

• provides an API for mediators to add results and clear the results container• listens for user interaction with results and broadcasts information about it to be handled by the mediator

results service

• provides an API for mediators to use to register user interaction with results, and to get information about those interactions later

Saturday, October 16, 2010

Page 44: Functionality Focused Code Organization

user requests page

app mediator

page mediator

app mediator hands request to appropriatepage mediator

service

service

service

view

view

view

page mediator sets up services that will be required for the page

page mediator sets up views that will be required

for the page

view

s an

d se

rvic

es

DO

NO

T co

mm

unic

ate

dire

ctly

Saturday, October 16, 2010

Page 45: Functionality Focused Code Organization

http://github.com/rmurphey/ffco

sample app

Saturday, October 16, 2010

Page 46: Functionality Focused Code Organization

Saturday, October 16, 2010

Page 47: Functionality Focused Code Organization

mediatorsapp mediator, page mediator

viewsmessage, recent searches, search input, results

servicessearchers, results

Saturday, October 16, 2010

Page 48: Functionality Focused Code Organization

MOAR FEATURES PLZ?

indicate search term in URL

persist recent searches across page reloads

tabbed search results view

improve the code I didn’t show you

Saturday, October 16, 2010

Page 49: Functionality Focused Code Organization

rebeccamurphey.com

blog.rebeccamurphey.com

@rmurphey

http://github.com/rmurphey/ffco

http://spkr8.com/t/4650

http://pinboard.in/u:rmurphey/t:ffco/

Saturday, October 16, 2010