Date post: | 06-May-2015 |
Category: |
Technology |
Upload: | marco-otte-witte |
View: | 1,546 times |
Download: | 2 times |
Rails is not just Ruby
Software Engineer, Consultant, Trainer
http://simplabs.com
Open Source
http://github.com/marcoowhttp://github.com/simplabs
Marco Otte-Witte
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!
var inputs = $$('input');for (var i = 0; i < inputs.length; i++) { alert(inputs[i].name);} X
Know your tools!
$$('input').each(function(input) { alert(input.name);});
Protoype.js
$.each('input', function() { alert(this.name);});
jQuery
Know your tools!
function setupPage() { $('userName').update( readUserName(); );} function readUserName() { //read user's name from cookie}
Know your tools!
function setupPage() { $('userName').update( readUserName(); );} function readUserName() { //read user's name from cookie}
X
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!
var timeout = window.setTimeout( "element.update(" + someContent + ");", 1000); X
Know your tools!
element.update.bind(element).curry( someContent).delay(1)
Protoype.js
Know your tools!
var loginField = document.getElementById('#user_login');alert(loginField.value);
Know your tools!
var loginField = document.getElementById('#user_login');alert(loginField.value);X
Know your tools!
alert($F('user_login'));Protoype.js
alert($('#user_login').val());jQuery
Know your tools!
var loginField = document.getElementById('#user_login');loginField.style.display = 'none';
Know your tools!
var loginField = document.getElementById('#user_login');loginField.style.display = 'none';X
Know your tools!
$('user_login').hide();Protoype.js
$('#user_login').hide();jQuery
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!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);}
X
Know your tools!
$('user_login').observe( 'change', function(event) { alert($F('user_login'));});
Protoype.js
$('#user_login').change(function() { alert(this.val());});
jQuery
Write valid JavaScript!
Write valid JavaScript!
someValue = 0;anotherValue = 1; function fun(param) { alert(param)}
Write valid JavaScript!
someValue = 0;anotherValue = 1; function fun(param) { alert(param)} X
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;anotherValue = 1; function fun(param) { alert(param)}
Missing semicolon.
Implied globals: someValue, anotherValue
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/replaceCode is at http://github.com/marcoow/js-and-rails
3 possible Solutions
the classic Solution
•Helpers (remote_form_for, link_to_remote etc.)
•RJS
•onclick=“...•href=“javascript:...
the classic Solution
the classic Solution
<div id="someElement"> some text that's replaced later</div><%= link_to_remote 'Replace', :url => classic_solution_replace_path, :method => :post %>
index.html.erb
the classic Solution
class ClassicSolutionController < ApplicationController
def index end
def replace end
end
the classic Solution
page.replace_html 'someElement', :partial => 'new_content'
replace.rjs
the classic Solution
<b>Fresh new content rendered at <%= Time.now %></b><%= link_to_remote 'Replace again', :url => classic_solution_replace_path, :method => :post %>
_new_content.html.erb
•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
the classic Solution
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
Full Separationvar 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 } ); }
});
replacer.js
Full Separationvar 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(); }
}
})();
application.js
Full Separation
document.observe('dom:loaded', function() { Application.setupOnLoad(); Ajax.Responders.register({ onComplete: Application.setupOnPageUpdate });});
application.js
Full Separation
document.observe('dom:loaded', function() { Application.setupOnLoad(); Ajax.Responders.register({ onComplete: Application.setupOnPageUpdate });});
application.js
Replacer controls are initialized on page load and after every AJAX request
Full Separation
<div id="someElement"> some text that's replaced later</div><%= link_to 'Replace', full_separation_replace_path, :'data-replaces' => 'someElement' %>
index.html.erb
Full Separationclass FullSeparationController < ApplicationController
def index end
def replace respond_to do |format| format.js { render :partial => 'new_content' } end end
end
Full Separation
<b>Fresh new content rendered at <%= Time.now %></b><%= link_to 'Replace again', full_separation_replace_path, :'data-replaces' => 'someElement' %>
_new_content.html.erb
•clean, semantic HTML
•full separation of concerns
•clean HTML/CSS/JS is crucial
•Behaviour is (kind of) implicit
•discipline required
Full Separation
explicit Controls
•like Full Separation
•but controls are initialized in the templates
•more explicit/ obvious what‘s going on
explicit Controls
explicit Controls
<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>
index.html.erb
explicit Controlsclass ControlsController < ApplicationController
def index end
def replace respond_to do |format| format.js { render :partial => 'new_content' } end end
end
explicit Controls
<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>
_new_content.html.erb
explicit Controls
<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>
_new_content.html.erb
No initialization on dom:loaded here as this is the result of an AJAX request (dom:loaded not fired)
•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
explicit Controls
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