Rails中不显眼的动态表单字段使用jQuery [英] Unobtrusive dynamic form fields in Rails with jQuery

查看:209
本文介绍了Rails中不显眼的动态表单字段使用jQuery的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图克服在Rails的动态表单字段的障碍 - 这似乎是一些框架不处理非常优雅。我也使用jQuery在我的项目。我安装了jRails,但我宁愿悄悄地写了AJAX code在可能的情况。

I'm attempting to get over the hurdle of dynamic form fields in Rails -- this appears to be something the framework doesn't handle very gracefully. I'm also using jQuery in my project. I have jRails installed, but I'd much rather write the AJAX code unobtrusively where possible.

我的形式是相当复杂的,两个或三个级别嵌套的也不在少数。我遇到的问题是生成正确形式的ID,因为他们是如此依赖于表单生成器上下文。我需要能够动态地在的has_many 的关系添加新字段或删除现有的记录,我完全不知所措。

My forms are fairly complex, two or three levels of nesting are not unusual. The problem I'm having is generating the correct form ids, since they are so dependant on the form builder context. I need to be able to dynamically add new fields or delete existing records in a has_many relationship, and I am completely at a loss.

我到目前为止看到的每一个例子是丑或那种方式。 Ryan Ba​​tes的教程需要RJS,这会导致一些pretty的丑陋突兀的JavaScript在标记,似乎已经被写入嵌套属性之前。我见过有不显眼的jQuery的例子的的,但我就是不明白它在做什么,而一直没能得到它在我的项目工作。

Every example I've seen so far has been ugly in one way or another. Ryan Bates' tutorial requires RJS, which results in some pretty ugly obtrusive javascript in the markup, and seems to have been written before nested attributes. I've seen a fork of that example with unobtrusive jQuery, but I just don't understand what it's doing, and haven't been able to get it working in my project.

有人可以提供如何做到这一点简单的例子吗?这甚至可能同时尊重控制器的基于REST的约定?

Can somebody provide a simple example of how this is done? Is this even possible while respecting the RESTful convention of the controllers?


安迪已经发布删除现有记录的一个很好的例子,有谁能够提供正确的属性创建新的领域的例子吗?我一直无法弄清楚如何与嵌套形式做到​​这一点。

Andy has posted an excellent example of deleting an existing record, can anybody provide an example of creating new fields with the correct attributes? I haven't been able to figure out how to do this with nested forms.

推荐答案

既然没有人提供了一个答案,即使在赏金,我终于成功地得到这个工作我自己。这不应该是一个难倒!希望这将是比较容易做的Rails 3.0。

Since nobody has offered an answer to this, even after a bounty, I've finally managed to get this working myself. This wasn't supposed to be a stumper! Hopefully this will be easier to do in Rails 3.0.

安迪的例子是直接删除记录,而无需提交表单服务器的一个好方法。在这种特殊情况下,我真正追求的是一种动态添加/做一个更新到嵌套表之前删除字段。这是一个稍微不同的情况下,因为作为字段被删除,而实际上并没有删除,直到在表单提交。我最终可能会同时使用视情况而定。

Andy's example is a good way of deleting records directly, without submitting a form to the server. In this particular case, what I'm really looking for is a way to dynamically add/remove fields before doing an update to a nested form. This is a slightly different case, because as the fields are removed, they aren't actually deleted until the form is submitted. I will probably end up using both depending on the situation.

我根据我的实现在蒂姆·莱利的复杂表单的例子叉。

首先建立了模型,并确保他们支持嵌套属性:

First set up the models, and make sure they support nested attributes:

class Person < ActiveRecord::Base
  has_many :phone_numbers, :dependent => :destroy
  accepts_nested_attributes_for :phone_numbers, :reject_if => lambda { |p| p.values.all?(&:blank?) }, :allow_destroy => true
end

class PhoneNumber < ActiveRecord::Base
  belongs_to :person
end

创建的联系号码的表单字段的局部视图:

Create a partial view for the PhoneNumber's form fields:

<div class="fields">
  <%= f.text_field :description %>
  <%= f.text_field :number %>
</div>

接下来写的人物模型的基本编辑观点:

Next write a basic edit view for the Person model:

<% form_for @person, :builder => LabeledFormBuilder do |f| -%>
  <%= f.text_field :name %>
  <%= f.text_field :email %>
  <% f.fields_for :phone_numbers do |ph| -%>
    <%= render :partial => 'phone_number', :locals => { :f => ph } %>
  <% end -%>
  <%= f.submit "Save" %>
<% end -%>

这将创建一个组的联系号码的模型,我们可以用JavaScript复制模板领域的工作。我们将创建的helper方法在应用程序/佣工/ application_helper.rb 此:

This will work by creating a set of template fields for the PhoneNumber model that we can duplicate with javascript. We'll create helper methods in app/helpers/application_helper.rb for this:

def new_child_fields_template(form_builder, association, options = {})
  options[:object] ||= form_builder.object.class.reflect_on_association(association).klass.new
  options[:partial] ||= association.to_s.singularize
  options[:form_builder_local] ||= :f

  content_tag(:div, :id => "#{association}_fields_template", :style => "display: none") do
    form_builder.fields_for(association, options[:object], :child_index => "new_#{association}") do |f|
      render(:partial => options[:partial], :locals => { options[:form_builder_local] => f })
    end
  end
end

def add_child_link(name, association)
  link_to(name, "javascript:void(0)", :class => "add_child", :"data-association" => association)
end

def remove_child_link(name, f)
  f.hidden_field(:_destroy) + link_to(name, "javascript:void(0)", :class => "remove_child")
end

现在添加这些辅助方法来编辑部分:

Now add these helper methods to the edit partial:

<% form_for @person, :builder => LabeledFormBuilder do |f| -%>
  <%= f.text_field :name %>
  <%= f.text_field :email %>
  <% f.fields_for :phone_numbers do |ph| -%>
    <%= render :partial => 'phone_number', :locals => { :f => ph } %>
  <% end -%>
  <p><%= add_child_link "New Phone Number", :phone_numbers %></p>
  <%= new_child_fields_template f, :phone_numbers %>
  <%= f.submit "Save" %>
<% end -%>

您现在有JS模板完成。这将提交一份空白模板为每个关联,但:reject_if 的示范条款将放弃它们,只留下用户创建的领域。 更新: 我重新思考这样的设计,见下文

You now have the js templating done. It will submit a blank template for each association, but the :reject_if clause in the model will discard them, leaving only the user-created fields. Update: I've rethought this design, see below.

这是不是真正的AJAX,因为没有任何通信回事超出了页面加载和提交表单服务器,但老实说,我无法找到一种方法在事后做。

This isn't truly AJAX, since there isn't any communication going on to the server beyond the page load and form submit, but I honestly could not find a way to do it after the fact.

在事实上,这可能提供比AJAX更好的用户体验,因为你没有,直到你完成等待每增加现场服务器响应。

In fact this may provide a better user experience than AJAX, since you don't have to wait for a server response for each additional field until you're done.

最后,我们需要这样组装起来的JavaScript。以下内容添加到您的`公有/ Java脚本/ application.js中文件:

Finally we need to wire this up with javascript. Add the following to your `public/javascripts/application.js' file:

$(function() {
  $('form a.add_child').click(function() {
    var association = $(this).attr('data-association');
    var template = $('#' + association + '_fields_template').html();
    var regexp = new RegExp('new_' + association, 'g');
    var new_id = new Date().getTime();

    $(this).parent().before(template.replace(regexp, new_id));
    return false;
  });

  $('form a.remove_child').live('click', function() {
    var hidden_field = $(this).prev('input[type=hidden]')[0];
    if(hidden_field) {
      hidden_field.value = '1';
    }
    $(this).parents('.fields').hide();
    return false;
  });
});

这个时候,你应该有一个准系统动态表单!这里的JavaScript是非常简单的,并且可以很容易地与其他框架进行。你可以很容易地与原型+ lowpro例如取代我的的application.js code。其基本思路是,你不嵌入硕大的JavaScript功能集成到您的标记,而不必写繁琐 phone_numbers =()模型中的功能。一切都只是工作。万岁!

By this time you should have a barebones dynamic form! The javascript here is really simple, and could easily be done with other frameworks. You could easily replace my application.js code with prototype + lowpro for instance. The basic idea is that you're not embedding gigantic javascript functions into your markup, and you don't have to write tedious phone_numbers=() functions in your models. Everything just works. Hooray!

在一些进一步的测试,我已经得出结论,模板需要被移出&LT的;形式&GT; 字段。让他们有意味着他们送回到服务器与形式的休息,这只是创建头痛以后。

After some further testing, I've concluded that the templates need to be moved out of the <form> fields. Keeping them there means they get sent back to the server with the rest of the form, and that just creates headaches later.

我把这个添加到我的布局的底部:

I've added this to the bottom of my layout:

<div id="jstemplates">
  <%= yield :jstemplates %>
</div

和修改 new_child_fields_template 助手:

def new_child_fields_template(form_builder, association, options = {})
  options[:object] ||= form_builder.object.class.reflect_on_association(association).klass.new
  options[:partial] ||= association.to_s.singularize
  options[:form_builder_local] ||= :f

  content_for :jstemplates do
    content_tag(:div, :id => "#{association}_fields_template", :style => "display: none") do
      form_builder.fields_for(association, options[:object], :child_index => "new_#{association}") do |f|        
        render(:partial => options[:partial], :locals => { options[:form_builder_local] => f })        
      end
    end
  end
end

现在,你可以删除:reject_if 从模型条款,不用担心被送回模板

Now you can remove the :reject_if clauses from your models and stop worrying about the templates being sent back.

这篇关于Rails中不显眼的动态表单字段使用jQuery的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆