The Problem (Unreadable RJS)
I've been finding my RJS for my web-app markadee becoming increasingly more complicated and unmanageable . Just take a look at my RJS for marking a task:
check.js.rjs (orginal)
page["task_item_#{@task_list.id}_#{@task.id}"].remove
if @created_mark
page.insert_html :top,
"marks",
:partial => 'calendars/mark',
:object => @mark,
:locals => { :calendar => @calendar }
page["day_#{@mark.day}"].className = 'marked'
else
page.insert_html :bottom,
"mark_tasks_#{@mark.id}",
:partial => 'tasks/task',
:object => @task,
:locals => { :calendar => @calendar,
:task_list => @mark,
:checked => true,
:marked_today => @mark.today? }
page << "Task.watch_checked_box('#{form_authenticity_token}','#{@task.id}')"
page << "if($('checked_task_items_#{@task_list.id}')){"
page.insert_html :bottom,
"checked_task_items_#{@task_list.id}",
:partial => 'tasks/checked_task',
:object => @task,
:locals => { :calendar => @calendar,
:task_list => @task_list,
:checked => true,
:marked_today => false }
page << "}else if($('view_all_#{@task_list.id}')){"
page.replace "view_all_#{@task_list.id}",
:partial => 'task_lists/view_all',
:locals => { :calendar => @calendar, :task_list => @task_list }
page << "}else{"
page.insert_html :after, "new_task_forms_#{@task_list.id}",
:partial => 'task_lists/view_all',
:locals => { :calendar => @calendar, :task_list => @task_list }
page << "}"
end
The Solution (Refactor RJS into Helper Files)
We can move RJS code into helpers files just like any other view. Thats the helpers job; to simplify views. Check out the rails doc for moving RJS code to helper files.
Before
Its pretty obvious that what's happening is here a checked task is being inserted but we should be more immediately readable that thats what its doing.
page.insert_html :bottom,
"checked_task_items_#{@task_list.id}",
:partial => 'tasks/checked_task',
:object => @task,
:locals => { :calendar => @calendar,
:task_list => @task_list,
:checked => true,
:marked_today => false }
The Helper
So I take that code and and move into a new function in my helper.
def insert_checked_task(calendar,task_list,task)
page.insert_html :bottom,
"checked_task_items_#{task_list.id}",
:partial => 'tasks/checked_task',
:locals => {
:checked_task => task,
:calendar => calendar,
:task_list => task_list }
end
After
Back in our view you simply call the function from 'page' and it works! Talk about cleaner.
page.insert_checked_task(@calendar,@task_list,@task)
The Results
So after moving more of my RJS to helpers that is my final result
check.js.rjs (with helpers)
page.remove_task(@calendar,@task_list,@task)
page.ef_current_month
if @marked_today
page.insert_mark(@calendar,@mark)
page["day_#{mark.day}"].className = 'marked'
else
page.insert_task(@calendar,@task_list,@task,@mark)
page.watch_task('uncheck',@calendar,@task_list,@task,form_authenticity_token)
end
page.en
page.ef "checked_task_items_#{@task_list.id}"
page.insert_checked_task(@calendar,@task_list,@task)
page.esf "view_all_#{@task_list.id}"
page.replace_view_all(@calendar,@task_list)
page.els
page.insert_view_all(@calendar,@task_list)
page.en
If Statements in RJS
Ugly RJS IFs
If Statements have always been ugly in RJS since in order to evaluate if an element exists you have to write raw javascript.
page << "if($('checked_task_items_#{@task_list.id}')){"
page << "}else if($('view_all_#{@task_list.id}')){"
page << "}else{"
page << "}"
Pretty RJS IFs
So it just makes sense to clean them up using RJS. So I throw these functions in my application_helper.rb I'm not calling the functions 'if, elsif, else or end' since they are reserved words.
def ef(e)
page << "if($('#{e}')){"
end
def esf(e)
page << "}else if($('#{e}')){"
end
def els
page << "}else{"
end
def en
page << "}"
end
You can see that this is much easier to read
page.ef "checked_task_items_#{@task_list.id}"
page.esf "view_all_#{@task_list.id}"
page.els
page.en
and if you needs something more specific
def ef_current_month
page << " if('#{Time.current.year}_#{Time.current.month}' == $('month_value').readAttribute('value')){"
end
so I can do this
page.ef_current_month
if @marked_today
page.insert_mark(@calendar,@mark)
page["day_#{mark.day}"].className = 'marked'
else
page.insert_task(@calendar,@task_list,@task,@mark)
page.watch_task('uncheck',@calendar,@task_list,@task,form_authenticity_token)
end
page.en
Thats pretty much all there it is too it. Enjoy your clean RJS. Now for some shameless plugging of my web-app!
