Improve unobtrusive javascript (and maybe use CoffeeScript) in a Rails application

I have an application that uses some Javascript for basic Ajax requests such as autocomplete and direct search. For example, I implemented a live search as follows; I noticed some potential problem and would like to talk to you about this in order to have better code.

application / controllers / company_controller.rb

def livesearch @companies = Company.search(params[:query]) render :partial => "companies", :locals => {:companies => @companies} end 

application / views / companies / _companies.html.haml

 - if companies.empty? None - else %table#company_list %tr %th Name %th Description %th Products %th = render companies 

app / views / companies / _livesearch_box.html.haml

 = content_for :scripts, "jlivesearch companies" = form_tag "#", :autocomplete => :off, :remote => true do %span.light Search:   = text_field_tag :search :javascript $('#search').livesearch({ searchCallback: update_listed_companies, queryDelay: 200, innerText: "Search companies" }); 

public /JavaScripts/companies.js

 function update_listed_companies(query) { if(typeof query == "undefined") query = ""; $("#company_list_container").showWith( "/companies/livesearch?query=" + query, false ); } 

public /JavaScripts/application.js

 (function($) { $.fn.showWith = function (what, popup) { element = this; $.get(what, function(data) { element.html(data); if(popup) element.bPopup(); }); return element; }; })(jQuery); 

Here is what makes me suspicious about the optimality of my code:

  • I have Javascript code in _livesearch_box.html.haml .
  • Even if I put it in public/javascripts/companies_livesearch.js , I would have to hardcode the part of #search in it.
  • I have a #company_list_container (which is the div in which _companies.html.haml displayed), hard-coded in public/javascripts/companies.js .
  • I have a path /companies/liveseach?query= encoded in public/javascript/companies.js .
  • I do not use CoffeeScript, mainly because it expects (at least if you use Barista) to find clean javascript code somewhere (e.g. in app/coffeescripts/ ) and compile it into public/javascripts . But in my application, I also have a .js.erb file in my app/views/companies ; for example, I have a voting system that uses the following application / views / companies / _vote.js.erb : $("#vote_link_<%= escape_javascript(@company.id.to_s) %>").html("<%= escape_javascript(vote_link_for(@company)) %>") To replace the" Vote for this company "link with" Unvote this company "one (and vice versa) with an Ajax request and displayed by the vote and unvote in the controller. I know that there is a coffee-haml filter that compiles CoffeeScript inside haml files, but this is not what I definitely need, and is usually considered obsolete and considered something dirty (?).

So the questions are no less than:

  • How to get CoffeeScript in my app/views/*/*.js.* ?
  • Should I have app/views/*/*.js.* Files at all?
  • How to remove all these element identifiers and these hardcoded paths in javascripts in the most efficient and elegant way?

Sorry for the long question and thank you for reaching the end!

+6
javascript unobtrusive-javascript ruby-on-rails coffeescript dry
source share
1 answer

Routes

There are several solutions like js-routes (my fork) which will let you write Router.post_path(3) in your JS / CS. This way you can bypass hardcoding urls.

Mixing JS and ERB

I would advise you not to mix JS and Ruby. In most cases, you can get around this by refactoring your JS code, the result will be easier to read and can simply be moved to a clean JS / CS file.

 # based on your vote-link example and assuming that your link # looks like: # # %a(href="#"){:"data-company-id" => @company.id} Vote # => <a href="#" data-company-id="6">Vote</a> makeAllCompaniesVotable: () -> $('.company a.voteLink').click -> companyId = $(this).data('company-id') $.ajax url: Router.vote_company_path(companyId) # ... 

Unless you do evil eval magic, you won’t even need escape_javascript . But you will have to remove JavaScript from your particulars. jquery.livequery simplified the transition.

 $(`.company`).livequery -> # do something with $(this) 

will be called every time a .company inserted into the document.

DOM-Paths Hardcoding

If you are writing code for a particular dom-tree (or special view), I would not consider this a bad practice. Writing unobtrusive JS is similar to writing CSS - and we also rigidly denote #company_list_container in CSS, right?

 $("#vote_link_<%= escape_javascript(@company.id.to_s) %>") # this is ugly though 

Call JS code from the interface

To have an interface between CoffeeScript static files and views, I usually write something like:

 :javascript $(function(){Companies.index()}); $(function(){Application.globalEnhancements()}); 

at the end of my views. Then it will call the function that I wrote using CoffeeScript, which will then improve the site with all the necessary scripts. There may be more efficient approaches (e.g. with a Rails-like router for JavaScript - see Backbone.js), but it just works for me.

Also, if I need some data quite often (for example: current_user):

 :javascript window.current_user = #{current_user.to_json}; 

However, I do not think that there is an effective way of refactoring. I had to do a lot of refactoring to remove my ERB / ​​JS mess. However, it's worth it.

+6
source share

All Articles