Slide 1

Slide 1 text

EOA and Client side applications Mihael Konjević - @mihaelkonjevic

Slide 2

Slide 2 text

WHO AM I? • JavaScript developer at Bitovi • Development, training and consulting • JavaScriptMVC, CanJS, StealJS, jQuery++... http://javascriptmvc.com http://canjs.us http://jquerypp.com

Slide 3

Slide 3 text

WHAT WE WANT?

Slide 4

Slide 4 text

WHAT WE WANT? • Ease of development

Slide 5

Slide 5 text

WHAT WE WANT? • Ease of development • Maintainability of code

Slide 6

Slide 6 text

WHAT WE WANT? • Ease of development • Maintainability of code • Testability

Slide 7

Slide 7 text

SRCHR APP

Slide 8

Slide 8 text

WHAT WE’LL COVER

Slide 9

Slide 9 text

WHAT WE’LL COVER • Modules and isolation

Slide 10

Slide 10 text

WHAT WE’LL COVER • Modules and isolation • Directory structure

Slide 11

Slide 11 text

WHAT WE’LL COVER • Modules and isolation • Directory structure • Application glue

Slide 12

Slide 12 text

MODULES Perfect module is dumb, lonely and replaceable

Slide 13

Slide 13 text

Modules should be Dumb

Slide 14

Slide 14 text

Modules should be Lonely

Slide 15

Slide 15 text

Modules should be Replaceable

Slide 16

Slide 16 text

Application structure

Slide 17

Slide 17 text

Search module • User can select services to search • User can enter the search term and trigger search

Slide 18

Slide 18 text

History module • Keep track of searches • On click redo search • User can delete search

Slide 19

Slide 19 text

Search Results • Perform search • List results

Slide 20

Slide 20 text

Tabs • Clicking on the tabs shows search results for that service

Slide 21

Slide 21 text

Events

Slide 22

Slide 22 text

Events • DOM events

Slide 23

Slide 23 text

Events • DOM events • Synthetic events on DOM elements

Slide 24

Slide 24 text

Events • DOM events • Synthetic events on DOM elements • Synthetic events on JavaScript objects

Slide 25

Slide 25 text

Search module • Triggers search event on the Search model $([Models.Search]).trigger("search",search);

Slide 26

Slide 26 text

History module • Listens to search event on the search model • Triggers selected event on the clicked element "{Models.Search} search": function(el, ev, searchInst){ ! if(this.history.indexOf(searchInst) === -1){ ! ! this.history.push(searchInst) ! } }, "li click" : function(el, ev){ ! el.trigger("selected", el.data('search')) }

Slide 27

Slide 27 text

Tabs module • Triggers activated event on clicked tab • Listens to activated event on clicked tab • Triggers show event on search results element this.tab(el.addClass('active')).show().trigger("show"); "li click": function( el, ev ) { ! ev.preventDefault(); ! el.trigger("activate"); }, "li activate": function( el, ev ) { ! this.activate(el); },

Slide 28

Slide 28 text

Search Results module • Listens to show event on the element triggered by the tabs module • Listens to search event on the search model "{Models.Search} search": function(el, ev, searchInst){ ! this.currentSearch = searchInst.query; ! ... }, activate: function( el ) { ! this.tab(this.element.find('.active').removeClass('active')).hide(); ! this.tab(el.addClass('active')).show().trigger("show"); }

Slide 29

Slide 29 text

What about disabled tabs?

Slide 30

Slide 30 text

"li activate": function( el, ev ) { ! if(!el.hasClass('disabled')){ ! ! this.activate(el); ! } } What about disabled tabs?

Slide 31

Slide 31 text

Disabler module Disabler = can.Control({}, { ! "{Models.Search} search": function(el, ev, searchInst){ ! ! var types = searchInst.attr('types'); ! ! this.element.find('li').removeClass('disabled').map(function(i, el){ ! ! ! var $el = $(el); ! ! ! if(types.indexOf($el.data('service')) === -1){ ! ! ! ! $el.addClass('disabled'); ! ! ! } ! ! }); ! ! this.element.find('li:not(.disabled):first').trigger('activate') ! }, ! "li activate" : function(el, ev){ ! ! if(el.hasClass('disabled')){ ! ! ! ev.stopImmediatePropagation(); ! ! } ! } })

Slide 32

Slide 32 text

Application workflow

Slide 33

Slide 33 text

Events diagram

Slide 34

Slide 34 text

• Keep your modules dumb, isolated and replaceable • Define inputs and outputs • Use events instead callbacks Summary

Slide 35

Slide 35 text

The secret to building large apps is to never build large apps.

Slide 36

Slide 36 text

button.js jquery.ui.calendar.js contactmanager.js tabs.js jquery.js nav.js resizer.js \test button_test.js contactmanager.js tabs_test.js nav_test.js

Slide 37

Slide 37 text

\tabs tabs.js - the code for a tabs widget tabs.html - a demo page funcunit.html - a test page tabs_test.js - test code tabs.css - css for the tab

Slide 38

Slide 38 text

Assembling the app • load dependencies • initialize the code \srchr \disabler \history \search \search_results search_results.js search_results.html search_results.css funcunit.html \tabs srchr.js srchr.html

Slide 39

Slide 39 text

Gluing it together var searchController = new Search($("#searchArea")) new SearchResult($('#upcoming'), { modelType: Models.Upcoming, resultView: 'searchResultUpcomingEJS' }) new SearchResult($('#twitter'), { modelType: Models.Twitter, resultView: 'searchResultTwitterEJS' }) new Disabler($('#resultsTabs')) new Tabs($("#resultsTabs")) new History($('#history')) $('#history').bind("selected", function(ev, search){ ! searchController.val(search) })

Slide 40

Slide 40 text

Questions?