我有一个应用程序,其中用户添加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)取出那个开放的<p>
标签 - 认为在改变之前一定是剩下的。谢谢你指出来。 (3)至于什么不起作用 - 我应该更清楚。我的目标是在我最初链接的页面上使用与this类似的功能。当用户输入学生人数时,coffeescript应该启动并显示适当数量的字段,以便输入许多新学生。但是,目前还没有发生这种情况。我没有在浏览器中收到任何错误(即,所有 else 都有效) - 但是当我选择学生人数时,页面上没有任何变化。谢谢你的帮助!
答案 0 :(得分:2)
首先,正如预期的那样,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>
将表格标题放在<thead>
内,因为这是<thead>
的用途;另外,如果您的标题行在<tbody>
内,那么在处理真实的<tr>
时,您必须进行一堆+ 1 / -1调整。也可以使用<th>
作为标题。这为您提供了更加语义的结构,使得样式和工作更容易。
emptyRow
功能可以而且应该大大简化。您应该将row_i
作为参数传递。并且,您在CoffeeScript中进行了字符串插值,因此您不需要所有string + string
噪声。您还可以在CoffeeScript中使用“here-strings”,jQuery很乐意在您调用append
时获取整个HTML片段;这些简单的事情可以让你做这样的事情:
emptyRow = (row_i) ->
"""
<tr>
<td><input type="text" size="5" value="#{row_i}"></td>
...
</tr>
"""
这实际上是可读的,不像一大堆转义引号和字符串连接。您还可以使用部分填充<script>
作为模板,它可以为您提供:
<!-- 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相同,它同时给我们两件事:
<tr>
s。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
,在使用parseInt
时始终指定显式基数,以便您不会获得八进制惊喜和类似的混乱。
欢迎您将我的版本与原始版本进行比较,以了解错误的位置。
演示:http://jsfiddle.net/ambiguous/qmVaK/
一些课程: