Slide 1

Slide 1 text

Beyond the DOM: Sane Structure for JS Apps Rebecca Murphey • jQuery Conference 2012 • San Francisco Thursday, June 28, 12

Slide 2

Slide 2 text

rmurphey.com • @rmurphey • bocoup.com Thursday, June 28, 12

Slide 3

Slide 3 text

function ObjInlineUp(e) { if (is.ie) e = event if (is.ie && ! is.ieMac && e.button != 1 && e.button != 2) return if (is.ieMac && e.button != 0) return if (is.ns && ! is.ns4 && ! is.nsMac && e.button != 0 && e.button != 2) return if (is.ns4 && e.which != 1 && e.which != 3) return if ((!is.ns4 && e.button == 2) || (is.ns4 && e.which == 3)) { if (this.hasOnRUp) { document.oncontextmenu = ocmNone this.onRUp() setTimeout("document.oncontextmenu = ocmOrig", 100) } } else if (this.hasOnUp) this.onUp() } Thursday, June 28, 12

Slide 4

Slide 4 text

Thursday, June 28, 12

Slide 5

Slide 5 text

Thursday, June 28, 12

Slide 6

Slide 6 text

Search
    Thursday, June 28, 12

    Slide 7

    Slide 7 text

    $("#searchForm form").submit(function(e) { alert('submit'); e.preventDefault(); var term = $('#searchForm input').val(), req = $.getJSON('http://search.twitter.com/search.json?callback=?&q=' + encodeURIComponent(term)); req.then(function(resp) { var resultsHTML = $.map(resp.results, function(r) { return '
  • ' + '

    ' + r.text + '

    ' + '

    ' + r.from_user + '

    ' + '
  • '; }).join(''); $('#searchResults').html(resultsHTML); }); }); Thursday, June 28, 12

    Slide 8

    Slide 8 text

    Thursday, June 28, 12

    Slide 9

    Slide 9 text

    Thursday, June 28, 12

    Slide 10

    Slide 10 text

    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('

    Slide 11

    Slide 11 text

    Thursday, June 28, 12

    Slide 12

    Slide 12 text

    search data search input search results $("#searchForm form").submit(function(e) { alert('submit'); e.preventDefault(); var term = $('#searchForm input').val(), req = $.getJSON('http://search.twitter.com encodeURIComponent(term)); req.then(function(resp) { var resultsHTML = $.map(resp.results, functi return '
  • ' + '

    ' + r.text + '

    ' + '

    ' + r.from_user + ' '

  • '; }).join(''); $('#searchResults').html(resultsHTML); }); }); Thursday, June 28, 12

    Slide 13

    Slide 13 text

    Thursday, June 28, 12

    Slide 14

    Slide 14 text

    Thursday, June 28, 12

    Slide 15

    Slide 15 text

    MVC Thursday, June 28, 12

    Slide 16

    Slide 16 text

    MV* Thursday, June 28, 12

    Slide 17

    Slide 17 text

    MVWTF Thursday, June 28, 12

    Slide 18

    Slide 18 text

    Thursday, June 28, 12

    Slide 19

    Slide 19 text

    Thursday, June 28, 12

    Slide 20

    Slide 20 text

    define([ 'jquery', 'text!template.html' ], function($, html) { return function() { $('body').append(html); }; }); Thursday, June 28, 12

    Slide 21

    Slide 21 text

    Thursday, June 28, 12

    Slide 22

    Slide 22 text

    require.config({ deps : [ 'main' ], paths : { // JavaScript folders lib : '../lib', plugins : '../lib/plugins', tests : '../tests', app : '.', // Libraries jquery : '../lib/jquery', text : '../lib/plugins/text' } }); Thursday, June 28, 12

    Slide 23

    Slide 23 text

    require([ 'use!backbone', 'jquery', 'router', 'models/app' ], function(B, $, Router, app) { $(function() { app.router = new Router(); B.history.start(); }); }); app/main Thursday, June 28, 12

    Slide 24

    Slide 24 text

    Thursday, June 28, 12

    Slide 25

    Slide 25 text

    Thursday, June 28, 12

    Slide 26

    Slide 26 text

    user interface display data, announce user interaction, and await further instruction managing state manage application state and communicate with the server brokering communication transport messages between user interface and state management Thursday, June 28, 12

    Slide 27

    Slide 27 text

    app/views/results app/views/searchForm app/views/recentSearches Thursday, June 28, 12

    Slide 28

    Slide 28 text

    #mainbar #sidebar app/controllers/search Thursday, June 28, 12

    Slide 29

    Slide 29 text

    searches collection keeps track of recent search terms search data collection fetches results from the server for a given search term app model keeps track of general application state, including the current search search model for representing individual searches Thursday, June 28, 12

    Slide 30

    Slide 30 text

    search controller searches collection app model app/views/results app/views/searchForm app/views/recentSearches search data server Thursday, June 28, 12

    Slide 31

    Slide 31 text

    $("#searchForm form").submit(function(e) { alert('submit'); e.preventDefault(); var term = $('#searchForm input').val(), req = $.getJSON('http://search.twitter.com/search.json?callba encodeURIComponent(term)); req.then(function(resp) { var resultsHTML = $.map(resp.results, function(r) { return '
  • ' + '

    ' + r.text + '

    ' + '

    ' + r.from_user + '

    ' + '
  • '; }).join(''); $('#searchResults').html(resultsHTML); }); }); Thursday, June 28, 12

    Slide 32

    Slide 32 text

    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('

    Slide 33

    Slide 33 text

    search controller searches collection app model app/views/results app/views/searchForm app/views/recentSearches search data server Thursday, June 28, 12

    Slide 34

    Slide 34 text

    events : { 'submit .search-form' : '_onSearch' }, _onSearch : function(e) { e.preventDefault(); if (this.disabled) { return; } var term = $.trim(this.$('.js-input').val()); if (!term) { return; } this._disable(); this.trigger('search', term); } app/views/searchForm Thursday, June 28, 12

    Slide 35

    Slide 35 text

    searchForm.on('search', update); function update(t) { var term = $.trim(t), existing = searches.where({ term : term }), dfd = $.Deferred(), search; app.set('currentSearch', term); if (term) { if (existing.length) { search = existing[0]; search.update(); } else { search = new Search({ term : term }); searches.add(search); } searchData.fetch({ data : { term : term } }) .then(dfd.resolve, dfd.reject) .always(searchForm.release); app/controllers/search Thursday, June 28, 12

    Slide 36

    Slide 36 text

    search controller searches collection app model app/views/results app/views/searchForm app/views/recentSearches search data server Thursday, June 28, 12

    Slide 37

    Slide 37 text

    searchForm.on('search', update); function update(t) { var term = $.trim(t), existing = searches.where({ term : term }), dfd = $.Deferred(), search; app.set('currentSearch', term); if (term) { if (existing.length) { search = existing[0]; search.update(); } else { search = new Search({ term : term }); searches.add(search); } searchData.fetch({ data : { term : term } }) .then(dfd.resolve, dfd.reject) .always(searchForm.release); app/controllers/search Thursday, June 28, 12

    Slide 38

    Slide 38 text

    this.bindTo(this.searchData, 'add change', this._update); this.bindTo(this.searchData, 'fetching', function() { this._empty(); this.reset(); }); app/views/results Thursday, June 28, 12

    Slide 39

    Slide 39 text

    Thursday, June 28, 12

    Slide 40

    Slide 40 text

    function update(t) { var term = $.trim(t), existing = searches.where({ term : term }), search; app.set('currentSearch', term); if (existing.length) { search = existing[0]; search.update(); } else { search = new Search({ term : term }); searches.add(search); } searchData.fetch({ data : { term : term } }) .always(searchForm.release); app.router.navigate('search/' + term); } Thursday, June 28, 12

    Slide 41

    Slide 41 text

    describe("#update", function() { it("should update the time", function(done) { var search = new Search(), oldTime = search.get('time'); setTimeout(function() { search.update(); expect(search.get('time')).to.be.greaterThan(oldTime); done(); }, 1000); }); }); Thursday, June 28, 12

    Slide 42

    Slide 42 text

    it("should update when there is a new search", function() { expect(el.html()).not.to.contain('baz'); rs.currentSearch = function() { return 'baz'; }; rs.searches.add({ term : 'baz' }); expect(el.html()).to.contain('baz'); expect(el.find('.active').html()).to.contain('baz'); }); Thursday, June 28, 12

    Slide 43

    Slide 43 text

    training.bocoup.com Coming Up Foundations of Programming JavaScript Writing Testable JavaScript & lots more Thursday, June 28, 12

    Slide 44

    Slide 44 text

    thanks. rmurphey.com • @rmurphey • bocoup.com github.com/rmurphey/srchr-demo srchr-demo.nodejitsu.com Thursday, June 28, 12