网站搜索

在 Rails 应用程序中改进不显眼的 JavaScript(并可能使用 CoffeeScript)


我有一个应用程序,它使用一些Javascript进行基本的Ajax请求,例如自动完成和实时搜索。例如,我以以下方式实现了实时搜索;我发现了一些潜在的问题,想和你讨论,以便提高代码质量。

应用程序/控制器/company_controller.rb

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

应用程序/视图/公司/_公司.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);

以下是让我对我的代码最优性产生怀疑的几点:

  • 我在 _livesearch_box.html.haml 中有 Javascript 代码。
  • 即使我将它放在 public/javascripts/companies_livesearch.js 中,我也必须在其中硬编码 #search 部分。
  • 我在 public/javascripts/companies.js 中硬编码了 #company_list_container(这是渲染 _companies.html.haml 的 div)。
  • 我在 public/javascript/companies.js 中将路径 /companies/liveseach?query= 硬编码了。
  • 我没有使用 CoffeeScript,主要是因为它期望(至少如果你使用 Barista)在某处找到纯 JavaScript 代码(例如在 app/coffeescripts/ 中)并将其编译到 public/javascripts 中。但在我的应用程序中,我在 app/views/companies 中也有一些 .js.erb 文件;例如,我有一个投票系统,在 app/views/companies/_vote.js.erb 中使用如下内容:$ ("#vote_link_<%= escape_javascript(@company.id.to_s) %>"). html("<%= escape_javascript(vote_link_for(@company)) %>") 用 Ajax 请求将“投票该公司”的链接替换为“取消投票该公司”的链接(反之亦然),并由控制器中的 vote 和 unvote 操作呈现。我知道有 coffee-haml-filter 可以在 haml 文件中编译 CoffeeScript,但这并不是我真正需要的,而且通常已被弃用并被认为是不好的做法(?)。

所以问题至少是:

  • 如何在我的 app/views/*/*.js.* 中使用 CoffeeScript?
  • 我是否应该完全拥有 app/views/*/*.js.* 文件?
  • 如何以最有效和优雅的方式移除 JavaScript 中硬编码的所有元素 ID 和路径?

抱歉问题有点长,感谢你能看到最后!

答案#1

路线

有一些解决方案,比如 js-routes(我的分支),它允许你在 JS/CS 中编写 Router.post_path(3)。这样你就可以避免硬编码 URL。

混合使用 JS 和 ERB

我建议你避免混合使用 JS 和 Ruby。在大多数情况下,你可以通过重构你的 JS 代码来避免这种情况,结果会更易于阅读,并且可以简单地移动到一个纯 JS/CS 文件中。

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)
      # ...

除非你使用邪恶的 eval 魔法,否则你甚至不需要 escape_javascript。但你将不得不从你的局部视图中移除 JavaScript。jquery.livequery 使过渡更加容易。

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

每次在文档中插入 .company 时都会被调用。

硬编码 DOM 路径

如果你正在为特定的 DOM 树(或特定的视图)编写代码,我不会认为这是不好的做法。编写无侵入的 JS 就像编写 CSS —— 我们在 CSS 中也硬编码了 #company_list_container,对吧?

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

从前端调用JS代码

为了在静态 CoffeeScript 文件和视图之间建立一个接口,我倾向于写类似这样的东西:

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

在我的视图的末尾。这将调用我用 CoffeeScript 编写的一个函数,然后它会用所有需要的脚本增强网站。可能有更好的方法(比如为 JavaScript 设置类似 Rails 的路由器——见 Backbone.js),但它简单且对我有效。

另外,如果我经常需要一些数据(例如:current_user):

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

然而,我不认为有一种高效的方法来重构。我不得不做大量重构来清理我的 ERB/JS 混乱。不过,仍然值得。