Single Page Web Applications with CoffeeScript, Backbone and Jasmine

34,644 views

Published on

Published in: Technology, Education
4 Comments
60 Likes
Statistics
Notes
  • CoffeeScript: Accelerated JavaScript Development --- http://amzn.to/1nZ06aZ
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • CoffeeScript in Action --- http://amzn.to/1SaSGft
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • The Little Book on CoffeeScript --- http://amzn.to/1T2KWys
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Can you upload the PDF version? Thanks
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
No Downloads
Views
Total views
34,644
On SlideShare
0
From Embeds
0
Number of Embeds
10,572
Actions
Shares
0
Downloads
467
Comments
4
Likes
60
Embeds 0
No embeds

No notes for slide
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • Single Page Web Applications with CoffeeScript, Backbone and Jasmine

    1. 1. Single Page Web ApplicationsCoffeeScript + Backbone.js + Jasmine BDD @pirelenito github.com/pirelenito #tdc2011
    2. 2. Who am I?
    3. 3. What can you expect?• What I am doing?• Why is CoffeeScript so awesome?• The era before Backbone;• Fix browser spaghetti code with Backbone;• Be professional: with Jasmine BDD;
    4. 4. Assumptions• You know your Javascript;• and your JQuery;• You developed for the web before.
    5. 5. What I am doing?
    6. 6. Fullscreen MapGeo CRUDsNo refreshes
    7. 7. Fullscreen MapGeo CRUDsNo refreshesCoffeeScriptBackbone.js
    8. 8. Fullscreen Map Geo CRUDs No refreshes CoffeeScript Backbone.js JSONRest API Ruby on Rails
    9. 9. Why is CoffeeScript so awesome?
    10. 10. I like to choose my tools!
    11. 11. I can to that on the Server!
    12. 12. Browser?Javascript Only!
    13. 13. Browser?Javascript Only!NO MOAR!
    14. 14. Too error-prone.
    15. 15. Semicolon insertionreturn return {{ a: 10 a: 10 };};
    16. 16. Semicolon insertionreturn ; return {{ a: 10 a: 10 };};
    17. 17. Global variablesfunction messWithGlobal() { a = 10;}
    18. 18. Global variablesfunction messWithGlobal() { a = 10;}messWithGlobal();alert(a);
    19. 19. Global variablesfunction messWithGlobal() { a = 10;}messWithGlobal();alert(a);
    20. 20. Global variablesfunction messWithGlobal() { a = 10;}messWithGlobal();alert(a);function messWithGlobal() { var a = 10;}
    21. 21. } square = function(x) { return x * x; }; list = [1, 2, 3, 4, 5]; math = { root: Math.sqrt, Too verbose! square: square, cube: function(x) { return x * square(x); } }; race = function() { var runners, winner; winner = arguments[0], runners = 2 <=arguments.length ? __slice.call(arguments,1) : []; return print(winner, runners); };
    22. 22. Awesome platform.
    23. 23. “I Think Coffeescript is clearly good stuff” Douglas Crockford Javascript Programming Style And Your Brain
    24. 24. Remember this?return return {{ a: 10 a: 10 };};
    25. 25. Coffeescript:return return a: 10 a: 10
    26. 26. Coffeescript:return return a: 10 a: 10 It has no semicolon!
    27. 27. How it fixes globals?
    28. 28. Never write var again! messWithGlobal = -> a = 10
    29. 29. Never write var again! messWithGlobal = -> a = 10messWithGlobal = function() { var a; return a = 10;};
    30. 30. Compiles with scopea = 10
    31. 31. Compiles with scopea = 10(function() { var a; a = 10;}).call(this);
    32. 32. You want global? Go explicit!window.messWithGlobal = -> a = 10
    33. 33. You want global? Go explicit!window.messWithGlobal = -> a = 10(function() { window.messWithGlobal = function() { var a; return a = 10; };}).call(this);
    34. 34. Functions as callbacks$(.button).click(function() { return alert(clicked!);});
    35. 35. Functions as callbacks$(.button).click(function() { return alert(clicked!);});$(.button).click -> alert clicked!
    36. 36. Block by identation if (somethingIsTrue) { complexMath = 10 * 3; }
    37. 37. Block by identation if (somethingIsTrue) { complexMath = 10 * 3; } if somethingIsTrue complexMath = 10 * 3
    38. 38. Classesclass MyClass attribute: value constructor: -> @objectVariable = Dude myMethod: -> # this.objectVariable @objectVariable
    39. 39. Classesclass MyClass attribute: value constructor: -> @objectVariable = Dude myMethod: -> # this.objectVariable @objectVariableobject = new MyClassobject.myMethod()object.objectVariable # Dudeobject.attribute
    40. 40. Compiled class:(function() { var MyClass; MyClass = (function() { MyClass.prototype.attribute = value; function MyClass() { this.objectVariable = Dude; } MyClass.prototype.myMethod = function() { return this.objectVariable; }; return MyClass; })();}).call(this);
    41. 41. Rangeslist = [1..5]
    42. 42. Rangeslist = [1..5]var list;list = [1, 2, 3, 4, 5];
    43. 43. Expressions!vehicles = for i in [1..3] "Vehicle#{i}"
    44. 44. Expressions!vehicles = for i in [1..3] "Vehicle#{i}"vehicles[Vehicle1, Vehicle2, Vehicle3]
    45. 45. More awesomeness...number = -42 if oppositemath = square: (x) -> x * xrace = (winner, runners...) -> print winner, runnersalert "I knew it!" if elvis?squares = (math.square num for num in list)
    46. 46. Don’t use --bare
    47. 47. Namespaces are coolclass namespace(views).MyView
    48. 48. Namespaces are coolclass namespace(views).MyViewnew oncast.views.MyView()
    49. 49. Namespaces are coolclass namespace(views).MyViewnew oncast.views.MyView()@.namespace = (name) -> @.oncast ||= {} return @.oncast[name] ||= {} if name return @.oncast
    50. 50. No more hidden bugs on my client code!
    51. 51. CoffeeScript will makeyou a better Javascript programer.
    52. 52. The era before Backbone.
    53. 53. Rails did the rendering Rails Coffeescript
    54. 54. Rails did the rendering Rails$(.new).load "/cars/new", prepare Coffeescript
    55. 55. Rails did the rendering Rails$(.new).load "/cars/new", prepare Coffeescript
    56. 56. Rails did the rendering Rails <form> <input name=name> ... </form>$(.new).load "/cars/new", prepare Coffeescript
    57. 57. Rails did the rendering Rails <form> <input name=name> ... </form>$(.new).load "/cars/new", prepare Coffeescript
    58. 58. Advantages• Simple;• Used tools we knew;• i18n.
    59. 59. Coffeescript ActionsActionActivationHandler One active at timeEditRefAction ListRefAction ...
    60. 60. Mapped an action onour Rail’s Controller
    61. 61. Action Abstraction• Would handle the ‘remote’ rendering;• Initialize the rich components on the DOM;• JQuery heavy.
    62. 62. Problems?
    63. 63. One Action at a time!
    64. 64. Difficult to keep thinks in sync.
    65. 65. Difficult to keep thinks in sync. No real state on the browser.
    66. 66. Slow (web 1.0)
    67. 67. “Not surprisingly, were finding ourselves solving similar problems repeatedly.” Henrik Joreteghttp://andyet.net/blog/2010/oct/29/building-a-single-page-app-with-backbonejs-undersc/
    68. 68. Fix browser spaghetticode with Backbone.
    69. 69. Why Backbone?
    70. 70. Gives enough structure
    71. 71. Very lightweight
    72. 72. Easy to get started!
    73. 73. Why not GWT(for instance)?
    74. 74. Doesn’t hide the DOM You are doing real web development.
    75. 75. Works nicely with CoffeeScript Inheritance. https://github.com/documentcloud/backbone/blob/master/test/model.coffee
    76. 76. Works nicely with CoffeeScript Inheritance.class Car extends Backbone.Model https://github.com/documentcloud/backbone/blob/master/test/model.coffee
    77. 77. Ok, but what is it?
    78. 78. Four Abstractions• Collection;• Model;• Router;• View;
    79. 79. Models / Collectionsclass Reference extends Backbone.Model urlRoot: "/references"
    80. 80. Models / Collectionsclass Reference extends Backbone.Model urlRoot: "/references"class References extends Backbone.Collection model: Reference url: /references Model Model Model Collection
    81. 81. Fetch Collection# creates a new collectionreferences = new References# fetch data asynchronouslyreferences.fetch()
    82. 82. Fetch Collection# creates a new collectionreferences = new References# fetch data asynchronouslyreferences.fetch() [{ id: 1 JSON name: "Paulos House", latitude: 10, longitude: 15 }]
    83. 83. Model attributes
    84. 84. Model attributes# get reference with id 1reference = references.get 1
    85. 85. Model attributes# get reference with id 1reference = references.get 1# get the name property of the modelname = reference.get name
    86. 86. Model attributes# get reference with id 1reference = references.get 1# get the name property of the modelname = reference.get name# change the name property of the modelreference.set name: new name
    87. 87. Model attributes# get reference with id 1reference = references.get 1# get the name property of the modelname = reference.get name# change the name property of the modelreference.set name: new name# save the model to the serverreference.save()
    88. 88. Why use model.set ?reference.set name: new name
    89. 89. Events!reference.bind change, (name)-> alert name
    90. 90. Events!reference.bind change, (name)-> alert namereference.set name: new name
    91. 91. Events!reference.bind change, (name)-> alert namereference.set name: new name
    92. 92. Viewsclass SaveButtonView extends Backbone.View className: save-button tagName: div events: click: _click render: -> $(@el).html "<input type=button value=Save></input>" return @ _click: -> @model.save()
    93. 93. Viewsclass SaveButtonView extends Backbone.View className: save-button tagName: div events: click: _click render: -> $(@el).html "<input type=button value=Save></input>" return @ _click: -> @model.save()
    94. 94. Viewsclass SaveButtonView extends Backbone.View className: save-button tagName: div events: click: _click render: -> $(@el).html "<input type=button value=Save></input>" return @ _click: -> @model.save()
    95. 95. Viewsclass SaveButtonView extends Backbone.View className: save-button tagName: div events: click: _click render: -> $(@el).html "<input type=button value=Save></input>" return @ _click: -> @model.save()
    96. 96. Viewsclass SaveButtonView extends Backbone.View className: save-button tagName: div events: click: _click render: -> $(@el).html "<input type=button value=Save></input>" return @ _click: -> @model.save()
    97. 97. Viewsclass SaveButtonView extends Backbone.View className: save-button tagName: div events: click: _click render: -> $(@el).html "<input type=button value=Save></input>" return @ _click: -> @model.save()
    98. 98. Using a View
    99. 99. Using a View# creates a new instance of the viewview = new SaveButtonView()
    100. 100. Using a View# creates a new instance of the viewview = new SaveButtonView()# render itview.render()
    101. 101. Using a View# creates a new instance of the viewview = new SaveButtonView()# render itview.render()# append the content of the view on the doc$(.some-div).html view.el
    102. 102. ‘Events’ is the magic element!
    103. 103. ‘Events’ is the magic element! They will keep you in sync!
    104. 104. View Model
    105. 105. Click!View Model
    106. 106. Click! event changes the modelView Model
    107. 107. Click! model.set name: new name event changes the modelView Model
    108. 108. Click! model.set name: new name event changes the modelView Model event changes the view
    109. 109. Click! model.set name: new name event changes the modelView Model event changes the view keeps everything in sync
    110. 110. Routersclass ReferencesRouter extends Backbone.Router routes: references: index initialize: (options)-> @listPanelView = options.listPanelView index: -> @listPanelView.renderReferences()
    111. 111. Routersclass ReferencesRouter extends Backbone.Router routes: references: index initialize: (options)-> @listPanelView = options.listPanelView index: -> @listPanelView.renderReferences()
    112. 112. Routersclass ReferencesRouter extends Backbone.Router routes: references: index initialize: (options)-> @listPanelView = options.listPanelView index: -> @listPanelView.renderReferences()
    113. 113. This is just the basics!
    114. 114. Lessons Learned
    115. 115. DOM is not always your friend!
    116. 116. DOM is not always your friend!# will be 0 unless it is attached to the# document$(@el).height()
    117. 117. Always do view.render() Before$(.some-div).html view.el
    118. 118. Always return this.
    119. 119. Always return this.new SaveButtonView().highlight().render().el
    120. 120. Routers should only route!
    121. 121. Routers should only route!index: -> @listPanelView.renderReferences()
    122. 122. You don’t need anAction abstraction!!!
    123. 123. The View will represent one model/collection forever!
    124. 124. Broken events# append the views element to the document$(.div).append view.el# remove it$(.div).empty# add it again$(.div).append view.el View events (click, change) will no longer work.
    125. 125. Collection.getOrFetchclass Posts extends Backbone.Collection url: "/posts" getOrFetch: (id) -> if @get(id) post = @get id else post = new Post { id : id } @add post post.fetch() post http://bennolan.com/2011/06/10/backbone-get-or-fetch.html
    126. 126. View.fetchBackbone.View::fetch = (options={})-> return @ unless (@collection || @model) _(options).extend complete: => @removeLoading() @renderLoading() (@collection || @model).fetch options return @
    127. 127. View.fetchBackbone.View::fetch = (options={})-> return @ unless (@collection || @model) _(options).extend complete: => @removeLoading() @renderLoading() (@collection || @model).fetch options return @ view.fetch()
    128. 128. Lazy fetchingclass LazyFetchView extends Backbone.View render: -> if @model.isFullyFetched() # if the data is ready we render it $(@el).html ... else # otherwise we fetch the data # rendering the loading indicator @fetch() return @
    129. 129. Be professional: with Jasmine BDD.
    130. 130. Why Jasmine?
    131. 131. BDD
    132. 132. BDDrspec like!
    133. 133. given a delete button viewwhen the user clicks the viewthen it should delete the model
    134. 134. Jasmine: given a delete button view when the user clicks the view then it should delete the modeldescribe DeleteButtonView, -> context when the user clicks the view, => it should delete the model
    135. 135. describe DeleteButton, -> context when the user clicks the view, => it should delete the model
    136. 136. describe DeleteButton, -> context when the user clicks the view, => it should delete the model, => expect(@model.delete).toHaveBeenCalledOnce()
    137. 137. describe DeleteButton, -> context when the user clicks the view, => it should delete the model, => expect(@model.delete).toHaveBeenCalledOnce() http://sinonjs.org/ https://github.com/froots/jasmine-sinon
    138. 138. describe DeleteButton, -> beforeEach => @model = new Backbone.Model @deleteButton = new DeleteButton(model: @model) context when the user clicks the view, => it should delete the model, => expect(@model.delete).toHaveBeenCalledOnce()
    139. 139. describe DeleteButton, -> beforeEach => @model = new Backbone.Model @deleteButton = new DeleteButton(model: @model) context when the user clicks the view, => beforeEach => sinon.spy @model, delete it should delete the model, => expect(@model.delete).toHaveBeenCalledOnce()
    140. 140. describe DeleteButton, -> beforeEach => @model = new Backbone.Model @deleteButton = new DeleteButton(model: @model) context when the user clicks the view, => beforeEach => sinon.spy @model, delete @deleteButton.render() $(@deleteButton.el).click() it should delete the model, => expect(@model.delete).toHaveBeenCalledOnce()
    141. 141. Run it headless! Text http://johnbintz.github.com/jasmine-headless-webkit/
    142. 142. http://tinnedfruit.com/2011/03/03/testing-backbone-apps- with-jasmine-sinon.html
    143. 143. Further reading• https://github.com/creationix/haml-js• https://github.com/fnando/i18n-js• http://documentcloud.github.com/underscore/• https://github.com/velesin/jasmine-jquery
    144. 144. So long and thanks for all the fish! @pirelenito github.com/pirelenito about.me/pirelenito

    ×