SlideShare a Scribd company logo
Rails is not just
      Ruby
Marco Otte-Witte


Software Engineer, Consultant, Trainer
         http://simplabs.com


            Open Source
     http://github.com/marcoow
     http://github.com/simplabs
JavaScript is
   serious
  Business!
It‘s not just
adding lib after
  lib after lib!
Give your
 JavaScript the
same love your
   Ruby code
     gets!
Know your
  tools!
Know your tools!



var inputs = $$('input');
for (var i = 0; i < inputs.length; i++) {
  alert(inputs[i].name);
}
Know your tools!




              X
var inputs = $$('input');
for (var i = 0; i < inputs.length; i++) {

}
  alert(inputs[i].name);
Know your tools!
Protoype.js
$$('input').each(function(input) {
  alert(input.name);
});

jQuery
$.each('input', function() {
  alert(this.name);
});
Know your tools!
function setupPage() {
  $('userName').update(
     readUserName();
  );
}

function readUserName() {
  //read user's name from cookie
}
Know your tools!




            X
function setupPage() {
  $('userName').update(
     readUserName();
  );
}

function readUserName() {
  //read user's name from cookie
}
Know your tools!
var Application = (function() {

  //private
  var readUserName = function() {
     //read user's name from cookie
  };

  return {

      //public
      setupPage: function() {
        $('userName').update(
           readUserName();
        );
      }

  }

})();
Know your tools!


var timeout = window.setTimeout(
   "element.update(" + someContent
     + ");",
   1000
);
Know your tools!




            X
var timeout = window.setTimeout(
   "element.update(" + someContent
     + ");",
   1000
);
Know your tools!

Protoype.js
element.update.bind(element).curry(
  someContent
).delay(1)
Know your tools!


var loginField =
  document.getElementById('#user_login');
alert(loginField.value);
Know your tools!




              X
var loginField =
  document.getElementById('#user_login');
alert(loginField.value);
Know your tools!

Protoype.js
alert($F('user_login'));

jQuery
alert($('#user_login').val());
Know your tools!


var loginField =
  document.getElementById('#user_login');
loginField.style.display = 'none';
Know your tools!




              X
var loginField =
  document.getElementById('#user_login');
loginField.style.display = 'none';
Know your tools!

Protoype.js
$('user_login').hide();

jQuery
$('#user_login').hide();
Know your tools!
var loginField =
  document.getElementById('user_login');
function loginChanged() {
  alert(loginField.value);
}
if (loginField.addEventListener) {
  loginField.addEventListener(
    'change', loginChanged, false);
} else if (obj.attachEvent) {
  obj.attachEvent('onchange', loginChanged);
}
Know your tools!




               X
var loginField =
  document.getElementById('user_login');
function loginChanged() {
  alert(loginField.value);
}
if (loginField.addEventListener) {
  loginField.addEventListener(
    'change', loginChanged, false);
} else if (obj.attachEvent) {
  obj.attachEvent('onchange', loginChanged);
}
Know your tools!
Protoype.js
$('user_login').observe(
  'change', function(event) {
    alert($F('user_login'));
});

jQuery
$('#user_login').change(function() {
  alert(this.val());
});
Write valid
JavaScript!
Write valid JavaScript!

someValue    = 0;
anotherValue = 1;

function fun(param) {
  alert(param)
}
Write valid JavaScript!




            X
someValue    = 0;
anotherValue = 1;

function fun(param) {
  alert(param)
}
Write valid JavaScript!

var someValue    = 0;
var anotherValue = 1;

function fun(param) {
  alert(param);
}
Write valid JavaScript!


someValue    = 0;
anotherValue = 1;

function fun(param) {
  alert(param)
}
Write valid JavaScript!


someValue    = 0;
anotherValue = 1;

function fun(param) {
  alert(param)
}
Write valid JavaScript!


someValue    = 0;       Missing semicolon.
anotherValue = 1;
                        Implied globals:
function fun(param) {   someValue, anotherValue
  alert(param)
}
JavaScript and
    Rails
JavaScript and Rails


•RESTful actions (delete, put, post)
•AJAX
•Effects
•etc.
the Demo App
the Demo App




 POST/replace
the Demo App




                POST/replace
Code is at http://github.com/marcoow/js-and-rails
3 possible
Solutions
the classic
 Solution
the classic Solution

•Helpers (remote_form_for,   link_to_remote
 etc.)
•RJS
•onclick=“...
•href=“javascript:...
the classic Solution

index.html.erb
<div id="someElement">
  some text that's replaced later
</div>
<%= link_to_remote 'Replace', :url =>
       classic_solution_replace_path,
       :method => :post %>
the classic Solution
class ClassicSolutionController <
ApplicationController

  def index
  end

  def replace
  end

end
the classic Solution


replace.rjs
page.replace_html 'someElement',
  :partial => 'new_content'
the classic Solution

_new_content.html.erb
<b>Fresh new content rendered at <%=
  Time.now %></b>
<%= link_to_remote 'Replace again',
    :url => classic_solution_replace_path,
    :method => :post %>
the classic Solution
•strong coupling
•hard to maintain
•no/ little code reuse
•bloated HTML
•code that actually belongs together is
 distributed over several places
•easy to write in the first place
Full Separation
Full Separation
•define JavaScript controls that encapsulate
 all frontend logic
•mark elements with class, rel or HTML5‘s
 data-* attributes
•full separation of HTML and JavaScript
•Initialization of controls on dom:loaded
 event
Full Separation
•define JavaScript controls that encapsulate
 all frontend logic
•mark elements with class, rel or HTML5‘s
 data-* attributes
•full separation of HTML and JavaScript
•Initialization of controls on dom:loaded
 event
Full Separation
replacer.js
var Replacer = Class.create({

  initialize: function(container, target) {
     this.container = $(container);
     this.target    = $(target);
     this.container.observe('click', this.onClick.bindAsEventListener(this));
  },

  onClick: function(event) {
    event.stop();
    new Ajax.Updater(
       this.target,
       this.container.href, {
         method:      'post',
         evalScripts: true
       }
    );
  }

});
Full Separation
application.js
var Application = (function() {

  var initializeReplacers = function() {
     $$('a[data-replaces]').each(function(replacingLink) {
       if (!replacingLink._initializedReplacer) {
         new Replacer(replacingLink, replacingLink.readAttribute('data-replaces'));
         replacingLink._initializedReplacer = true;
       }
     });
  };

  return {

      setupOnLoad: function() {
         initializeReplacers();
      },

      setupOnPageUpdate: function() {
        initializeReplacers();
      }

  }

})();
Full Separation

application.js
document.observe('dom:loaded', function() {
  Application.setupOnLoad();
  Ajax.Responders.register({
    onComplete: Application.setupOnPageUpdate
  });
});
Full Separation

application.js
document.observe('dom:loaded', function() {
  Application.setupOnLoad();
  Ajax.Responders.register({
    onComplete: Application.setupOnPageUpdate
  });
});


Replacer controls are initialized on page load
and after every AJAX request
Full Separation

index.html.erb
<div id="someElement">
  some text that's replaced later
</div>
<%= link_to 'Replace', full_separation_replace_path,
       :'data-replaces' => 'someElement' %>
Full Separation
class FullSeparationController <
ApplicationController

  def index
  end

  def replace
    respond_to do |format|
      format.js { render :partial => 'new_content' }
    end
  end

end
Full Separation

_new_content.html.erb
<b>Fresh new content rendered at
  <%= Time.now %></b>
<%= link_to 'Replace again',
    full_separation_replace_path,
    :'data-replaces' => 'someElement' %>
Full Separation

•clean, semantic HTML
•full separation of concerns
•clean HTML/CSS/JS is crucial
•Behaviour is (kind of) implicit
•discipline required
explicit
Controls
explicit Controls


•like Full Separation
•but controls are initialized in the templates
•more explicit/ obvious what‘s going on
explicit Controls

index.html.erb
<div id="someElement">
  some text that's replaced later
</div>
<%= link_to 'Replace', controls_replace_path,
       :id => 'replacerLink' %>
<script type="text/javascript" charset="utf-8">
  document.observe('dom:loaded', function() {
    var replacer = new Replacer('replacerLink', 'someElement');
  });
</script>
explicit Controls
class ControlsController < ApplicationController

  def index
  end

  def replace
    respond_to do |format|
      format.js { render :partial => 'new_content' }
    end
  end

end
explicit Controls
_new_content.html.erb
<b>Fresh new content rendered at
  <%= Time.now %></b>
<%= link_to 'Replace again', controls_replace_path,
       :id => 'secondReplacerLink' %>
<script type="text/javascript" charset="utf-8">
  var newReplacer = new Replacer(
     'secondReplacerLink', 'someElement'
  );
</script>
explicit Controls
_new_content.html.erb
<b>Fresh new content rendered at
  <%= Time.now %></b>
<%= link_to 'Replace again', controls_replace_path,
       :id => 'secondReplacerLink' %>
<script type="text/javascript" charset="utf-8">
  var newReplacer = new Replacer(
     'secondReplacerLink', 'someElement'
  );
</script>

No initialization on dom:loaded here as this is
the result of an AJAX request (dom:loaded not
fired)
explicit Controls

•HTML is (mostly) clean and semantic
•Behaviour is explicit in the templates
•easier to grasp what‘s going on than with
 Full Separation
•though not as nice as full separation
Either go with
Full Separation
  or explicit
   Controls!
Avoid the
classic Solution
 when you can!
Resources
Resources

•http://ejohn.org/apps/learn/
•http://www.jslint.com/
•http://api.prototypejs.org/
•http://docs.jquery.com
•http://github.com/marcoow/js-and-rails
•http://javascriptrocks.com
Q&A

More Related Content

Rails is not just Ruby