【问题标题】:adapting Coffeescript for a rails application为 Rails 应用程序调整 Coffeescript
【发布时间】:2013-06-28 15:36:24
【问题描述】:

我有一个应用程序,用户在其中添加了student_group,他们在其中声明了该组中有多少students。我正在尝试从 thread (我通过 js2coffee.org 更改为 coffeescript)调整 js 以使用我的应用程序,但我之前从未学习或使用过 js,所以我遇到了一些麻烦。感谢您的帮助!

student_groups.js.coffee

# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
# 
# https://stackoverflow.com/questions/15130587/jquery-add-or-remove-table-row-based-on-inputs

emptyRow = ->
  row_i++
  @obj = $("<tr></tr>")
  @obj.append "<td><input type=\"text\" size=\"5\" value=\"" + row_i + "\"/></td>"
  @obj.append "<td><input type=\"text\" size=\"5\" name=\"mm" + row_i + "\" id=\"id_mm" + row_i + "\"\"/></td>"
  @obj.append "<td><input type=\"text\" size=\"5\" name=\"dd" + row_i + "\" id=\"id_dd" + row_i + "\"\"/></td>"
  @obj.append "<td><input type=\"text\" size=\"5\" name=\"ma" + row_i + "\" id=\"id_ma" + row_i + "\"\"/></td>"
  @obj.append "<td><input type=\"text\" size=\"5\" name=\"sr" + row_i + "\" id=\"id_sr" + row_i + "\" value=\"0\"\"/></td>"
# how many applications we have drawed now ?
refresh = (new_count) ->
  if new_count > 0
    $("#nos_header").show()
  else
    $("#nos_header").hide()
  old_count = parseInt($("tbody").children().length)
# the difference, we need to add or remove ?
  rows_difference = parseInt(new_count) - old_count
# if we have rows to add
  if rows_difference > 0
    i = 0

  while i < rows_difference
      $("tbody").append (new emptyRow()).obj
      i++
  else if rows_difference < 0 # we need to remove rows ..
    index_start = old_count + rows_difference + 1
    $("tr:gt(" + index_start + ")").remove()
    row_i += rows_difference
row_i = 0
$(document).ready ->
  $("#nos").change ->
    refresh $(this).val()

student_groups/new

<%= form_for(@student_group) do |f| %>

  <p>
    <%= f.label :name, "Enter a nickname for this group" %>
    <%= f.text_field :name, placeholder: "..." %>
  </p>  

  <p>
    <%= f.label :number_of_students, "How many students are in this group?" %>
    <!-- https://stackoverflow.com/questions/10038993/rails-and-forms-drop-down-with-range-of-numbers-and-unlimited -->
    <%= f.select :number_of_students, (0..60), :id => "nos" %>
  </p>

<table class="student_input_form">
  <tbody>    
    <tr>
      <td><%= f.label :name, "What is the student's name?" %></td>
      <td><%= f.label :gender, "What is the student's gender?" %></td>
    </tr>
      <%= f.fields_for :students do |builder| %>
        <%= render 'students/form', :f => builder %>
      <% end %>
  </tbody>
</table>

<%= f.submit "Submit", :class => 'big_button round unselectable' %>

<% end %>

最后,`students/_form'

<tr id="nos_header" style="display:none">
  <td><%= f.text_field :name, placeholder: "..." %></td>
  <td><%= f.select :gender, ['Female', 'Male', 'Transgender'] %></td>   
</tr>

编辑:@mu,解决您的评论:(1)已修复格式,尽管输入框和输出几乎没有关系。我已经阅读了关于在 SO 上格式化文本/代码的文档,但是仍然没有点击清楚。道歉。 (2) 取出那个打开的&lt;p&gt; 标签——认为那一定是更改前的遗留物。感谢您指出。 (3)至于什么不起作用-我应该更清楚。我的目标是在我最初链接到的页面上实现类似于this 的功能。当用户输入学生人数时,coffeescript 应该启动并显示适当数量的字段,以便输入那么多新学生。然而,目前,这并没有发生。我在浏览器中没有收到任何错误(即,else 一切正常) - 但是当我选择学生人数时,页面上没有任何变化。感谢您的帮助!

【问题讨论】:

  • (1) 清理您的格式和缩进,以便您的代码可读。 (2) 您不能将&lt;table&gt; 放在&lt;p&gt; 中,因此在您的&lt;table class="student_input_form"&gt; 之前有一个隐含的&lt;/p&gt;,您可能想要明确说明。 (3) 问题是什么?有什么不工作吗?在这种情况下,“不起作用”是什么意思?
  • @muistooshort,见编辑。

标签: jquery ruby-on-rails-3 coffeescript


【解决方案1】:

首先,js2coffee 生成的“CoffeeScript”正如预期的那样是非常糟糕的 CoffeeScript。很明显,一个软件只是从一种语言音译到另一种语言,而不了解代码的真正作用。如果您打算进行任何 Web 开发,我强烈建议您学习 JavaScript(如果需要,还可以学习 CoffeeScript)。

现在进入代码。功能分解很好,但功能内部过于复杂。

您不需要在全局变量中跟踪row_i,甚至根本不需要跟踪它,因为您可以随时计算所需的索引;所以我们会把它扔掉,假装它从未发生过。

您的主 HTML 应该看起来更像这样:

<table class="student_input_form">
    <thead>    
        <tr>
            <th>Name</th>
            <th>Gender</th>
        </tr>
    </thead>
    <tbody>
    </tbody>
</table>

将表头放在&lt;thead&gt; 中,因为这就是&lt;thead&gt; 的用途;此外,如果您的标题行在&lt;tbody&gt; 内,那么在使用实体&lt;tr&gt;s 时,您必须进行一系列+1/-1 调整。也可以将&lt;th&gt; 用于标题。这为您提供了更加语义化的结构,并使事情更容易设计和使用。

emptyRow 函数可以而且应该大大简化。您应该将row_i 作为参数传递。而且,您在 CoffeeScript 中有字符串插值,因此您不需要所有 string + string 噪音。您还可以在 CoffeeScript 中使用“here-strings”,当您调用 append 时,jQuery 很乐意获取整个 HTML 片段;这些简单的事情可以让你做这样的事情:

emptyRow = (row_i) ->
    """
        <tr>
            <td><input type="text" size="5" value="#{row_i}"></td>
            ...
        </tr>
    """

这实际上是可读的,不像一大堆转义引号和字符串连接。您还可以使用部分填充 &lt;script&gt; 以用作模板,这为您提供:

<!-- Your partial would go inside... -->
<script id="empty_row" type="text/x-template">
    <tr>
        <td><input type="text" size="5" value="{row_i}"></td>
        <td><input type="text" size="5" name="mm{row_i}" id="id_mm{row_i}"></td>
    </tr>
</script>

然后emptyRow 减少为:

emptyRow = (row_i) ->
    $('#empty_row').html().replace(/\{row_i\}/g, row_i)

我想一个真正的客户端模板解决方案会更好,但是对于像这样的简单情况,简单的正则表达式修改就可以了。

refresh 函数也可以大大简化。如果您要多次使用 jQuery 选择器,只需计算一次并将其保存在变量中,让您的生活更轻松:

$tbody = $('.student_input_form tbody');

上面的 HTML 结构可以很容易地计算出我们当前有多少行:

current_rows = $tbody.find('tr').length

我们得到我们需要多少作为参数:

refresh = (need_rows) ->

如果我们需要添加新的行,那么你可以使用一个简单的循环结合一个范围数组:

if(current_rows < need_rows)
    $tbody.append(emptyRow(i)) for i in [current_rows ... need_rows]

... 或多或少与 Ruby Range 中的相同,这同时为我们提供了两件事:

  1. &lt;tr&gt;s 的正确数量。
  2. emptyRow 的正确索引值,这样我们就不会得到重复的 id 属性。

如果您需要删除行,那么您可以很好地利用jQuery's :gt selector,它可以采用负索引从末尾开始计数(就像 Ruby 的数组...):

else if(current_rows > need_rows)
    $tbody.find("tr:gt(#{need_rows - current_rows - 1})").remove()

我们仍然坚持-1 调整,但这就是生活。

结果又好又瘦:

refresh = (need_rows) ->
    $tbody = $('.student_input_form tbody')
    current_rows = $tbody.find('tr').length

    if(current_rows < need_rows)
        $tbody.append(emptyRow(i)) for i in [current_rows ... need_rows]
    else if(current_rows > need_rows)
        $tbody.find("tr:gt(#{need_rows - current_rows - 1})").remove()

然后全力以赴:

$(document).ready ->
    $("#nos").change ->
        refresh(parseInt($(@).val(), 10))

注意parseInt 调用在这里,所以refresh 可以假定need_rows 是一个数字。另请注意,parseInt 是使用显式 radix 参数调用的,在使用 parseInt 时始终指定显式基数,这样您就不会得到八进制惊喜和类似的位混乱。

欢迎您将我的版本与您的原始版本进行比较,看看错误在哪里。

演示:http://jsfiddle.net/ambiguous/qmVaK/


一些教训:

  1. 请勿在不了解其工作原理时使用他人的代码。
  2. 如果您要编写 CoffeeScript,请学习 CoffeeScript 并在 CoffeeScript 上编写代码。忘记像 js2coffee 这样的翻译工具存在吧,它们对你没有任何好处。
  3. 了解您的工具的工作原理。 CoffeeScript 和 jQuery 中有很多有用的东西可以让你的代码更紧凑,更容易理解。

【讨论】:

  • 谢谢,穆。我从另一个问题中得到了this answer(我刚刚报废了咖啡脚本,因为我可以说它转换得不好——正如你所说,这是有道理的,因为转换器工具不能代替知道你在做什么:p )。为您的帮助干杯,我真的很感激!我将比较这两个,但我已经看出你是对的 - CoffeeScript 更精简!
猜你喜欢
  • 1970-01-01
  • 2019-01-12
  • 2012-03-04
  • 2023-03-24
  • 2012-02-02
  • 2013-05-10
  • 1970-01-01
  • 2012-02-07
相关资源
最近更新 更多