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

发布于 2024-10-22 03:46:55 字数 2951 浏览 2 评论 0原文

我有一个应用程序,它使用一些 Javascript 来执行基本的 Ajax 请求,例如自动完成和实时搜索。例如,我通过以下方式实现了实时搜索;我发现了一些潜在的问题,想与您讨论一下,以便有更好的代码。

app/controllers/company_controller.rb

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

app/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);

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

  • 我在 _livesearch_box.html.haml
  • 即使我将其放入 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 请求将“投票该公司”链接替换为“取消投票该公司”链接(反之亦然),并由 voteunvote 操作呈现在控制器中。 我知道有 Coffee-haml-filter 可以在 haml 文件中编译 CoffeeScript,但这不是我真正需要的,并且通常被弃用并被视为肮脏的东西(?)。

所以问题至少是:

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

抱歉问了这么长的问题,感谢您看完它!

I have an application which uses some Javascript for basic Ajax requests such as autocompletion and live search. For example I implemented live search in the following way; I spotted some potential issue and would like to talk with you about it, so to have a better code.

app/controllers/company_controller.rb

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

app/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 are the things that make me suspicious about the optimality of my code:

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

So the questions are at least:

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

Sorry for the long question and thanks for getting to the end of it!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

几味少女 2024-10-29 03:46:55

路线

有一些解决方案,例如 js-routes (我的叉子),它允许您编写 Router.post_path(3) 在你的 JS/CS 中。这样你就可以绕过硬编码 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-magic,否则你甚至不需要 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 编写的函数,该函数将使用所有需要的脚本增强站点。可能有更好的方法(比如有一个类似 Rails 的 JavaScript 路由器 - 请参阅 Backbone.js),但它很简单并且适合我。

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

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

但是我认为没有一种有效的重构方法。我必须进行大量重构才能消除 ERB/JS 混乱。不过还是值得的。

Routes

There are some solutions like js-routes (my fork) which will allow you to write Router.post_path(3) in your JS/CS. This way you can get around hardcoding urls.

Mixing JS and ERB

I would advise you to avoid mixing JS and Ruby. In most cases you can get around that by refactoring your JS code, the result will be easier to read and can simply be moved into a pure 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 the JavaScript from inside your partials. jquery.livequery made the transition easier.

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

will be called each time a .company is inserted into the document.

Hardcoding DOM-Paths

If you are writing code for a specific dom-tree (or a specifc view) I wouldn't consider it a bad practice. Writing unobtrusive JS is like writing CSS - and we hardcode #company_list_container in CSS too, don't we?

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

Calling the JS code from the frontend

To have an interface between the static CoffeeScript-files and the views I tend to write something like:

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

at the end of my views. This will then call a function I wrote with CoffeeScript, which will then enhance the site with all the needed scripts. There might be better approaches (like having a Rails-like Router for JavaScript - see Backbone.js) but it's simple and works for me.

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

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

However I don't think there is an efficient way to refactor. I had to do a lot of refactoring to get my ERB/JS mess removed. Still worth, though.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文