Functionality Focused Code Organization
-
Upload
rebecca-murphey -
Category
Technology
-
view
13.253 -
download
0
description
Transcript of Functionality Focused Code Organization
Functionality-Focused Code OrganizationRebecca Murphey • @rmurphey • rebeccamurphey.comjQuery Boston 2010
Saturday, October 16, 2010
Saturday, October 16, 2010
Saturday, October 16, 2010
object laterals literals
module pattern
revealing module pattern
etc.
Saturday, October 16, 2010
Saturday, October 16, 2010
$('#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
what happens when the spec evolves?
Saturday, October 16, 2010
Saturday, October 16, 2010
Saturday, October 16, 2010
is the codebase suitable for collaboration?
Saturday, October 16, 2010
$('#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
is it testable?
Saturday, October 16, 2010
$('#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
is it readable & maintainable?
Saturday, October 16, 2010
“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
maintainable code is ...
readable
consistent
predictable
looks like one person wrote it
documented
Saturday, October 16, 2010
$('#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
// 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
(By the way, buy this book.)
Saturday, October 16, 2010
Saturday, October 16, 2010
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
there’s a better way**lots of them, in fact. this is one.
Saturday, October 16, 2010
Functionality-focused code organization means identifying the pieces of functionality in your application and teasing them apart.
Saturday, October 16, 2010
diversion: pubsub 101like custom events, but without the overhead!
Saturday, October 16, 2010
$('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
$('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
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
diversion: require.defpossibly my new favorite thing
(also a great tool for this modularization stuff)
Saturday, October 16, 2010
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
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
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
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
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
Functionality-focused code organization means identifying the pieces of functionality in your application and teasing them apart.
Saturday, October 16, 2010
mediators
views
services
Saturday, October 16, 2010
Saturday, October 16, 2010
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
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
mediators set up views and services, and broker communications between them, eliminating the need for direct communication
Saturday, October 16, 2010
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
views in our sample application
Saturday, October 16, 2010
services manage data & state, exposing a limited public API for mediators
Saturday, October 16, 2010
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
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
http://github.com/rmurphey/ffco
sample app
Saturday, October 16, 2010
Saturday, October 16, 2010
mediatorsapp mediator, page mediator
viewsmessage, recent searches, search input, results
servicessearchers, results
Saturday, October 16, 2010
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
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