【问题标题】:Adding\Removing items in a nested form using Rails 4 and JQuery使用 Rails 4 和 JQuery 以嵌套形式添加\删除项目
【发布时间】:2014-10-01 10:01:22
【问题描述】:

我正在使用 ruby​​ 1.9.3 和 rails 4.1。
我对 ruby​​ 很陌生,以前没有 javascripting 知识。

我有几个简单的有很多关系,一个有很多通过关系。

class Service < ActiveRecord::Base
  has_many :websites,   :dependent => :destroy
  has_many :timetables, :dependent => :destroy
  has_many :service_places, :dependent => :destroy
  has_many :places, through: :service_places
end

class ServicePlace < ActiveRecord::Base
  belongs_to :service
  belongs_to :place
end

class Website < ActiveRecord::Base
  belongs_to :service
end

class Place < ActiveRecord::Base
  has_many :service_places    
  has_many :services, through: :service_places
end

class Timetable < ActiveRecord::Base
  belongs_to :service
end

我已经在服务表单 (_form.html.erb) 中为每个渲染良好的关联创建了嵌套表单,所有 CRUD 功能都可以工作。

<%= form_for(@service, :html => {:class => "form-horizontal", :role => "form"}) do |f| %>

  <%= render 'wcc_style/layouts/error_messages', object: @service %>

  <!-- I've skipped some of the html for the service details to just rendering the forms... -->

  <!-- Adds the Service_Places associations -->
  <%= f.fields_for :service_places do |service_places| %>
    <%= render "service_place_fields", :f => service_places %>
  <%end %>

  <!-- Adds the website associations via a partial -->
  <%= f.fields_for :websites do |website| %>
    <%= render "website_fields", :f => website %>
  <%end %>
  <%= link_to_add_fields "Add a website", f, :websites, "btn btn-default", "Add a new website" %>


  <!-- Adds the timetables associations via a partial -->
  <%=f.fields_for :timetables do |timetable| %>
    <%= render "timetable_fields", :f => timetable %>
  <%end %>

  <%= f.submit 'Save', :class => 'btn btn-primary' %>
  <%= link_to 'Cancel', services_path, :class => "btn btn-default", :role =>"button" %>

<% end %>

网站部分示例 (_website_fields.html.erb)

<!-- This partial holds the websites information for main service form -->
<div class="form-group"> 
  <%= f.label :website, :class=>'col-sm-2 control-label' %>
  <div class="col-sm-4">
    <%= f.text_field :url, :class => "form-control", :placeholder => "Service Provider Website" %>
  </div>
</div>

我现在要做的是添加功能,以便在添加/编辑服务记录时能够添加和删除与服务相关的项目(例如网站、时间表、service_places)。我看过很多与此相关的帖子,但没有使用 jquery 和 rails 4 的良好工作解决方案。

在对此进行了一些研究后,我发现了旧的 railcast 196 和 197。它们已经过时并且在 rails 4 中不起作用。更多的研究表明命令已被贬值。我看到有一个更新版本的 railscasts 我还没有看过。我确实阅读了修订版上的 cmets,它表明它使用的是咖啡脚本,尽管我无法确认这一点。

我还看到了使用 cocoon gem 和nested_form_fields 的其他潜在解决方案的链接。两者似乎都使用我不熟悉的 HAML。

我正在尝试使用 jquery 来做到这一点,以帮助我更了解 javascript 流程(因为我需要在开发完成后支持开发,而我们的标准是尽可能使用 JQuery)。

我已经设法获得了添加类型的工作,因为它在第一次按下时呈现网站标签和复选框,但随后在随后的点击中再次呈现它大约一秒钟,然后消失。该位置也不是我期望的位置,在页面顶部,但我确信这取决于我们自己处理 CSS 的样式宝石。我认为 javascript 不应该执行 POST,这似乎是因为它会在每次单击按钮时清除我的其他字段。

我的第一个问题是如何在不清除服务表单中的其他项目的情况下获得添加功能来添加新项目?

我的第二个问题是如何让删除功能起作用。我假设删除必须在部分的表单构建器中(例如,每个项目一个删除)。

我在下面包含了应用程序助手和 javascript(从各种帖子拼凑而成)。

application_helper.rb 模块ApplicationHelper

  #def link_to_remove_fields(name, f)
  #  f.hidden_field(:destroy) + link_to_function(name, "remove_fields(this)")
  #end

  # remove link doesn't work!!!!!!!!!
  def link_to_remove_fields(name, f)
    f.hidden_field(:_destroy) + link_to_function(name, "remove_fields(this)")
  end

  # This now sort of works but the field disappears when it is created after about a second.  It also appears at the top of the page.  
  def link_to_add_fields(name, f, association, cssClass, title)  
    new_object = f.object.class.reflect_on_association(association).klass.new  
    fields = f.fields_for(association, new_object, :child_index => "new_#{association}") do |builder|  
      render(association.to_s.singularize + "_fields", :f => builder)  
    end  
    link_to name, "#", :onclick => h("add_fields(this, \"#{association}\", \"#{escape_javascript(fields)}\")"), :class => cssClass, :title => title 
  end 

end

应用程序.js

function remove_fields(link) {
    $(link).prev("input[type=hidden]").val("1");
    $(link).closest(".fields").hide();
}

function add_fields(link, association, content) {
    var new_id = new Date().getTime();
    var regexp = new RegExp("new_" + association, "g")
    $(link).parent().before(content.replace(regexp, new_id));
}

services_controller.rb

class ServicesController < ApplicationController
  # Set the service record before each action
  before_action :set_service, only: [:show, :edit, :update, :destroy]

  ################################################################################
  # Create
  ################################################################################
  def create
    @service = Service.new(service_params)

    respond_to do |format|
      if @service.save
        format.html { redirect_to @service, notice: 'Service was successfully created.' }
        format.json { render action: 'show', status: :created, location: @service }
      else
        @service.websites.build
        @service.timetables.build
        @service.service_places.build
        format.html { render action: 'new' }
        format.json { render json: @service.errors, status: :unprocessable_entity }
      end
    end

  end


  ################################################################################
  # Delete
  ################################################################################
  def destroy
    @service.destroy

    respond_to do |format|
      format.html { redirect_to services_url , notice: 'Service was successfully deleted.' }
      format.json { head :no_content }
    end
  end


  ################################################################################
  # Edit
  ################################################################################
  def edit
    @service.websites.build
    @service.timetables.build
    @service.service_places.build
  end


  ################################################################################
  # Index
  ################################################################################
  def index
    @services = Service.order(:service_number).page params[:page]
  end


  ################################################################################
  # New
  ################################################################################
  def new
    @service = Service.new
    @service.websites.build
    @service.timetables.build
    @service.service_places.build
  end


  ################################################################################
  # Search
  ################################################################################
  def search
    @search_term = params[:search_term]
    if @search_term && @search_term.strip.empty?
      redirect_to root_path
    else
      @services = Service.search(@search_term).order(:service_number).page params[:page]
    end
  end


  ################################################################################
  # Show
  ################################################################################
  def show
  end


  ################################################################################
  # Update
  ################################################################################
  def update
    respond_to do |format|
      if @service.update(service_params)
        format.html { redirect_to @service, notice: 'Service was successfully updated.' }
        format.json { head :no_content }
      else
        format.html { render action: 'edit' }
        format.json { render json: @service.errors, status: :unprocessable_entity }
      end
    end
  end


  ################################################################################
  # Private methods
  ################################################################################
  private

    # Use callbacks to share common setup or constraints between actions.
    def set_service
      @service = Service.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def service_params
      params.require(:service).permit(:service_number, :operator, :monday, :tuesday, :wednesday, :thursday, :friday, :saturday, :sunday, :bank_holiday, :search_term, websites_attributes: [:id, :service_id, :url, :_destroy], timetables_attributes: [:id, :service_id, :url, :_destroy], service_places_attributes: [:id, :service_id, :position, :place_id, :_destroy],  places_attributes: [:id, :place_name, :active, :_destroy])
    end

end

在互联网上搜索后,我似乎找不到任何关于如何使用 jquery 和标准 ruby​​\rails 使其工作的链接。有谁知道任何有助于实现这一目标的链接或资源?

假设我可以将 HAML 转换为 ERB 并将咖啡脚本转换为 javascript,我并不反对使用 cocoon 或 nested_form_fields 之类的 gem。作为 ruby​​\rails 和 javascript 的新手,让构建者做一些事情是很棒的,但是当它出错时,我不知道如何修复它。

感谢任何帮助或指导。

【问题讨论】:

    标签: jquery ruby-on-rails-4


    【解决方案1】:

    我没有快速得到任何地方,我跟进了同事的建议使用茧。我按照这里的步骤操作:cocoon gem on github 并设法让它工作。我知道这并不能回答我的实际问题,但它确实为我提供了一种方法来做我想做的事情。我已经为其他人记录了这些步骤,以防他们觉得有用。我还将 HAML 转换为普通的 ruby​​,因为我对此更满意。

    1) 将以下行添加到您的 gemfile:

    gem "cocoon"
    

    2) 将下面的行添加到您的 app/assets/javascripts/application.js 文件中

    //= require cocoon
    

    3) 修改我的服务表单(_form.html.erb)如下

    <%= form_for(@service, :html => {:class => "form-horizontal", :role => "form"}) do |f| %>
    
      <%= render 'wcc_style/layouts/error_messages', object: @service %>
    
      <div class="form-group">
        <%= f.label :service_number, :class=>'col-sm-2 control-label' %>
        <div class="col-sm-3">
          <%= f.text_field :service_number, :class => "form-control", :placeholder => "Service Number" %>
        </div>
      </div>
    
      <div class="form-group">
        <%= f.label :operator, :class=>'col-sm-2 control-label' %>
        <div class="col-sm-4">
          <%= f.text_field :operator, :class => "form-control", :placeholder => "Operator" %>
        </div>
      </div>
    
      <div class="form-group">
        <%= f.label :monday, :class=>'col-sm-2 control-label' %>
        <div class="col-sm-2">
          <%= f.check_box :monday, :class => "form-control, pull-left", :placeholder => "Monday" %>
        </div>
      </div>
    
      <div class="form-group">
        <%= f.label :tuesday, :class=>'col-sm-2 control-label' %>
        <div class="col-sm-2">
          <%= f.check_box :tuesday, :class => "form-control, pull-left", :placeholder => "Tuesday" %>
        </div>
      </div>
    
      <div class="form-group">
        <%= f.label :wednesday, :class=>'col-sm-2 control-label' %>
        <div class="col-sm-2">
          <%= f.check_box :wednesday, :class => "form-control, pull-left", :placeholder => "Wednesday" %>
        </div>
      </div>
    
      <div class="form-group">
        <%= f.label :thursday, :class=>'col-sm-2 control-label' %>
        <div class="col-sm-2">
          <%= f.check_box :thursday, :class => "form-control, pull-left", :placeholder => "thursday" %>
        </div>
      </div>
    
      <div class="form-group">
        <%= f.label :friday, :class=>'col-sm-2 control-label' %>
        <div class="col-sm-2">
          <%= f.check_box :friday, :class => "form-control, pull-left", :placeholder => "Friday" %>
        </div>
      </div>
    
      <div class="form-group">
        <%= f.label :saturday, :class=>'col-sm-2 control-label' %>
        <div class="col-sm-2">
          <%= f.check_box :saturday, :class => "form-control, pull-left", :placeholder => "Saturday" %>
        </div>
      </div>
    
      <div class="form-group">
        <%= f.label :sunday, :class=>'col-sm-2 control-label' %>
        <div class="col-sm-2">
          <%= f.check_box :sunday, :class => "form-control, pull-left", :placeholder => "Sunday" %>
        </div>
      </div>
    
      <div class="form-group">
        <%= f.label :bank_holiday, :class=>'col-sm-2 control-label' %>
        <div class="col-sm-2">
          <%= f.check_box :bank_holiday, :class => "form-control, pull-left", :placeholder => "Bank Holiday" %>
        </div>
      </div>
    
    <h3>Stops</h3>
    
      <!-- Adds the Service_Places associations via partial (sort applied in model) -->
      <div>
        <%= f.fields_for :service_places do |service_places| %>
          <%= render 'service_place_fields', :f => service_places %>
        <% end %>
        <div class="links">
          <%= link_to_add_association 'add service places', f, :service_places, :class => "btn btn-default" %>
        </div>
      </div>
    
    
    <h3>Websites</h3>
    
      <!-- Adds the website associations via a partial -->
      <div>
        <%= f.fields_for :websites do |website| %>
          <%= render "website_fields", :f => website %>
        <%end %>
        <div class="links">
          <%= link_to_add_association 'add websites', f, :websites, :class => "btn btn-default" %>
        </div>
      </div>
    
    <h3>Timetables</h3>
    
      <!-- Adds the timetables associations via a partial -->
      <div>
        <%=f.fields_for :timetables do |timetable| %>
          <%= render "timetable_fields", :f => timetable %>
        <%end %>
        <div class="links">
          <%= link_to_add_association 'add timetables', f, :timetables, :class => "btn btn-default" %>
        </div>
      </div>
    
      <br>
    
      <%= f.submit 'Save', :class => 'btn btn-primary' %>
      <%= link_to 'Cancel', services_path, :class => "btn btn-default", :role =>"button" %>
    
    <% end %>
    

    4) 将部分修改为如下所示。它需要 class="nested-fields" 否则它将无法工作。

    <div class="nested-fields">
    
      <%= f.label :website, :class=>'col-sm-2 control-label' %>
    
      <div class="col-sm-4">
        <%= f.text_field :url, :class => "form-control", :placeholder => "Service Provider Website" %>
      </div>
    
      <div>
        <%= link_to_remove_association "remove website", f, :class => "btn btn-default" %>
      </div>
    
    </div>
    

    我没有包含我所有的部分,但它们遵循相同的格式。

    【讨论】:

      猜你喜欢
      • 2015-08-05
      • 2013-10-14
      • 2013-01-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多