Advertisement

JavaScript Best Practices, Backbone.js, and Mario for the PHP Develop

Symfony Developer at KnpUniversity
May. 24, 2012
Advertisement

More Related Content

Advertisement

JavaScript Best Practices, Backbone.js, and Mario for the PHP Develop

  1. JAVASCRIPT BEST PRACTICES & BACKBONE.JS FOR THE PHP DEVELOPER Ryan Weaver @weaverryan Thursday, May 24, 12
  2. 7 tips for writing JavaScript like a jerk + What to take and leave in Backbone.js Thursday, May 24, 12
  3. Who is this dude? • Co-author of the Symfony2 Docs • KnpLabs US - Symfony consulting, training, Kumbaya • Writer for KnpUniversity.com screencasts • Fiancee of the much more talented @leannapelham -----> June 9th, 2012! http://www.knplabs.com/en @weaverryan http://www.github.com/weaverryan Thursday, May 24, 12
  4. Chapter 1: Java$criptQuery Look mom, I’m a JavaScript professional @weaverryan Thursday, May 24, 12
  5. 1. jQuery(document).ready(...) 2. 1500 lines, with deeply nested anonymous functions 3. Profit! $$$ @weaverryan Thursday, May 24, 12
  6. Too soon? ... sry fb, luv u kthxbye @weaverryan Thursday, May 24, 12
  7. The brains behind this presentation... Thursday, May 24, 12
  8. source: http://www.geeky-gadgets.com/super-mario-bros-matryoshka-dolls/ Thursday, May 24, 12
  9. Thursday, May 24, 12
  10. <div class="mario box"> .mario <div class="luigi box"> .luigi <div class="kick-butt">...</div> <a href="#" class="peach box"> .peach </a> </div> </div> Thursday, May 24, 12
  11. Let’s kick some arse! jQuery(document).ready(function() { var $luigi = $('.event-details .luigi'); $luigi.click(function() { $(this).addClass('active'); return false; }); }); Thursday, May 24, 12
  12. DEMO!!!!! http://bit.ly/php-js-demo 0 - The starting point Thursday, May 24, 12
  13. Princess Pro-tip Instead of listening to this guy drone on, just find, fork and play with the code yourself! http://bit.ly/php-js-play Thursday, May 24, 12
  14. ‣ Click either .luigi or .peach ‣ the .luigi wrapper gets the active class Thursday, May 24, 12
  15. Mario’s bad JavaScript tip #1: Ignore the jQuery “event” object, it’s probably stupid... http://bit.ly/php-js-demo 01 - The jQuery Event @weaverryan Thursday, May 24, 12
  16. Events happen Thursday, May 24, 12
  17. Event Click! 1) You click! 2) An event travels up the tree Thursday, May 24, 12
  18. Browser event versus jQuery event ‣ The browser event contains all the information about what happened ‣the jQuery event cleans up cross- browser compatibility ugliness Thursday, May 24, 12
  19. $luigi.click(function(event) { // ... }); ‣ event.target: The actual element that received the click ‣ event.currentTarget: The element that this listener was attached to Thursday, May 24, 12
  20. $luigi.click(function(event) { // ... }); Our listener is registered on .luigi Thursday, May 24, 12
  21. If you click .peach: event.target: .peach event.currentTarget: .luigi Thursday, May 24, 12
  22. If you click .luigi: event.target: .luigi event.currentTarget: .luigi Thursday, May 24, 12
  23. event.currentTarget == this * unless you screw with scope, which we’ll see! Thursday, May 24, 12
  24. $luigi.click(function(event) { // these are the same! $(event.currentTarget) .addClass('active'); $luigi.addClass('active'); }); Thursday, May 24, 12
  25. Preventing the damn “#” on the URL $luigi.click(function() { $(this).addClass('active'); return false; }); Thursday, May 24, 12
  26. Princess Pro-tip return false stops propagation, and is a great way to screw with your teammate’s events http://fuelyourcoding.com/jquery-events-stop-misusing-return-false/ Thursday, May 24, 12
  27. ... but if you like your coworkers... $luigi.click(function(event) { event.preventDefault(); // ... }); The event keeps traveling up the DOM Thursday, May 24, 12
  28. Bad JavaScript moral #1: jQuery’s event object is a dangerous source of useful information and control @weaverryan Thursday, May 24, 12
  29. Mario bad JavaScript tip #2: Avoid using objects: they threaten to organize your code... and are creepy... http://bit.ly/php-js-demo 02 - Basic jQuery Objects @weaverryan Thursday, May 24, 12
  30. A PHP object @weaverryan Thursday, May 24, 12
  31. <?php class MagicBoxes { public function initializeClick($wrapper) { // ... } } $magicBoxes = new MagicBoxes(); $magicBoxes->initializeClick('something'); Thursday, May 24, 12
  32. A JavaScript object @weaverryan Thursday, May 24, 12
  33. var MagicBoxes = { someProperty: 0, initializeClick: function($container) { // ... } }; MagicBoxes.initializeClick(something); Thursday, May 24, 12
  34. Just like with PHP, you can choose to organize your code into objects and functions @weaverryan Thursday, May 24, 12
  35. var MagicBoxes = { initializeClick: function($container) { $container.find('.luigi') .click(this._handleLuigiClick); }, _handleLuigiClick: function(event) { event.preventDefault(); $luigi.addClass('active'); } }; jQuery(document).ready(function() { var $wrap = $('.mario-world'); MagicBoxes.initializeClick($wrap); }); Thursday, May 24, 12
  36. Princess Pro-tip Be careful with objects, they can increase readability, which threatens job security Thursday, May 24, 12
  37. Objects: Advantages ‣ All of the logic of this “mini-app” is wrapped up inside a named object ‣ The jQuery document.ready is skinny: contains simple, descriptive calls to the object ‣ The object methods are reusable Thursday, May 24, 12
  38. Object Scope // hola! I’m just a local variable var MagicBoxes = { // ... }; // I’m a global variable, available anywhere window.MagicBoxes = { }; Thursday, May 24, 12
  39. the “window” window.foo = 'foo-window'; console.log(foo); // prints "foo-window" bar = 'bar-global'; console.log(window.bar); // prints "bar-global" Thursday, May 24, 12
  40. Bad JavaScript moral #2: Using global objects risks organizing your code into separate, distinct units @weaverryan Thursday, May 24, 12
  41. Mario’s bad JavaScript tip #3: JavaScript’s “prototype” object model is too scary to use http://bit.ly/php-js-demo 03 - Intro to the JS Prototype @weaverryan Thursday, May 24, 12
  42. Everything is an object Thursday, May 24, 12
  43. An object with a property var object1 = { fooProperty: 'foo!' } console.log(object1.fooProperty); // prints “foo!” Thursday, May 24, 12
  44. Also an object with a property var alsoAnObject = function() { return 'foo-return!' }; alsoAnObject.barProperty = 'bar!'; console.log(alsoAnObject.barProperty); // prints “bar!” console.log(alsoAnObject()); // prints “foo-return!” Thursday, May 24, 12
  45. In PHP, we can create a class and then instantiate many instances @weaverryan Thursday, May 24, 12
  46. <?php class MagicBoxes { public function __construct($option) { // ... } } $magicBoxes1 = new MagicBoxes('foo'); $magicBoxes2 = new MagicBoxes('bar'); Thursday, May 24, 12
  47. How can we do this in JavaScript? @weaverryan Thursday, May 24, 12
  48. Imagine if we could do this craziness in PHP @weaverryan Thursday, May 24, 12
  49. ImaginationLand PHP code $magicBox = function($var) { $this->var = $var; $this->initialize(); }; $magicBox->prototype->initialize = function() { var_dump($this->var); }; $magicBoxObj = new $magicBox('something'); // causes "something" to be printed Thursday, May 24, 12
  50. Yep, that’s how it works in JavaScript! @weaverryan Thursday, May 24, 12
  51. #1: Create a function window.MagicBoxes = function($container) { this.$el = $container; this.initialize(); }; Thursday, May 24, 12
  52. #2: Add things to your future object MagicBoxes.prototype.initialize = function() { var $luigi = this.$el.find('.luigi'); $luigi.click(function(event) { event.preventDefault(); $(this).toggleClass('active'); }); }; Thursday, May 24, 12
  53. #2: Add things to your future object MagicBoxes.prototype.initialize = function() The “prototype” is a magic place where you stick “future” things that will become a part of the eventual new object Thursday, May 24, 12
  54. #3: Instantiate your new object jQuery(document).ready(function() { var $mario = $('.mario-world'); var magicBoxApp = new MagicBoxes($mario); }); Thursday, May 24, 12
  55. #4: Beers! @weaverryan Thursday, May 24, 12
  56. Princess Pro-tip Your speaker is a liar and a thief. Using the prototype is for sissies! Thursday, May 24, 12
  57. Bowser rebuttal Don’t listen to her! A gentleman and scholar named Garrison Locke is going to teach us the prototypical object model of JavaScript tomorrow at 11:30 AM Thursday, May 24, 12
  58. Peach come-back No you didn’t! Mario and I both agree that using objects in JavaScript is all weird! Viva the 1500 line jQuery document.ready! Thursday, May 24, 12
  59. Bowser final word Seriously, this is why I fight against you guys Thursday, May 24, 12
  60. Bowser final word Your ignorance may seem blissful, but you work as a detriment towards the larger cause of evolving and learning as a community Thursday, May 24, 12
  61. Bowser final word Thank God Garrison Locke will be teaching us Object- oriented JavaScript tomorrow at 11:30 AM. Thursday, May 24, 12
  62. Bowser final word He’s a much better speaker than this guy Thursday, May 24, 12
  63. Ryan does damage control on his presentation Dude, I’m right here Thursday, May 24, 12
  64. Purple is a manly color You really like that shirt... Thursday, May 24, 12
  65. Bad JavaScript moral #3: Don’t use jQuery’s prototype or go to Garrison Locke’s presentation tomorrow if you want unreadable JavaScript @weaverryan Thursday, May 24, 12
  66. Mario’s bad JavaScript tip #4: “this” probably always means “this”... don’t ask questions http://bit.ly/php-js-demo 04 - Scoping Concerns @weaverryan Thursday, May 24, 12
  67. ImaginationLand PHP code $magicBox = new MagicBoxes('foo'); $foo = new Foo(); // imaginary PHP function // this calls $magicBox->doSomething() // BUT, forces $this to actual be $foo inside // that object. Madness! call_func($magicBox, 'doSomething', $foo); Thursday, May 24, 12
  68. With JavaScript objects, “this” may not always be what you think it is @weaverryan Thursday, May 24, 12
  69. window.Boxes = function($container) { this.$el = $container; var $luigi = this.$el.find('.luigi'); $luigi.on('click', this._luigiClick); }; Boxes.prototype._luigiClick = function(event) { event.preventDefault(); this.luigiFights($(this)); }; Boxes.prototype.luigiFights = function($luigi) { $luigi.toggleClass('active'); }; Thursday, May 24, 12
  70. Is it our Boxes object? Boxes.prototype._luigiClick = function(event) { event.preventDefault(); this.luigiFights($(this)); }; Or is it the DOM element that triggered the jQuery event? Thursday, May 24, 12
  71. “this” is actually the DOM element that triggered the jQuery event Boxes.prototype._luigiClick = function(event) { event.preventDefault(); this.luigiFights($(this)); }; so “this” will not work pun intended... Thursday, May 24, 12
  72. window.Boxes = function($container) { this.$el = $container; var $luigi = this.$el.find('.luigi'); $luigi.on( 'click', $.proxy(this._luigiClick, this) ); }; Boxes.prototype._luigiClick = function(event) { event.preventDefault(); this.makeLuigiFight($(event.currentTarget)); }; Thursday, May 24, 12
  73. window.Boxes = function($container) { this.$el = $container; var $luigi = this.$el.find('.luigi'); $luigi.on( 'click', $.proxy(this._luigiClick, this) ); }; $.proxy forces _luigiClick to have “this” as this object when called Thursday, May 24, 12
  74. event.currentTarget returns the element that was registered on this event (formerly “this”) Boxes.prototype._luigiClick = function(event) { event.preventDefault(); this.makeLuigiFight($(event.currentTarget)); }; Thursday, May 24, 12
  75. Mario’s bad JavaScript tip #5: Avoid jQuery.extend so that you can repeat yourself as much as possible: DRY* * definitely repeat yourself http://bit.ly/php-js-demo @weaverryan 05 - jQuery extends Thursday, May 24, 12
  76. PHP ImaginationLand // foo has printFoo() method on it $foo = new Foo(); // bar has a printBar() method on it $bar = new Bar(); // now $fooBar has both methods! $fooBar = extend($foo, $bar); $fooBar->printFoo(); $fooBar->printBar(); Thursday, May 24, 12
  77. var foo = { foo: function() { return 'foo-string'; } }; var bar = { bar: function() { return 'bar-string'; } }; var fooBar = $.extend(foo, bar); console.log(fooBar.foo(), fooBar.bar()); Thursday, May 24, 12
  78. Writing bad JavaScript tip #6: Avoid using “delegate” events, and instead constantly worry about re-attaching events to new elements http://bit.ly/php-js-demo 06 - Delegate events @weaverryan Thursday, May 24, 12
  79. Can you see the difference? this.$el.find('.luigi').on( 'click', $.proxy(this._luigiClick, this) ); this.$el.on( 'click', '.luigi', $.proxy(this._luigiClick, this) ); Thursday, May 24, 12
  80. this.$el.find('.luigi')on( 'click', $.proxy(this._luigiClick, this) ); Since the event is registered specifically on “.luigi”, if any new “.luigi” elements are added, they will not response to this event Thursday, May 24, 12
  81. The event is registered on the wrapper, but looks for “.luigi”. If new “.luigi” elements are added to the wrapper, they will automatically trigger the event this.$el.on( 'click', '.luigi', $.proxy(this._luigiClick, this) ); Thursday, May 24, 12
  82. jQuery.live? $('.foo').live('click', ...); really just means this $('body').on('click', '.foo', ...); Thursday, May 24, 12
  83. Mario’s bad JavaScript tip #7: Self-executing JavaScript blocks are just plain scary looking http://bit.ly/php-js-demo 07 - Self-executing blocks @weaverryan Thursday, May 24, 12
  84. PHP Imaginationland $dbConn = ''; // initialize this $ourPreparedObject = (function($db) { $a = 5; $b = 10; $db->execute('...'); // ... a lot more complex stuff return $someObject; })($dbConn); $ourPreparedObject->callSomething(); Thursday, May 24, 12
  85. JavaScript Real Life window.MagicBoxes = (function($) { return { // do a bunch of craziness }; })(jQuery); MagicBoxes.someMethod(); Thursday, May 24, 12
  86. This is just a function... window.MagicBoxes = (function($) { return { // do a bunch of craziness }; })(jQuery); MagicBoxes.someMethod(); Thursday, May 24, 12
  87. Then we execute it, and pass in an argument window.MagicBoxes = (function($) { return { // do a bunch of craziness }; })(jQuery); MagicBoxes.someMethod(); Thursday, May 24, 12
  88. The function does any craziness it wants, but returns something window.MagicBoxes = (function($) { return { // do a bunch of craziness }; })(jQuery); MagicBoxes.someMethod(); Thursday, May 24, 12
  89. which we assign to a variable and then use window.MagicBoxes = (function($) { return { // do a bunch of craziness }; })(jQuery); MagicBoxes.someMethod(); Thursday, May 24, 12
  90. And that’s it! ‣ Makes isolated chunks of code ‣ Used by most JavaScript libraries to isolate their code from yours Thursday, May 24, 12
  91. Backbone.js! @weaverryan Thursday, May 24, 12
  92. Mario’s bad JavaScript tip #8: Don’t use, or over-use Backbone.js http://bit.ly/php-js-demo 08 - A Backbone View @weaverryan Thursday, May 24, 12
  93. What is Backbone? ‣ Set of tools for creating heavy front- end applications: * views * models * router @weaverryan Thursday, May 24, 12
  94. Views A formalized method for creating a JavaScript object that “manages” a DOM element Thursday, May 24, 12
  95. Views We’ve been building an object that’s very similar to a Backbone view Thursday, May 24, 12
  96. var MagicBoxes = Backbone.View.extend({ events: { 'click .luigi': '_luigiClick' }, initialize: function() { _.bindAll(this, '_luigiClick') }, _luigiClick: function(event) { event.preventDefault(); $(event.currentTarget) .toggleClass('active'); } }); var magicBoxApp = new MagicBoxes({ el: $('.mario-world') }); Thursday, May 24, 12
  97. var MagicBoxes = Backbone.View.extend({ events: { 'click .luigi': '_luigiClick' }, initialize: function() { _.bindAll(this, '_luigiClick') }, That sure is a simple way to bind to events. And thanks to _luigiClick: function(event) { “delegate” events, when the event.preventDefault(); DOM updates, the events still $(event.currentTarget) fire on new elements! .toggleClass('active'); } }); var magicBoxApp = new MagicBoxes({ el: $('.mario-world') }); Thursday, May 24, 12
  98. var MagicBoxes = Backbone.View.extend({ events: { 'click .luigi': '_luigiClick' }, initialize: function() { _.bindAll(this, '_luigiClick') }, _luigiClick: function(event) { event.preventDefault(); $(event.currentTarget) automatically initialize() is .toggleClass('active'); called by Backbone. } }); var magicBoxApp = new MagicBoxes({ el: $('.mario-world') }); Thursday, May 24, 12
  99. var MagicBoxes = Backbone.View.extend({ events: { 'click .luigi': '_luigiClick' }, initialize: function() { _.bindAll(this, '_luigiClick') }, _luigiClick: function(event) { _.bindAll is from event.preventDefault(); underscore.js - it does the $(event.currentTarget) same job as jQuery.proxy: .toggleClass('active'); } guarantees that “this” is }); this object in that function var magicBoxApp = new MagicBoxes({ el: $('.mario-world') }); Thursday, May 24, 12
  100. var MagicBoxes = Backbone.View.extend({ events: { 'click .luigi': '_luigiClick' }, initialize: function() { _.bindAll(this, '_luigiClick') DOM We attach a real }, element to the view by passing it into a pre-made _luigiClick: function(event) { constructor as “el”. In the event.preventDefault(); object, it’s available via $(event.currentTarget) this.el .toggleClass('active'); } }); var magicBoxApp = new MagicBoxes({ el: $('.mario-world') }); Thursday, May 24, 12
  101. Models A formalized object that just holds key-value data on it Thursday, May 24, 12
  102. var Character = Backbone.Model.extend(); var mario = new Character({ 'name': 'Mario' }); var peach = new Character({ 'name': 'Peach', 'status': 'captured' }); // change some data peach.set({ 'status': 'rescued' }); Thursday, May 24, 12
  103. Events make them wonderful peach.on('change', function(changedModel) { var name = changedModel.get('name'); console.log(name + ' was updated!'); }); // will cause "Peach was updated" to log peach.set({ 'status': 'rescued' }); Thursday, May 24, 12
  104. var Character = Backbone.Model.extend(); var App = Backbone.View.extend({ initialize: function() { _.bindAll(this, '_updateName'); this.model.on('change', this._updateName); this._updateName(); }, _updateName: function() { var name = this.model.get('name'); this.$('.name').html(name); } }); var mario = new Character({ 'name': 'Mario' }); var app = new App({ el: $('.mario-world'), model: mario }); Thursday, May 24, 12
  105. var Character = Backbone.Model.extend(); var App = Backbone.View.extend({ initialize: function() { _.bindAll(this, '_updateName'); this.model.on('change', this._updateName); this._updateName(); }, _updateName: function() { var name = this.model.get('name'); this.$('.name').html(name); } }); When the model changes, we can update the DOM anywhere that we’re listening for that change Thursday, May 24, 12
  106. Backbone is a great solution Thursday, May 24, 12
  107. But do you have a problem? Thursday, May 24, 12
  108. Views: Easy win ‣ Easy event binding ‣ An object-oriented structure that’s setup for you already ‣ “Patterns” to follow as you get comfortable @weaverryan Thursday, May 24, 12
  109. Views: Who Renders? ‣ There are 2 types of Views: Easy 1) Views applied to existing elements Win in the DOM (like our example) 2) Views that are given an empty Depends element, and then render using client- side templates and model data @weaverryan Thursday, May 24, 12
  110. When to use Models + Views ‣ A highly interactive app that communicates to a PHP API that is never responsible for rendering any HTML @weaverryan Thursday, May 24, 12
  111. When *not* to use Models + Views ‣ An app where PHP renders HTML ‣ An app where the API isn’t robust @weaverryan Thursday, May 24, 12
  112. Duplication ‣ If your PHP application renders HTML and you also try to use Backbone Views that render HTML, you’ll need duplicate templates ‣ Do one or the other @weaverryan Thursday, May 24, 12
  113. Duplication You can potentially duplicate a lot of work both on the client and server sides: ‣ validation ‣ models ‣ templates @weaverryan Thursday, May 24, 12
  114. Find your Comfort Zone ‣ Not really comfortable with a fully- client side application? Use Backbone views attached to existing DOM element ‣ Feel pretty awesome about doing everything in the browser? Dive in :) @weaverryan Thursday, May 24, 12
  115. Thanks... Ryan Weaver @weaverryan Thursday, May 24, 12
  116. ... and we love you! http://joind.in/talk/view/6508 Ryan Weaver @weaverryan Ryan Weaver @weaverryan Thursday, May 24, 12
Advertisement