SlideShare a Scribd company logo
RUBYFYING JAVASCRIPT:RUBYFYING JAVASCRIPT:
AVOIDING JQUERY SPAGHETTIAVOIDING JQUERY SPAGHETTI
FORREST CHANGFORREST CHANG
FKCHANG2000@YAHOO.COMFKCHANG2000@YAHOO.COM
A JQUERY STORYA JQUERY STORY
Have a problem
Add a little jQuery
Fixed, yay!
AS TIME GOES ONAS TIME GOES ON
Add function, add function, nest function
Insert event handlers in DOM
Add business logic
Get input, dialogs
Ajax
Effects
Update DOM
Assign handlers to id
etc.
RESULTRESULT
JQUERY SPAGHETTIJQUERY SPAGHETTI
by Steve O'Brien in http://steve-obrien.com/javascript-jquery-spaghetti/
Without a strong framework or architecture you end up
with jQuery spaghetti. This is usually because you start
with a small piece of jquery shoved somewhere in the dom
as you add features it all grows out of proportion and
becomes a tangled mess. The most challenging thing is
maintaining state. Relying heavily on jQuery means your
application state information is stores in the dom, this
works well for small features and isolated components
here and there, but in a complex app it quickly becomes
very difficult to manage.
JQUERY SPAGHETTI IS JSAPABO #1JQUERY SPAGHETTI IS JSAPABO #1
JavaScript AntiPatterns Addressed by Opal (JSAPABO)
There are anti patterns addressed by Opal - trying to codify why Opal
makes browser code better, starting w/naming what's wrong
Very much a Work In Progress, apologies
http://funkworks.blogspot.com/2015/04/javascript-antipatterns-
addressed-by.html
OPAL CAN HELPOPAL CAN HELP
What is Opal? TLDR; Ruby in the browser
How can it help?
Ruby > JS
Culture, Conventions
OOP
and More!
See here for more info
REAL LIFE STORYREAL LIFE STORY
Want slide out bar
Need both Left and right slide bars
Would like simple, low cruft
Demo of finished product
ORIGINAL CODEORIGINAL CODE HTTP://JSFIDDLE.NET/DMYTR/37/HTTP://JSFIDDLE.NET/DMYTR/37/
$.asm = {};
$.asm.panels = 1;
function sidebar(panels) {
$.asm.panels = panels;
if (panels === 1) {
$('#sidebar').animate({
left: -180,
});
} else if (panels === 2) {
$('#sidebar').animate({
left: 20,
});
$('#sidebar').height($(window).height() - 50);
}
};
$(function() {
$('#toggleSidebar').click(function() {
if ($.asm.panels === 1) {
$('#toggleSidebar i').addClass('glyphicon-chevron-left');
$('#toggleSidebar i').removeClass('glyphicon-chevron-right');
return sidebar(2);
} else {
$('#toggleSidebar i').removeClass('glyphicon-chevron-left');
$('#toggleSidebar i').addClass('glyphicon-chevron-right');
return sidebar(1);
}
});
});
THE CODETHE CODE
Simple
Does what I want for the left sidebar
HOW ABOUT THE RIGHT SIDEBAR? MY SPIKEHOW ABOUT THE RIGHT SIDEBAR? MY SPIKE
$.asm2 = {};
$.asm2.panels = 1;
function sidebar2(panels) {
$.asm2.panels = panels;
if (panels === 1) {
$('#sidebar-right').animate({
right: -780,
});
} else if (panels === 2) {
$('#sidebar-right').animate({
right: 20,
});
$('#mapCanvas').width($('#mapCanvas').parent().width());
$('#mapCanvas').height($(window).height() - 50);
$('#sidebar-right').height($(window).height() - 50);
}
};
$(function() {
$('#toggleSidebar-right').click(function() {
if ($.asm2.panels === 1) {
$('#toggleSidebar-right i').removeClass('glyphicon-chevron-left');
$('#toggleSidebar-right i').addClass('glyphicon-chevron-right');
return sidebar2(2);
} else {
$('#toggleSidebar-right i').addClass('glyphicon-chevron-left');
$('#toggleSidebar-right i').removeClass('glyphicon-chevron-right');
return sidebar2(1);
}
});
});
KINDA UGLY CODEKINDA UGLY CODE
Original code for a jsfiddle - don't expect a lot
Typical for jQuery examples
by itself not bad. NOT good OO code
Now that concept has been proven, time to make the code "real"
HOW TO CONVERT?HOW TO CONVERT? JUST TRANSLATE?JUST TRANSLATE?
Didn't like it from the beginning
Document.ready? {
Element.find('#toggleSidebar').on :click {
}
}
MIRED IN THE DETAILSMIRED IN THE DETAILS
JSAPABO #6 Stuck in the weeds
What's the big picture
What's my intent?
A BETTER APPROACHA BETTER APPROACH
SEGUESEGUE
Reasons Opal Makes your Browser Code Better #1 (ROMYBCB) - a future
blog series
In Ruby, we Think in Objects
So Start w/objects
HOW I WANT TO USE IT?HOW I WANT TO USE IT?
# Create w/intent
left_sidebar = Sidebar.new('#toggleSidebar', 'left')
# elsewhere manipulate
left_sidebar.hide
BETTERBETTER
Intent revealed
Objects from the get go
We'll see why this matters later
CONVERTING THE JS CLICK HANDLERCONVERTING THE JS CLICK HANDLER
// original code
$(function() {
$('#toggleSidebar').click(function() {
if ($.asm.panels === 1) {
$('#toggleSidebar i').addClass('glyphicon-chevron-left');
$('#toggleSidebar i').removeClass('glyphicon-chevron-right');
return sidebar(2);
} else {
$('#toggleSidebar i').removeClass('glyphicon-chevron-left');
$('#toggleSidebar i').addClass('glyphicon-chevron-right');
return sidebar(1);
}
});
});
WHAT DOES IT DO?WHAT DOES IT DO?
if $.asm.panels = 1 // magic number 1 = closed state
make sidebar handle left facing left
sidebar(2) // set sidebar state to 2 (open state) - slide out
else
make sidebar handle face right
sidebar(1) // set sidebar state to 2 (closed state) - slide in
WHAT DOES IT DO AT A HIGHER LEVELWHAT DOES IT DO AT A HIGHER LEVEL
step away from the details (JSAPABO #6)
If the slider is open close it
else open it
OPAL CLICK HANDLER WITH INTENTION REVEALEDOPAL CLICK HANDLER WITH INTENTION REVEALED
Put it in #initialize(), so it happens for each instance
class Sidebar
def initialize(element_id, side)
@state = :closed
Element.find(element_id).on :click {
if @state == :open
close
else
open
end
}
end
end
WHERE TO HANG THE STATE?WHERE TO HANG THE STATE?
JSAPABO #5
Where do you put state?
Coz not using objects, where put state? Global?
In jQuery, can hang off of jQuery
$.asm.panels // hung off of jQuery
Where would you hang data in Ruby/Opal
instance variable, because you use objects from the get go
easy
@state = :closed
IMPLEMENT OPEN AND CLOSE, ROUND 1IMPLEMENT OPEN AND CLOSE, ROUND 1
def open
icon = Element.find("#{element_id} i")
icon.add_class('glyphicon-chevron-left')
icon.remove_class('glyphicon-chevron-right')
Element.find('#sidebar').animate left: 20
@state = :open
end
def close
icon = Element.find("#{element_id} i")
icon.remove_class('glyphicon-chevron-left')
icon.add_class('glyphicon-chevron-right')
Element.find('#sidebar').animate left: -180
@state = :close
end
ROUND 2: REMOVE DUPLICATIONROUND 2: REMOVE DUPLICATION
def open
set_icon('glyphicon-chevron-left', 'glyphicon-chevron-right')
Element.find('#sidebar').animate left: 20
@state = :open
end
def set_icon(class_to_add, class_to_remove)
icon = Element.find("#{element_id} i")
icon.add_class(class_to_add)
icon.remove_class(class_to_remove)
end
def close
set_icon('glyphicon-chevron-right', 'glyphicon-chevron-left')
Element.find('#sidebar').animate left: -180
@state = :closed
end
ROUND 3: REFACTOR MORE DUPLICATIONROUND 3: REFACTOR MORE DUPLICATION
def open
set_icon('glyphicon-chevron-left', 'glyphicon-chevron-right', 20)
@state = :open
end
def set_icon(class_to_add, class_to_remove, new_position)
icon = Element.find("#{element_id} i")
icon.add_class(class_to_add)
icon.remove_class(class_to_remove)
Element.find('#sidebar').animate left: new_position
end
def close
set_icon('glyphicon-chevron-right', 'glyphicon-chevron-left', -180)
@state = :closed
end
YET ANOTHER PATTERNYET ANOTHER PATTERN
There's another pattern- the state change, so we move that functionality into
set_icon
def open
set_icon('glyphicon-chevron-left', 'glyphicon-chevron-right', 20, :open)
end
def set_icon(class_to_add, class_to_remove, new_position, new_state)
icon = Element.find("#{element_id} i")
icon.add_class(class_to_add)
icon.remove_class(class_to_remove)
Element.find('#sidebar').animate left: new_position
@state = new_state
end
def close
set_icon('glyphicon-chevron-right', 'glyphicon-chevron-left', -180, :closed)
end
NEED A NEW NAMENEED A NEW NAME
set_icon() no longer describes what it's doing
def open
new_state('glyphicon-chevron-left', 'glyphicon-chevron-right', 20, :open)
end
def new_state(class_to_add, class_to_remove, new_position, new_state)
icon = Element.find("#{element_id} i")
icon.add_class(class_to_add)
icon.remove_class(class_to_remove)
Element.find('#sidebar').animate left: new_position
@state = new_state
end
def close
new_state('glyphicon-chevron-right', 'glyphicon-chevron-left', -180, :closed)
end
OPAL CODE THAT MATCHES THE JSFIDDLEOPAL CODE THAT MATCHES THE JSFIDDLE
# Sidebar abstraction
class Sidebar
attr_reader :element_id
def initialize(element_id, side)
@element_id = element_id
@state = :closed
Element.find("#{element_id} .toggles").on :click do
if @state == :open
close
else
open
end
end
end
def open
new_state('glyphicon-chevron-left', 'glyphicon-chevron-right', 20, :open)
end
def new_state(class_to_add, class_to_remove, new_position, new_state)
icon = Element.find("#{element_id} i")
icon.add_class(class_to_add)
icon.remove_class(class_to_remove)
Element.find("#{element_id}").animate left: new_position
@state = new_state
end
def close
new_state('glyphicon-chevron-right', 'glyphicon-chevron-left', -180, :closed)
end
end
Document.ready? {
left_sidebar = Sidebar.new('#sidebar', 'left')
}
CODE IS BETTERCODE IS BETTER
About same lines of code (LOC)
More intention revealing
Code can be reused/repurposed
Can programmaticaly open or close sidebar easily, i.e. left_sidebar.open
Couldn't do that w/original code WITHOUT refactoring
STILL NEED A RIGHT SIDEBARSTILL NEED A RIGHT SIDEBAR
Begin w/the end in mind
Document.ready? {
left_sidebar = Sidebar.new('#sidebar', 'left')
right_sidebar = Sidebar.new('#sidebar-right', 'right)
}
ORIGINAL EVIL CUT AND PASTE CODEORIGINAL EVIL CUT AND PASTE CODE
$.asm2 = {};
$.asm2.panels = 1;
function sidebar2(panels) {
$.asm2.panels = panels;
if (panels === 1) {
$('#sidebar-right').animate({
right: -780,
});
} else if (panels === 2) {
$('#sidebar-right').animate({
right: 20,
});
$('#sidebar-right').height($(window).height() - 50);
}
};
$(function() {
$('#toggleSidebar-right').click(function() {
if ($.asm2.panels === 1) {
$('#toggleSidebar-right i').removeClass('glyphicon-chevron-left');
$('#toggleSidebar-right i').addClass('glyphicon-chevron-right');
return sidebar2(2);
} else {
$('#toggleSidebar-right i').addClass('glyphicon-chevron-left');
$('#toggleSidebar-right i').removeClass('glyphicon-chevron-right');
return sidebar2(1);
}
});
});
NOTESNOTES
Because of JSAPABO #5, needed to store right panel state
Can't use $.asm.panels, cut and paste $.asm2
What if I want a dropdown, $.asm3 ?
Not a problem if dealing with objects from the get go
PARAMETRIZEPARAMETRIZE
Instead of converting the copy pasted code, we could parametrize by side
Add below to #initialize
set_params_for_side(side)
SETTING VALUES FOR :LEFTSETTING VALUES FOR :LEFT
Set values and use them
SETTING LEFTSETTING LEFT
attr_reader :closed_icon_class, :opened_icon_class, :opened_x_position, :closed_x_positio
n
def set_params_for_side(side)
if side == :left
@closed_icon_class = 'glyphicon-chevron-right'
@opened_icon_class = 'glyphicon-chevron-left'
@opened_x_position = 20
@closed_x_position = -180
end
end
def open
new_state(opened_icon_class, closed_icon_class, opened_x_position, :open)
end
def new_state(class_to_add, class_to_remove, new_position, new_state)
icon = Element.find("#{element_id} i")
icon.add_class(class_to_add)
icon.remove_class(class_to_remove)
Element.find("#{element_id}").animate left: new_position
@state = new_state
end
def close
new_state(closed_icon_class, opened_icon_class, closed_x_position, :closed)
end
HANDLE NON LEFT PARAMETERHANDLE NON LEFT PARAMETER
attr_reader :closed_icon_class, :opened_icon_class, :opened_x_position,
:closed_x_position, :x_position_side
def set_params_for_side(side)
if side == :left
@closed_icon_class = 'glyphicon-chevron-right'
@opened_icon_class = 'glyphicon-chevron-left'
@opened_x_position = 20
@closed_x_position = -180
@x_position_side = 'left'
else
@closed_icon_class = 'glyphicon-chevron-left'
@opened_icon_class = 'glyphicon-chevron-right'
@opened_x_position = 20
@closed_x_position = -780
@x_position_side = 'right'
end
end
def open
new_state(opened_icon_class, closed_icon_class, opened_x_position, :open)
end
def new_state(class_to_add, class_to_remove, new_position, new_state)
icon = Element.find("#{element_id} i")
icon.add_class(class_to_add)
icon.remove_class(class_to_remove)
Element.find("#{element_id}").animate x_position_side => new_position
@state = new_state
end
DONE FOR NOWDONE FOR NOW
Does what I need
Exceeds original implementation
Reusable
RESULTING CODERESULTING CODE
class Sidebar
attr_reader :element_id
def initialize(element_id, side)
@element_id = element_id
@state = :closed
set_params_for_side(side)
Element.find("#{element_id} .toggles").on :click do
if @state == :open
close
else
open
end
end
end
attr_reader :closed_icon_class, :opened_icon_class,
:opened_x_position, :closed_x_position,
:x_position_side
def set_params_for_side(side)
if side == :left
@closed_icon_class = 'glyphicon-chevron-right'
@opened_icon_class = 'glyphicon-chevron-left'
@opened_x_position = 20
@closed_x_position = -180
@x_position_side = 'left'
else
@closed_icon_class = 'glyphicon-chevron-left'
@opened_icon_class = 'glyphicon-chevron-right'
@opened_x_position = 20
@closed_x_position = -780
@x_position_side = 'right'
end
end
PAGE 2PAGE 2
def open
new_state(opened_icon_class, closed_icon_class, opened_x_position, :open)
end
def new_state(class_to_add, class_to_remove, new_position, new_state)
icon = Element.find("#{element_id} i")
icon.add_class(class_to_add)
icon.remove_class(class_to_remove)
Element.find("#{element_id}").animate x_position_side => new_position
@state = new_state
end
def close
new_state(closed_icon_class, opened_icon_class, closed_x_position, :closed)
end
end
Document.ready? {
left_sidebar = Sidebar.new('#sidebar', 'left')
right_sidebar = Sidebar.new('#sidebar-right', 'right')
}
CONCLUSIONCONCLUSION
Opal gives you
Better Code
Better functionality
Happiness
Blogged here

More Related Content

What's hot

Business News, Personal Finance and Money News
Business News, Personal Finance and Money NewsBusiness News, Personal Finance and Money News
Business News, Personal Finance and Money News
eminentoomph4388
 
11. delete record
11. delete record11. delete record
11. delete record
Razvan Raducanu, PhD
 
10. view one record
10. view one record10. view one record
10. view one record
Razvan Raducanu, PhD
 
Taming forms with React
Taming forms with ReactTaming forms with React
Taming forms with React
GreeceJS
 
Business News, Personal Finance and Money News
Business News, Personal Finance and Money NewsBusiness News, Personal Finance and Money News
Business News, Personal Finance and Money News
acousticassista07
 
UITableView Pain Points
UITableView Pain PointsUITableView Pain Points
UITableView Pain Points
Ken Auer
 
Hacking Your Way to Better Security - PHP South Africa 2016
Hacking Your Way to Better Security - PHP South Africa 2016Hacking Your Way to Better Security - PHP South Africa 2016
Hacking Your Way to Better Security - PHP South Africa 2016
Colin O'Dell
 
Hacking Your Way To Better Security
Hacking Your Way To Better SecurityHacking Your Way To Better Security
Hacking Your Way To Better Security
Colin O'Dell
 
jQuery - Introdução
jQuery - IntroduçãojQuery - Introdução
jQuery - Introdução
Gustavo Dutra
 
12. edit record
12. edit record12. edit record
12. edit record
Razvan Raducanu, PhD
 
Business News, Personal Finance and Money News
Business News, Personal Finance and Money NewsBusiness News, Personal Finance and Money News
Business News, Personal Finance and Money News
bernardwilcox8
 
Business News, Personal Finance and Money News
Business News, Personal Finance and Money NewsBusiness News, Personal Finance and Money News
Business News, Personal Finance and Money News
organicprosperi63
 
Unit testing with zend framework PHPBenelux
Unit testing with zend framework PHPBeneluxUnit testing with zend framework PHPBenelux
Unit testing with zend framework PHPBenelux
Michelangelo van Dam
 
USF ARG seminar Feb 08
USF ARG seminar Feb 08USF ARG seminar Feb 08
USF ARG seminar Feb 08JP Allen
 
JQuery In Rails
JQuery In RailsJQuery In Rails
JQuery In RailsLouie Zhao
 
Business News, Personal Finance and Money News
Business News, Personal Finance and Money NewsBusiness News, Personal Finance and Money News
Business News, Personal Finance and Money News
blogginatl1963
 
sanya's Bug database
sanya's Bug databasesanya's Bug database
sanya's Bug database
sanyabhasin18
 

What's hot (20)

Business News, Personal Finance and Money News
Business News, Personal Finance and Money NewsBusiness News, Personal Finance and Money News
Business News, Personal Finance and Money News
 
11. delete record
11. delete record11. delete record
11. delete record
 
10. view one record
10. view one record10. view one record
10. view one record
 
Bd venta.sql
Bd venta.sqlBd venta.sql
Bd venta.sql
 
Taming forms with React
Taming forms with ReactTaming forms with React
Taming forms with React
 
Business News, Personal Finance and Money News
Business News, Personal Finance and Money NewsBusiness News, Personal Finance and Money News
Business News, Personal Finance and Money News
 
UITableView Pain Points
UITableView Pain PointsUITableView Pain Points
UITableView Pain Points
 
Migrare da symfony 1 a Symfony2
 Migrare da symfony 1 a Symfony2  Migrare da symfony 1 a Symfony2
Migrare da symfony 1 a Symfony2
 
Hacking Your Way to Better Security - PHP South Africa 2016
Hacking Your Way to Better Security - PHP South Africa 2016Hacking Your Way to Better Security - PHP South Africa 2016
Hacking Your Way to Better Security - PHP South Africa 2016
 
Hacking Your Way To Better Security
Hacking Your Way To Better SecurityHacking Your Way To Better Security
Hacking Your Way To Better Security
 
jQuery - Introdução
jQuery - IntroduçãojQuery - Introdução
jQuery - Introdução
 
Actividad 1
Actividad 1Actividad 1
Actividad 1
 
12. edit record
12. edit record12. edit record
12. edit record
 
Business News, Personal Finance and Money News
Business News, Personal Finance and Money NewsBusiness News, Personal Finance and Money News
Business News, Personal Finance and Money News
 
Business News, Personal Finance and Money News
Business News, Personal Finance and Money NewsBusiness News, Personal Finance and Money News
Business News, Personal Finance and Money News
 
Unit testing with zend framework PHPBenelux
Unit testing with zend framework PHPBeneluxUnit testing with zend framework PHPBenelux
Unit testing with zend framework PHPBenelux
 
USF ARG seminar Feb 08
USF ARG seminar Feb 08USF ARG seminar Feb 08
USF ARG seminar Feb 08
 
JQuery In Rails
JQuery In RailsJQuery In Rails
JQuery In Rails
 
Business News, Personal Finance and Money News
Business News, Personal Finance and Money NewsBusiness News, Personal Finance and Money News
Business News, Personal Finance and Money News
 
sanya's Bug database
sanya's Bug databasesanya's Bug database
sanya's Bug database
 

Similar to Ruby-ying Javascript: Avoiding jQuery Spaghetti

jQuery for Beginners
jQuery for Beginners jQuery for Beginners
jQuery for Beginners
NAILBITER
 
Cheap frontend tricks
Cheap frontend tricksCheap frontend tricks
Cheap frontend tricks
ambiescent
 
Hooks WCSD12
Hooks WCSD12Hooks WCSD12
Hooks WCSD12
Jeffrey Zinn
 
Building Large jQuery Applications
Building Large jQuery ApplicationsBuilding Large jQuery Applications
Building Large jQuery Applications
Rebecca Murphey
 
The Ring programming language version 1.10 book - Part 71 of 212
The Ring programming language version 1.10 book - Part 71 of 212The Ring programming language version 1.10 book - Part 71 of 212
The Ring programming language version 1.10 book - Part 71 of 212
Mahmoud Samir Fayed
 
Cyclejs introduction
Cyclejs introductionCyclejs introduction
Cyclejs introduction
Arye Lukashevski
 
State of jQuery and Drupal
State of jQuery and DrupalState of jQuery and Drupal
State of jQuery and Drupal
jeresig
 
Modular and Event-Driven JavaScript
Modular and Event-Driven JavaScriptModular and Event-Driven JavaScript
Modular and Event-Driven JavaScript
Eduardo Shiota Yasuda
 
Let jQuery Rock Your World
Let jQuery Rock Your WorldLet jQuery Rock Your World
Let jQuery Rock Your World
Matt Gifford
 
[removed] $file, removeRemove}, list #su.docx
[removed] $file, removeRemove}, list #su.docx[removed] $file, removeRemove}, list #su.docx
[removed] $file, removeRemove}, list #su.docx
gerardkortney
 
Programmation fonctionnelle en JavaScript
Programmation fonctionnelle en JavaScriptProgrammation fonctionnelle en JavaScript
Programmation fonctionnelle en JavaScript
Loïc Knuchel
 
Kick start with j query
Kick start with j queryKick start with j query
Kick start with j queryMd. Ziaul Haq
 
i am unsure how to make the button below the image instead of on it- a.pdf
i am unsure how to make the button below the image instead of on it- a.pdfi am unsure how to make the button below the image instead of on it- a.pdf
i am unsure how to make the button below the image instead of on it- a.pdf
MattU5mLambertq
 
Creating an Uber Clone - Part VIII - Transcript.pdf
Creating an Uber Clone - Part VIII - Transcript.pdfCreating an Uber Clone - Part VIII - Transcript.pdf
Creating an Uber Clone - Part VIII - Transcript.pdf
ShaiAlmog1
 
Fact, Fiction, and FP
Fact, Fiction, and FPFact, Fiction, and FP
Fact, Fiction, and FP
Brian Lonsdorf
 
jQuery
jQueryjQuery
jQuery & 10,000 Global Functions: Working with Legacy JavaScript
jQuery & 10,000 Global Functions: Working with Legacy JavaScriptjQuery & 10,000 Global Functions: Working with Legacy JavaScript
jQuery & 10,000 Global Functions: Working with Legacy JavaScript
Guy Royse
 
jQuery: out with the old, in with the new
jQuery: out with the old, in with the newjQuery: out with the old, in with the new
jQuery: out with the old, in with the newRemy Sharp
 
Javascript in Plone
Javascript in PloneJavascript in Plone
Javascript in Plone
Steve McMahon
 
Yearning jQuery
Yearning jQueryYearning jQuery
Yearning jQueryRemy Sharp
 

Similar to Ruby-ying Javascript: Avoiding jQuery Spaghetti (20)

jQuery for Beginners
jQuery for Beginners jQuery for Beginners
jQuery for Beginners
 
Cheap frontend tricks
Cheap frontend tricksCheap frontend tricks
Cheap frontend tricks
 
Hooks WCSD12
Hooks WCSD12Hooks WCSD12
Hooks WCSD12
 
Building Large jQuery Applications
Building Large jQuery ApplicationsBuilding Large jQuery Applications
Building Large jQuery Applications
 
The Ring programming language version 1.10 book - Part 71 of 212
The Ring programming language version 1.10 book - Part 71 of 212The Ring programming language version 1.10 book - Part 71 of 212
The Ring programming language version 1.10 book - Part 71 of 212
 
Cyclejs introduction
Cyclejs introductionCyclejs introduction
Cyclejs introduction
 
State of jQuery and Drupal
State of jQuery and DrupalState of jQuery and Drupal
State of jQuery and Drupal
 
Modular and Event-Driven JavaScript
Modular and Event-Driven JavaScriptModular and Event-Driven JavaScript
Modular and Event-Driven JavaScript
 
Let jQuery Rock Your World
Let jQuery Rock Your WorldLet jQuery Rock Your World
Let jQuery Rock Your World
 
[removed] $file, removeRemove}, list #su.docx
[removed] $file, removeRemove}, list #su.docx[removed] $file, removeRemove}, list #su.docx
[removed] $file, removeRemove}, list #su.docx
 
Programmation fonctionnelle en JavaScript
Programmation fonctionnelle en JavaScriptProgrammation fonctionnelle en JavaScript
Programmation fonctionnelle en JavaScript
 
Kick start with j query
Kick start with j queryKick start with j query
Kick start with j query
 
i am unsure how to make the button below the image instead of on it- a.pdf
i am unsure how to make the button below the image instead of on it- a.pdfi am unsure how to make the button below the image instead of on it- a.pdf
i am unsure how to make the button below the image instead of on it- a.pdf
 
Creating an Uber Clone - Part VIII - Transcript.pdf
Creating an Uber Clone - Part VIII - Transcript.pdfCreating an Uber Clone - Part VIII - Transcript.pdf
Creating an Uber Clone - Part VIII - Transcript.pdf
 
Fact, Fiction, and FP
Fact, Fiction, and FPFact, Fiction, and FP
Fact, Fiction, and FP
 
jQuery
jQueryjQuery
jQuery
 
jQuery & 10,000 Global Functions: Working with Legacy JavaScript
jQuery & 10,000 Global Functions: Working with Legacy JavaScriptjQuery & 10,000 Global Functions: Working with Legacy JavaScript
jQuery & 10,000 Global Functions: Working with Legacy JavaScript
 
jQuery: out with the old, in with the new
jQuery: out with the old, in with the newjQuery: out with the old, in with the new
jQuery: out with the old, in with the new
 
Javascript in Plone
Javascript in PloneJavascript in Plone
Javascript in Plone
 
Yearning jQuery
Yearning jQueryYearning jQuery
Yearning jQuery
 

More from Forrest Chang

Crystal is a Rubyists friend (quick anecdote)
Crystal is a Rubyists friend (quick anecdote)Crystal is a Rubyists friend (quick anecdote)
Crystal is a Rubyists friend (quick anecdote)
Forrest Chang
 
Making terminal based apps w:ruby
Making terminal based apps w:rubyMaking terminal based apps w:ruby
Making terminal based apps w:ruby
Forrest Chang
 
Working Effectively with Legacy Javascript code in Opal
Working Effectively with Legacy Javascript code in OpalWorking Effectively with Legacy Javascript code in Opal
Working Effectively with Legacy Javascript code in Opal
Forrest Chang
 
Opal-hot-reloader
Opal-hot-reloaderOpal-hot-reloader
Opal-hot-reloader
Forrest Chang
 
Rubyconf 2014 recap
Rubyconf 2014 recapRubyconf 2014 recap
Rubyconf 2014 recap
Forrest Chang
 
6 reasons Jubilee could be a Rubyist's new best friend
6 reasons Jubilee could be a Rubyist's new best friend6 reasons Jubilee could be a Rubyist's new best friend
6 reasons Jubilee could be a Rubyist's new best friend
Forrest Chang
 
Opal a new_hope
Opal a new_hopeOpal a new_hope
Opal a new_hope
Forrest Chang
 
Opal chapter 4_a_new_hope
Opal chapter 4_a_new_hopeOpal chapter 4_a_new_hope
Opal chapter 4_a_new_hope
Forrest Chang
 
Data Intensive RIAs on Rails with very little code (Netzke)
Data Intensive RIAs on Rails with very little code (Netzke)Data Intensive RIAs on Rails with very little code (Netzke)
Data Intensive RIAs on Rails with very little code (Netzke)
Forrest Chang
 
Rubyconf2012 recap
Rubyconf2012 recapRubyconf2012 recap
Rubyconf2012 recap
Forrest Chang
 
Opal - Ruby Style!! Ruby in the browser
Opal - Ruby Style!!  Ruby in the browserOpal - Ruby Style!!  Ruby in the browser
Opal - Ruby Style!! Ruby in the browser
Forrest Chang
 

More from Forrest Chang (11)

Crystal is a Rubyists friend (quick anecdote)
Crystal is a Rubyists friend (quick anecdote)Crystal is a Rubyists friend (quick anecdote)
Crystal is a Rubyists friend (quick anecdote)
 
Making terminal based apps w:ruby
Making terminal based apps w:rubyMaking terminal based apps w:ruby
Making terminal based apps w:ruby
 
Working Effectively with Legacy Javascript code in Opal
Working Effectively with Legacy Javascript code in OpalWorking Effectively with Legacy Javascript code in Opal
Working Effectively with Legacy Javascript code in Opal
 
Opal-hot-reloader
Opal-hot-reloaderOpal-hot-reloader
Opal-hot-reloader
 
Rubyconf 2014 recap
Rubyconf 2014 recapRubyconf 2014 recap
Rubyconf 2014 recap
 
6 reasons Jubilee could be a Rubyist's new best friend
6 reasons Jubilee could be a Rubyist's new best friend6 reasons Jubilee could be a Rubyist's new best friend
6 reasons Jubilee could be a Rubyist's new best friend
 
Opal a new_hope
Opal a new_hopeOpal a new_hope
Opal a new_hope
 
Opal chapter 4_a_new_hope
Opal chapter 4_a_new_hopeOpal chapter 4_a_new_hope
Opal chapter 4_a_new_hope
 
Data Intensive RIAs on Rails with very little code (Netzke)
Data Intensive RIAs on Rails with very little code (Netzke)Data Intensive RIAs on Rails with very little code (Netzke)
Data Intensive RIAs on Rails with very little code (Netzke)
 
Rubyconf2012 recap
Rubyconf2012 recapRubyconf2012 recap
Rubyconf2012 recap
 
Opal - Ruby Style!! Ruby in the browser
Opal - Ruby Style!!  Ruby in the browserOpal - Ruby Style!!  Ruby in the browser
Opal - Ruby Style!! Ruby in the browser
 

Recently uploaded

AI Pilot Review: The World’s First Virtual Assistant Marketing Suite
AI Pilot Review: The World’s First Virtual Assistant Marketing SuiteAI Pilot Review: The World’s First Virtual Assistant Marketing Suite
AI Pilot Review: The World’s First Virtual Assistant Marketing Suite
Google
 
Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...
Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...
Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...
Globus
 
BoxLang: Review our Visionary Licenses of 2024
BoxLang: Review our Visionary Licenses of 2024BoxLang: Review our Visionary Licenses of 2024
BoxLang: Review our Visionary Licenses of 2024
Ortus Solutions, Corp
 
Lecture 1 Introduction to games development
Lecture 1 Introduction to games developmentLecture 1 Introduction to games development
Lecture 1 Introduction to games development
abdulrafaychaudhry
 
Using IESVE for Room Loads Analysis - Australia & New Zealand
Using IESVE for Room Loads Analysis - Australia & New ZealandUsing IESVE for Room Loads Analysis - Australia & New Zealand
Using IESVE for Room Loads Analysis - Australia & New Zealand
IES VE
 
RISE with SAP and Journey to the Intelligent Enterprise
RISE with SAP and Journey to the Intelligent EnterpriseRISE with SAP and Journey to the Intelligent Enterprise
RISE with SAP and Journey to the Intelligent Enterprise
Srikant77
 
Globus Compute wth IRI Workflows - GlobusWorld 2024
Globus Compute wth IRI Workflows - GlobusWorld 2024Globus Compute wth IRI Workflows - GlobusWorld 2024
Globus Compute wth IRI Workflows - GlobusWorld 2024
Globus
 
GlobusWorld 2024 Opening Keynote session
GlobusWorld 2024 Opening Keynote sessionGlobusWorld 2024 Opening Keynote session
GlobusWorld 2024 Opening Keynote session
Globus
 
Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...
Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...
Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...
Anthony Dahanne
 
top nidhi software solution freedownload
top nidhi software solution freedownloadtop nidhi software solution freedownload
top nidhi software solution freedownload
vrstrong314
 
May Marketo Masterclass, London MUG May 22 2024.pdf
May Marketo Masterclass, London MUG May 22 2024.pdfMay Marketo Masterclass, London MUG May 22 2024.pdf
May Marketo Masterclass, London MUG May 22 2024.pdf
Adele Miller
 
Enhancing Research Orchestration Capabilities at ORNL.pdf
Enhancing Research Orchestration Capabilities at ORNL.pdfEnhancing Research Orchestration Capabilities at ORNL.pdf
Enhancing Research Orchestration Capabilities at ORNL.pdf
Globus
 
Beyond Event Sourcing - Embracing CRUD for Wix Platform - Java.IL
Beyond Event Sourcing - Embracing CRUD for Wix Platform - Java.ILBeyond Event Sourcing - Embracing CRUD for Wix Platform - Java.IL
Beyond Event Sourcing - Embracing CRUD for Wix Platform - Java.IL
Natan Silnitsky
 
Orion Context Broker introduction 20240604
Orion Context Broker introduction 20240604Orion Context Broker introduction 20240604
Orion Context Broker introduction 20240604
Fermin Galan
 
Field Employee Tracking System| MiTrack App| Best Employee Tracking Solution|...
Field Employee Tracking System| MiTrack App| Best Employee Tracking Solution|...Field Employee Tracking System| MiTrack App| Best Employee Tracking Solution|...
Field Employee Tracking System| MiTrack App| Best Employee Tracking Solution|...
informapgpstrackings
 
Exploring Innovations in Data Repository Solutions - Insights from the U.S. G...
Exploring Innovations in Data Repository Solutions - Insights from the U.S. G...Exploring Innovations in Data Repository Solutions - Insights from the U.S. G...
Exploring Innovations in Data Repository Solutions - Insights from the U.S. G...
Globus
 
Climate Science Flows: Enabling Petabyte-Scale Climate Analysis with the Eart...
Climate Science Flows: Enabling Petabyte-Scale Climate Analysis with the Eart...Climate Science Flows: Enabling Petabyte-Scale Climate Analysis with the Eart...
Climate Science Flows: Enabling Petabyte-Scale Climate Analysis with the Eart...
Globus
 
First Steps with Globus Compute Multi-User Endpoints
First Steps with Globus Compute Multi-User EndpointsFirst Steps with Globus Compute Multi-User Endpoints
First Steps with Globus Compute Multi-User Endpoints
Globus
 
Large Language Models and the End of Programming
Large Language Models and the End of ProgrammingLarge Language Models and the End of Programming
Large Language Models and the End of Programming
Matt Welsh
 
Understanding Globus Data Transfers with NetSage
Understanding Globus Data Transfers with NetSageUnderstanding Globus Data Transfers with NetSage
Understanding Globus Data Transfers with NetSage
Globus
 

Recently uploaded (20)

AI Pilot Review: The World’s First Virtual Assistant Marketing Suite
AI Pilot Review: The World’s First Virtual Assistant Marketing SuiteAI Pilot Review: The World’s First Virtual Assistant Marketing Suite
AI Pilot Review: The World’s First Virtual Assistant Marketing Suite
 
Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...
Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...
Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...
 
BoxLang: Review our Visionary Licenses of 2024
BoxLang: Review our Visionary Licenses of 2024BoxLang: Review our Visionary Licenses of 2024
BoxLang: Review our Visionary Licenses of 2024
 
Lecture 1 Introduction to games development
Lecture 1 Introduction to games developmentLecture 1 Introduction to games development
Lecture 1 Introduction to games development
 
Using IESVE for Room Loads Analysis - Australia & New Zealand
Using IESVE for Room Loads Analysis - Australia & New ZealandUsing IESVE for Room Loads Analysis - Australia & New Zealand
Using IESVE for Room Loads Analysis - Australia & New Zealand
 
RISE with SAP and Journey to the Intelligent Enterprise
RISE with SAP and Journey to the Intelligent EnterpriseRISE with SAP and Journey to the Intelligent Enterprise
RISE with SAP and Journey to the Intelligent Enterprise
 
Globus Compute wth IRI Workflows - GlobusWorld 2024
Globus Compute wth IRI Workflows - GlobusWorld 2024Globus Compute wth IRI Workflows - GlobusWorld 2024
Globus Compute wth IRI Workflows - GlobusWorld 2024
 
GlobusWorld 2024 Opening Keynote session
GlobusWorld 2024 Opening Keynote sessionGlobusWorld 2024 Opening Keynote session
GlobusWorld 2024 Opening Keynote session
 
Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...
Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...
Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...
 
top nidhi software solution freedownload
top nidhi software solution freedownloadtop nidhi software solution freedownload
top nidhi software solution freedownload
 
May Marketo Masterclass, London MUG May 22 2024.pdf
May Marketo Masterclass, London MUG May 22 2024.pdfMay Marketo Masterclass, London MUG May 22 2024.pdf
May Marketo Masterclass, London MUG May 22 2024.pdf
 
Enhancing Research Orchestration Capabilities at ORNL.pdf
Enhancing Research Orchestration Capabilities at ORNL.pdfEnhancing Research Orchestration Capabilities at ORNL.pdf
Enhancing Research Orchestration Capabilities at ORNL.pdf
 
Beyond Event Sourcing - Embracing CRUD for Wix Platform - Java.IL
Beyond Event Sourcing - Embracing CRUD for Wix Platform - Java.ILBeyond Event Sourcing - Embracing CRUD for Wix Platform - Java.IL
Beyond Event Sourcing - Embracing CRUD for Wix Platform - Java.IL
 
Orion Context Broker introduction 20240604
Orion Context Broker introduction 20240604Orion Context Broker introduction 20240604
Orion Context Broker introduction 20240604
 
Field Employee Tracking System| MiTrack App| Best Employee Tracking Solution|...
Field Employee Tracking System| MiTrack App| Best Employee Tracking Solution|...Field Employee Tracking System| MiTrack App| Best Employee Tracking Solution|...
Field Employee Tracking System| MiTrack App| Best Employee Tracking Solution|...
 
Exploring Innovations in Data Repository Solutions - Insights from the U.S. G...
Exploring Innovations in Data Repository Solutions - Insights from the U.S. G...Exploring Innovations in Data Repository Solutions - Insights from the U.S. G...
Exploring Innovations in Data Repository Solutions - Insights from the U.S. G...
 
Climate Science Flows: Enabling Petabyte-Scale Climate Analysis with the Eart...
Climate Science Flows: Enabling Petabyte-Scale Climate Analysis with the Eart...Climate Science Flows: Enabling Petabyte-Scale Climate Analysis with the Eart...
Climate Science Flows: Enabling Petabyte-Scale Climate Analysis with the Eart...
 
First Steps with Globus Compute Multi-User Endpoints
First Steps with Globus Compute Multi-User EndpointsFirst Steps with Globus Compute Multi-User Endpoints
First Steps with Globus Compute Multi-User Endpoints
 
Large Language Models and the End of Programming
Large Language Models and the End of ProgrammingLarge Language Models and the End of Programming
Large Language Models and the End of Programming
 
Understanding Globus Data Transfers with NetSage
Understanding Globus Data Transfers with NetSageUnderstanding Globus Data Transfers with NetSage
Understanding Globus Data Transfers with NetSage
 

Ruby-ying Javascript: Avoiding jQuery Spaghetti

  • 1. RUBYFYING JAVASCRIPT:RUBYFYING JAVASCRIPT: AVOIDING JQUERY SPAGHETTIAVOIDING JQUERY SPAGHETTI FORREST CHANGFORREST CHANG FKCHANG2000@YAHOO.COMFKCHANG2000@YAHOO.COM
  • 2. A JQUERY STORYA JQUERY STORY Have a problem Add a little jQuery Fixed, yay!
  • 3. AS TIME GOES ONAS TIME GOES ON Add function, add function, nest function Insert event handlers in DOM Add business logic Get input, dialogs Ajax Effects Update DOM Assign handlers to id etc.
  • 5. JQUERY SPAGHETTIJQUERY SPAGHETTI by Steve O'Brien in http://steve-obrien.com/javascript-jquery-spaghetti/ Without a strong framework or architecture you end up with jQuery spaghetti. This is usually because you start with a small piece of jquery shoved somewhere in the dom as you add features it all grows out of proportion and becomes a tangled mess. The most challenging thing is maintaining state. Relying heavily on jQuery means your application state information is stores in the dom, this works well for small features and isolated components here and there, but in a complex app it quickly becomes very difficult to manage.
  • 6. JQUERY SPAGHETTI IS JSAPABO #1JQUERY SPAGHETTI IS JSAPABO #1 JavaScript AntiPatterns Addressed by Opal (JSAPABO) There are anti patterns addressed by Opal - trying to codify why Opal makes browser code better, starting w/naming what's wrong Very much a Work In Progress, apologies http://funkworks.blogspot.com/2015/04/javascript-antipatterns- addressed-by.html
  • 7. OPAL CAN HELPOPAL CAN HELP What is Opal? TLDR; Ruby in the browser How can it help? Ruby > JS Culture, Conventions OOP and More! See here for more info
  • 8. REAL LIFE STORYREAL LIFE STORY Want slide out bar Need both Left and right slide bars Would like simple, low cruft Demo of finished product
  • 9. ORIGINAL CODEORIGINAL CODE HTTP://JSFIDDLE.NET/DMYTR/37/HTTP://JSFIDDLE.NET/DMYTR/37/ $.asm = {}; $.asm.panels = 1; function sidebar(panels) { $.asm.panels = panels; if (panels === 1) { $('#sidebar').animate({ left: -180, }); } else if (panels === 2) { $('#sidebar').animate({ left: 20, }); $('#sidebar').height($(window).height() - 50); } }; $(function() { $('#toggleSidebar').click(function() { if ($.asm.panels === 1) { $('#toggleSidebar i').addClass('glyphicon-chevron-left'); $('#toggleSidebar i').removeClass('glyphicon-chevron-right'); return sidebar(2); } else { $('#toggleSidebar i').removeClass('glyphicon-chevron-left'); $('#toggleSidebar i').addClass('glyphicon-chevron-right'); return sidebar(1); } }); });
  • 10. THE CODETHE CODE Simple Does what I want for the left sidebar
  • 11. HOW ABOUT THE RIGHT SIDEBAR? MY SPIKEHOW ABOUT THE RIGHT SIDEBAR? MY SPIKE $.asm2 = {}; $.asm2.panels = 1; function sidebar2(panels) { $.asm2.panels = panels; if (panels === 1) { $('#sidebar-right').animate({ right: -780, }); } else if (panels === 2) { $('#sidebar-right').animate({ right: 20, }); $('#mapCanvas').width($('#mapCanvas').parent().width()); $('#mapCanvas').height($(window).height() - 50); $('#sidebar-right').height($(window).height() - 50); } }; $(function() { $('#toggleSidebar-right').click(function() { if ($.asm2.panels === 1) { $('#toggleSidebar-right i').removeClass('glyphicon-chevron-left'); $('#toggleSidebar-right i').addClass('glyphicon-chevron-right'); return sidebar2(2); } else { $('#toggleSidebar-right i').addClass('glyphicon-chevron-left'); $('#toggleSidebar-right i').removeClass('glyphicon-chevron-right'); return sidebar2(1); } }); });
  • 12. KINDA UGLY CODEKINDA UGLY CODE Original code for a jsfiddle - don't expect a lot Typical for jQuery examples by itself not bad. NOT good OO code Now that concept has been proven, time to make the code "real"
  • 13. HOW TO CONVERT?HOW TO CONVERT? JUST TRANSLATE?JUST TRANSLATE? Didn't like it from the beginning Document.ready? { Element.find('#toggleSidebar').on :click { } }
  • 14. MIRED IN THE DETAILSMIRED IN THE DETAILS JSAPABO #6 Stuck in the weeds What's the big picture What's my intent?
  • 15. A BETTER APPROACHA BETTER APPROACH
  • 16. SEGUESEGUE Reasons Opal Makes your Browser Code Better #1 (ROMYBCB) - a future blog series In Ruby, we Think in Objects So Start w/objects
  • 17. HOW I WANT TO USE IT?HOW I WANT TO USE IT? # Create w/intent left_sidebar = Sidebar.new('#toggleSidebar', 'left') # elsewhere manipulate left_sidebar.hide
  • 18. BETTERBETTER Intent revealed Objects from the get go We'll see why this matters later
  • 19. CONVERTING THE JS CLICK HANDLERCONVERTING THE JS CLICK HANDLER // original code $(function() { $('#toggleSidebar').click(function() { if ($.asm.panels === 1) { $('#toggleSidebar i').addClass('glyphicon-chevron-left'); $('#toggleSidebar i').removeClass('glyphicon-chevron-right'); return sidebar(2); } else { $('#toggleSidebar i').removeClass('glyphicon-chevron-left'); $('#toggleSidebar i').addClass('glyphicon-chevron-right'); return sidebar(1); } }); }); WHAT DOES IT DO?WHAT DOES IT DO? if $.asm.panels = 1 // magic number 1 = closed state make sidebar handle left facing left sidebar(2) // set sidebar state to 2 (open state) - slide out else make sidebar handle face right sidebar(1) // set sidebar state to 2 (closed state) - slide in
  • 20. WHAT DOES IT DO AT A HIGHER LEVELWHAT DOES IT DO AT A HIGHER LEVEL step away from the details (JSAPABO #6) If the slider is open close it else open it
  • 21. OPAL CLICK HANDLER WITH INTENTION REVEALEDOPAL CLICK HANDLER WITH INTENTION REVEALED Put it in #initialize(), so it happens for each instance class Sidebar def initialize(element_id, side) @state = :closed Element.find(element_id).on :click { if @state == :open close else open end } end end
  • 22. WHERE TO HANG THE STATE?WHERE TO HANG THE STATE? JSAPABO #5 Where do you put state? Coz not using objects, where put state? Global? In jQuery, can hang off of jQuery $.asm.panels // hung off of jQuery Where would you hang data in Ruby/Opal instance variable, because you use objects from the get go easy @state = :closed
  • 23. IMPLEMENT OPEN AND CLOSE, ROUND 1IMPLEMENT OPEN AND CLOSE, ROUND 1 def open icon = Element.find("#{element_id} i") icon.add_class('glyphicon-chevron-left') icon.remove_class('glyphicon-chevron-right') Element.find('#sidebar').animate left: 20 @state = :open end def close icon = Element.find("#{element_id} i") icon.remove_class('glyphicon-chevron-left') icon.add_class('glyphicon-chevron-right') Element.find('#sidebar').animate left: -180 @state = :close end
  • 24. ROUND 2: REMOVE DUPLICATIONROUND 2: REMOVE DUPLICATION def open set_icon('glyphicon-chevron-left', 'glyphicon-chevron-right') Element.find('#sidebar').animate left: 20 @state = :open end def set_icon(class_to_add, class_to_remove) icon = Element.find("#{element_id} i") icon.add_class(class_to_add) icon.remove_class(class_to_remove) end def close set_icon('glyphicon-chevron-right', 'glyphicon-chevron-left') Element.find('#sidebar').animate left: -180 @state = :closed end
  • 25. ROUND 3: REFACTOR MORE DUPLICATIONROUND 3: REFACTOR MORE DUPLICATION def open set_icon('glyphicon-chevron-left', 'glyphicon-chevron-right', 20) @state = :open end def set_icon(class_to_add, class_to_remove, new_position) icon = Element.find("#{element_id} i") icon.add_class(class_to_add) icon.remove_class(class_to_remove) Element.find('#sidebar').animate left: new_position end def close set_icon('glyphicon-chevron-right', 'glyphicon-chevron-left', -180) @state = :closed end
  • 26. YET ANOTHER PATTERNYET ANOTHER PATTERN There's another pattern- the state change, so we move that functionality into set_icon def open set_icon('glyphicon-chevron-left', 'glyphicon-chevron-right', 20, :open) end def set_icon(class_to_add, class_to_remove, new_position, new_state) icon = Element.find("#{element_id} i") icon.add_class(class_to_add) icon.remove_class(class_to_remove) Element.find('#sidebar').animate left: new_position @state = new_state end def close set_icon('glyphicon-chevron-right', 'glyphicon-chevron-left', -180, :closed) end
  • 27. NEED A NEW NAMENEED A NEW NAME set_icon() no longer describes what it's doing def open new_state('glyphicon-chevron-left', 'glyphicon-chevron-right', 20, :open) end def new_state(class_to_add, class_to_remove, new_position, new_state) icon = Element.find("#{element_id} i") icon.add_class(class_to_add) icon.remove_class(class_to_remove) Element.find('#sidebar').animate left: new_position @state = new_state end def close new_state('glyphicon-chevron-right', 'glyphicon-chevron-left', -180, :closed) end
  • 28. OPAL CODE THAT MATCHES THE JSFIDDLEOPAL CODE THAT MATCHES THE JSFIDDLE # Sidebar abstraction class Sidebar attr_reader :element_id def initialize(element_id, side) @element_id = element_id @state = :closed Element.find("#{element_id} .toggles").on :click do if @state == :open close else open end end end def open new_state('glyphicon-chevron-left', 'glyphicon-chevron-right', 20, :open) end def new_state(class_to_add, class_to_remove, new_position, new_state) icon = Element.find("#{element_id} i") icon.add_class(class_to_add) icon.remove_class(class_to_remove) Element.find("#{element_id}").animate left: new_position @state = new_state end def close new_state('glyphicon-chevron-right', 'glyphicon-chevron-left', -180, :closed) end end Document.ready? { left_sidebar = Sidebar.new('#sidebar', 'left') }
  • 29. CODE IS BETTERCODE IS BETTER About same lines of code (LOC) More intention revealing Code can be reused/repurposed Can programmaticaly open or close sidebar easily, i.e. left_sidebar.open Couldn't do that w/original code WITHOUT refactoring
  • 30. STILL NEED A RIGHT SIDEBARSTILL NEED A RIGHT SIDEBAR Begin w/the end in mind Document.ready? { left_sidebar = Sidebar.new('#sidebar', 'left') right_sidebar = Sidebar.new('#sidebar-right', 'right) }
  • 31. ORIGINAL EVIL CUT AND PASTE CODEORIGINAL EVIL CUT AND PASTE CODE $.asm2 = {}; $.asm2.panels = 1; function sidebar2(panels) { $.asm2.panels = panels; if (panels === 1) { $('#sidebar-right').animate({ right: -780, }); } else if (panels === 2) { $('#sidebar-right').animate({ right: 20, }); $('#sidebar-right').height($(window).height() - 50); } }; $(function() { $('#toggleSidebar-right').click(function() { if ($.asm2.panels === 1) { $('#toggleSidebar-right i').removeClass('glyphicon-chevron-left'); $('#toggleSidebar-right i').addClass('glyphicon-chevron-right'); return sidebar2(2); } else { $('#toggleSidebar-right i').addClass('glyphicon-chevron-left'); $('#toggleSidebar-right i').removeClass('glyphicon-chevron-right'); return sidebar2(1); } }); });
  • 32. NOTESNOTES Because of JSAPABO #5, needed to store right panel state Can't use $.asm.panels, cut and paste $.asm2 What if I want a dropdown, $.asm3 ? Not a problem if dealing with objects from the get go
  • 33. PARAMETRIZEPARAMETRIZE Instead of converting the copy pasted code, we could parametrize by side Add below to #initialize set_params_for_side(side)
  • 34. SETTING VALUES FOR :LEFTSETTING VALUES FOR :LEFT Set values and use them
  • 35. SETTING LEFTSETTING LEFT attr_reader :closed_icon_class, :opened_icon_class, :opened_x_position, :closed_x_positio n def set_params_for_side(side) if side == :left @closed_icon_class = 'glyphicon-chevron-right' @opened_icon_class = 'glyphicon-chevron-left' @opened_x_position = 20 @closed_x_position = -180 end end def open new_state(opened_icon_class, closed_icon_class, opened_x_position, :open) end def new_state(class_to_add, class_to_remove, new_position, new_state) icon = Element.find("#{element_id} i") icon.add_class(class_to_add) icon.remove_class(class_to_remove) Element.find("#{element_id}").animate left: new_position @state = new_state end def close new_state(closed_icon_class, opened_icon_class, closed_x_position, :closed) end
  • 36. HANDLE NON LEFT PARAMETERHANDLE NON LEFT PARAMETER attr_reader :closed_icon_class, :opened_icon_class, :opened_x_position, :closed_x_position, :x_position_side def set_params_for_side(side) if side == :left @closed_icon_class = 'glyphicon-chevron-right' @opened_icon_class = 'glyphicon-chevron-left' @opened_x_position = 20 @closed_x_position = -180 @x_position_side = 'left' else @closed_icon_class = 'glyphicon-chevron-left' @opened_icon_class = 'glyphicon-chevron-right' @opened_x_position = 20 @closed_x_position = -780 @x_position_side = 'right' end end def open new_state(opened_icon_class, closed_icon_class, opened_x_position, :open) end def new_state(class_to_add, class_to_remove, new_position, new_state) icon = Element.find("#{element_id} i") icon.add_class(class_to_add) icon.remove_class(class_to_remove) Element.find("#{element_id}").animate x_position_side => new_position @state = new_state end
  • 37. DONE FOR NOWDONE FOR NOW Does what I need Exceeds original implementation Reusable
  • 38. RESULTING CODERESULTING CODE class Sidebar attr_reader :element_id def initialize(element_id, side) @element_id = element_id @state = :closed set_params_for_side(side) Element.find("#{element_id} .toggles").on :click do if @state == :open close else open end end end attr_reader :closed_icon_class, :opened_icon_class, :opened_x_position, :closed_x_position, :x_position_side def set_params_for_side(side) if side == :left @closed_icon_class = 'glyphicon-chevron-right' @opened_icon_class = 'glyphicon-chevron-left' @opened_x_position = 20 @closed_x_position = -180 @x_position_side = 'left' else @closed_icon_class = 'glyphicon-chevron-left' @opened_icon_class = 'glyphicon-chevron-right' @opened_x_position = 20 @closed_x_position = -780 @x_position_side = 'right' end end
  • 39. PAGE 2PAGE 2 def open new_state(opened_icon_class, closed_icon_class, opened_x_position, :open) end def new_state(class_to_add, class_to_remove, new_position, new_state) icon = Element.find("#{element_id} i") icon.add_class(class_to_add) icon.remove_class(class_to_remove) Element.find("#{element_id}").animate x_position_side => new_position @state = new_state end def close new_state(closed_icon_class, opened_icon_class, closed_x_position, :closed) end end Document.ready? { left_sidebar = Sidebar.new('#sidebar', 'left') right_sidebar = Sidebar.new('#sidebar-right', 'right') }
  • 40. CONCLUSIONCONCLUSION Opal gives you Better Code Better functionality Happiness Blogged here