【问题标题】:Rails: How to add an object through a bootstrap modal?Rails:如何通过引导模式添加对象?
【发布时间】:2016-03-08 01:26:51
【问题描述】:

我是 Rails 新手,我有这个 Web 应用程序,允许用户使用 Rails 4 创建新的打印作业

app/models/job.erb

class Job < ActiveRecord::Base
  belongs_to :job_type
end

app/models/job_type.erb

class Job < ActiveRecord::Base
  has_many :jobs
end

在新工作创建表单中,用户必须从列表中为他的新工作选择工作类型,我设法得到它并抛出以下代码。

app/views/jobs/_form.html.erb

<div class="row">
  <div class="col-md-6 col-md-offset-3">
    <%= form_for(@job) do |f| %>

      .....

      <div class="field">
        <%= f.label :job_type_id %><br>
        <%= f.collection_select :job_type_id, JobType.all,:id,:name %>
      </div>
      <!-- Modal -->
      <div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
        <div class="modal-dialog" role="document">
          <div class="modal-content">
            <div class="modal-header">
              <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
              <h4 class="modal-title" id="myModalLabel">Modal title</h4>
            </div>
            <div class="modal-body">
              ...
            </div>
            <div class="modal-footer">

            </div>
          </div>
        </div>
      </div>

    <% end %>

  </div>
</div>

通过一些咖啡脚本,我设法在 Job Types 菜单中添加了“Other”选项,如果它不存在,它会触发模态 #myModal 以将新的 JobType 添加到数据库中......

app/assets/javascript/jobs.coffee

$('#job_job_type_id').append("<option>Other</option>")
$('#job_job_type_id').change ->
  jobType = $('#job_job_type_id :selected').text()
  if jobType == "Other"
    $('#myModal').modal('show')
  else
    $('#myModal').modal('hide')

代码运行良好并触发引导模式。但就是这样,我不知道接下来该怎么办?

我尝试了很多代码并查看了很多问题,但我没有让这个模式能够添加新的 JobType 并更新列表,我认为需要对控制器进行一些修改和花哨的 AJAX 代码超出了我的知识范围......

我有一些问题

1)我应该在模态代码中输入什么才能将新的JobType添加到数据库中,然后返回新的Job创建表单并选择新创建的JobType

2) 哪些控制器需要修改?如何?需要什么 AJAX 代码?

3) 如何重构 Job _form ?我可以将模态代码放在新的 Partial 中吗?如果是,如何实现?

希望您能帮助我,我这几天一直在努力解决这个问题。 谢谢

【问题讨论】:

    标签: javascript jquery ruby-on-rails ajax twitter-bootstrap


    【解决方案1】:

    好的,首先回答你的问题:

    1)我应该在模态代码中放入什么才能将新的JobType添加到数据库中,然后返回新的Job创建表单并选择新创建的JobType

    您的模态将添加一个表单,本质上是您可能在app/views/job_types/_form.html.erb 下拥有的表单,但它将有一个按钮,该按钮将通过 AJAX 提交而不是表单提交。

    2) 哪些控制器需要修改?如何?需要什么 AJAX 代码?

    您需要向job_types_controller 添加一个可以处理上述AJAX 调用的方法。它需要:

    • 保存新的作业类型
    • 向调用者返回成功状态和新创建的实体。

    3) 如何重构 Job _form ?我可以将模态代码放在新的 Partial 中吗?如果是,如何实现?

    您需要有一个方法,该方法在调用时会向列表框添加一个新选项。您可以将模态代码放在部分中,也可以不放,由您决定;该决定对所有这些的功能没有任何影响。

    那么,你需要在这里做什么:

    服务器(控制器):

    1) 在config/routes.rb 中创建一个可以处理 AJAX 调用的方法。

    resources :job_types do
      post :append, on: :collection
    end
    

    这会添加一个自定义资源路由。因为我们是这样添加的,所以我们自动得到了URLHelper函数append_job_types_path

    2) 在controllers/job_types_controller.rb 中实现此方法以保存新的 JobType 并将其(最重要的是它的 ID)返回给调用者。

    def append
      job_type = JobType.new
      job_type.name = params[:job_type_name]
      if job_type.save
        render :status => 201, :json => { id: job_type.id, name: job_type.name }
      else
        err_msg = job_type.errors.empty?? 'Save failed' : job_type.errors.full_messages.join('; ')
        render :status => 400, :json => { message: err_msg }
      end
    end
    

    如果保存顺利,新实体的 ID 和名称将以 JSON 格式返回给调用者。如果没有,我们将返回错误和任何验证消息。

    现在,我们准备好使用这些方法了……

    客户(查看):

    1) 创建一个可以启动模态的按钮

    你已经这样做了!

    2) 向模态框添加可以提交作业类型的表单

    <%= form_tag(append_job_types_path) do %>
      Enter Job Name:<br/>
      <input type="text" name="new_job_type_name" id="new_job_type_name" />
      <br/>
      <input type="button" value="Submit" id="append_job_type_submit" />
    <% end %>
    

    实际上,它不需要在表单中,因为我们通过 AJAX 提交,但您可能会通过使用表单获得一些样式帮助。但是,此处的 id 属性对于后续步骤很重要。请注意,我在这里使用form_tag 而不是form_for。那是因为我不会在表单上附加任何东西(或提交它)。

    3) 在提交时(呃,在按钮点击时),将用户输入的名称发送到新的 AJAX 方法

    在这里,我们将使用不显眼的 javascript 将侦听器方法挂钩到提交按钮。你可以把这段代码放在视图的底部,也可以把它移到coffeescript:

    javascript:
      $("#append_job_type_submit").click(function() {
        var name = $("#new_job_type_name").val();
        //TODO: validation on the name, ensure it's not blank, etc
        $.ajax({
          url: '/job_types/append',
          method: 'POST',
          data: {job_type_name: name},
          success: function(data) {
            //TODO: Handle success
          },
          error: function(err) {
            //TODO: Handle error
          }
        });
      });
    

    在这里,我们向服务器方法发送 AJAX 调用,传递用户输入的名称。请注意,我为您在提交之前进行的简单验证留出了空间

    4) 响应后,将新选项附加到列表框。

    这将继续上面的功能:

    javascript:
      $("#append_job_type_submit").click(function() {
        var name = $("#new_job_type_name").val();
        //TODO: validation on the name, ensure it's not blank, etc
        $.ajax({
          url: '/job_types/append',
          method: 'POST',
          data: {job_type_name: name},
          success: function(data) {
            var sel = $("#job_type_id");
            sel.append('<option value="' + data.id + '">' + data.name + '</option>');
            $("#myModal").modal('hide');
            alert("New job type " + data.name + " created.");
            //TODO: probably be nice to auto-select this option; I'll leave that exercise to the alert reader
          },
          error: function(err) {
            alert(err.responseJSON.message);
          }
        });
      });
    

    现在,用户可以从选择框中选择选项,并且新选项已保存到数据库中(无论他们是否创建新作业)。

    免责声明:我没有测试过这段代码,甚至没有检查它是否可以编译。但它应该可以工作,无论如何,这是你想要遵循的模式,所以如果没有别的,至少你现在有了方向。

    现在,说了这么多,我仍然会推荐我建议的不使用 AJAX 的另一种方法,因为它省去了大部分步骤,但这只是我个人的想法。随意接受任何有助于您以您想要的方式完成任务的答案。

    注意:除了 aldrien 的回答之外,我还决定发布此内容,因为我发现有一些差异足以证明这一点:

    • 成功和失败响应应返回成功/失败状态代码。让它总是返回成功有点误导。我认为状态码是任何 REST 设计的重要组成部分,即使是这么小的东西。
    • 我只推荐使用match 路由作为最后的手段。资源丰富的路线更干净,更清晰,它们也为您提供更多开箱即用的功能。另外,我喜欢/job_types 下的路线,而不仅仅是在根部浮动。
    • 还有更多的标准,应该是 POST 请求而不是 GET;毕竟,我们正在创建一个新实体。
    • 错误:append 方法应该为选项值产生data.id,而不是data.title(根据原始代码应该是data.name)。否则,该值将不是 ID,尝试保存将失败。

    【讨论】:

    • !,非常感谢,你的回答很清楚,你一步一步地把答案扔给我,解释每一件事。它完美地发挥了我想要的效果。除了一件事:&lt;%= form_tag(append_job_type_path) do %&gt;它一直给我一个错误undefined local variable or method 'append_job_type_path',但是当我把它改成:&lt;%= form_tag(append_job_type_path) do %&gt;它工作得很好,“我很尴尬地说”一个我不完全理解的解决方案.但无论如何谢谢你
    • 谢谢,我在后代的答案中更新了这一点。如果您想对任何事情进行任何澄清,请随时发表评论和提问!
    【解决方案2】:

    如果我可以提供替代方案...

    您的方法将需要一些高级编码方法,如果您对 AJAX 感到不舒服,这可能是一条艰难的道路。

    作为替代方案,我建议不要显示模式,而是在现有表单中显示/隐藏一个文本框以容纳其他名称:

    <input type="text" name="other_job_type" />
    

    然后,当您提交表单时,您知道它将转到 JobController#create(或 #update 用于现有作业)。在那里,你可以得到这个字段:

    def create
      save_successful = false
      @job = Job.new params[:job]
      other_job_type = params[:other_job_type]
      if other_job_type
        new_job_type = JobType.new
        new_job_type.name = other_job_type
        save_successful = new_job_type.save
        @job.job_type = new_job_type
      end
      if save_successful && @job.save
        # redirect to success page
      else
        # render new/edit with error messages
      end
    end
    

    通过这种方式,您只有在用户实际提交作业表单时才创建新的作业类型,这很好。

    如果您想对 JobType 强制执行验证(例如唯一名称),请使用 save_successful 模式。如果用户尝试使用现有作业类型进行保存,则保存将失败。您也可以只为他们选择现有的,但如果您选择这样做,我会留给您。

    同样,所有这些都遵循您现在使用的相同模式,只是一个新的文本字段和控制器中的更多处理。

    如果您愿意,我可以详细说明您的问题的答案,即使用 AJAX 方法来使其工作,因为这些步骤虽然更长,但具有相当的确定性,但对于您的用例而言,这可能是矫枉过正。也就是说,我讨厌不实际回答给定的问题,不管我个人对这种方法的看法如何,所以请告诉我。

    【讨论】:

    • 真心希望你能用AJAX回答
    【解决方案3】:

    首先,在你的modal中设置触发提交功能的按钮或表单。

    其次,设置/检查要用于AJAX方法的Routes/URL。

    以及设置控制器功能以保存新的工作类型。

    控制器中的示例:

    def create
      job_type = JobType.new
      job_type.title = params[:job_type]
      if job_type.save
       render :json => job_type
      else
       render :json => "some error here."
      end
    end
    

    最后,制作用于发送数据的AJAX函数。

    $("#add_new_job_type").click(function(){
        $.ajax({
            url: '/add_new_job_type',
            type: 'GET', // or POST
            data: {job_type: $("#field_contains_new_value").val()}
        }).done(function(data){
            // Do some validation for checking error response (like if statement)
            // Append the new data (job type to select option tag)
            new_job_type = "<option value="'+data.title+'">" + data.title + "</option>"
            $('#job_job_type_id').append(new_job_type);
            ....
            $("#myModal").modal('hide');
        });
    
    });
    

    注意:AJAX url中使用的add_new_job_type是自定义路由。

    config/routes.rb(自定义路由)中:

    match 'add_new_job_type' => 'job_types#add_new_job_type', :via => :post #or :get
    

    在您的 JobTypes Controller 中,您必须拥有:

    def add_new_job_type
      job_type = JobType.new
      job_type.title = params[:job_type]
      if job_type.save
       render :json => job_type
      else
       render :json => "some error here."
      end
    end
    

    根据需要修改代码。

    【讨论】:

    • 你的意思是:设置/检查要用于 AJAX 方法的路由/URL。??你能解释一下你的答案吗?
    • 嗨@MoatazZaitoun,如果你在config/routes.rb中有资源:job_types,创建方法的默认路径是“/job_types/new”。我会为你定制路线。请检查更新的代码。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-05
    • 1970-01-01
    • 1970-01-01
    • 2016-03-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多