在Rails 4中为Ajaxified表单在form_for中创建一个新对象

时间:2014-01-23 23:16:54

标签: jquery ruby-on-rails ajax forms ruby-on-rails-4

使用Rails 4,制作一个简单的单页应用程序。

我的root设置为索引页面,我在其中显示了一系列非常短的帖子。另一方面,我有一个部分表格,所以有人可以轻松提交新帖子。

我决定对表单提交进行Ajax化,以便将新帖子附加到列表顶部而不重新加载页面并清除表单。

关于我的表单的一个注释 - 每个帖子都属于一个公司,所以我在表单中有<%= fields_for Company.new do |company|... %>。在创建操作中,我在公司上调用first_or_create,因此如果公司不存在,则会在提交时创建并与新帖相关。

最终,我必须在我的表单中显式创建新对象,而不是将本地传递给我的部分并使用表单中的裸字。我试图理解为什么会这样,以及如何最终实现更加接受的方法。

在下面实现我的终极解决方案之前,我试图让它像这样工作:在posts控制器中的我的索引操作中,不仅声明@post = Post.new@company = Company.new而且声明{{1 }和@new_company = Company.new。然后在我的create.js.erb文件中,重新呈现表单时,将@new_post = Post.new@new_company作为@new_form等本地人传递,然后在表单中,使用简单的单词{{1 }}。这似乎是更标准的方式,但在我放弃实例变量和本地并且只是在表单中显式创建新对象之前,我无法使其工作。

posts_controller.rb:

$(".js-ref-form").html("<%= escape_javascript(render partial: 'form', locals: { post: @new_post, company: @new_company ) %>");

视图/帖/ create.js.erb:

<%= form_for post .... %>

视图/帖/ _form.html.erb

class PostsController < ApplicationController

  respond_to :html, :js

  def index
    @company = Company.new
    @post = Post.new
    @posts = Post.all
  end

  def create
    @company = Company.where(name: post_params[:company]).first_or_create
    @post = @company.posts.build(company_id: @company.id, details: post_params[:details], link: post_params[:link],
                                         expiration: post_params[:expiration], code: post_params[:code], limit: post_params[:limit])
    if @post.save
      flash[:success] = "Post submitted!"
    else
      flash[:danger] = "Problem submitting post. Please try again."
    end

    respond_with(@post) do |f|
      f.html { redirect_to root_path }
    end
  end

视图/帖/ index.html.erb:

<% if @post.valid? %>
  $(".js-post").prepend("<%= escape_javascript(render(@post)) %>");
  $(".js-post-form").html("<%= escape_javascript(render partial: 'form') %>");
<% else %>
  $(".js-post-form").replaceWith("<%= escape_javascript(render partial: 'form') %>");
<% end %>

的routes.rb

# writing form_for @post doesn't work with this solution, and if I pass
# in locals to the partials, i.e. render partial: 'form', locals: { post: @post }
# and using form_for post didn't work either
<%= form_for Post.new, html: { class: 'form-horizontal' }, remote: true do |f| %>
  <h2>Add Post</h2>
  <%= render 'shared/error_messages' %>

  <%= f.label :company, "Company" %>
  # Same problem as the comment above
  <%= fields_for Company.new do |company| %>
      <%= f.text_field :company, class: 'form-control' %>
  <% end %>

  <br>

  <%= f.label :details, "What's new?" %> <span class="detail-counter pull-right"></span>
  <%= f.text_area :details, class: 'form-control' %>

  <br>

  <%= f.submit "Submit", class: 'btn btn-lg btn-block btn-primary' %>
<% end %>

<script type="text/javascript">
  var elem = $(".detail-counter");
  $("#post_details").limiter(140, elem);
</script>

模型:

post.rb

<div class="row">
  <div class="col-md-4 post-form">
    <div class="js-post-form">
      <%= render partial: 'form' %>
    </div>
  </div>
  <div class="col-md-8">
    <div class="post-list js-posts">
      <% if @posts.any? %>
        <%= render @posts %>
      <% else %>
        No posts yet!
      <% end %>
    </div>
  </div>
</div>

company.rb

CompanyPosts::Application.routes.draw do
  resources :posts

  root to: 'posts#index'
end

1 个答案:

答案 0 :(得分:1)

好的,想通了。为了能够将局部传递给我的表单部分并在表单中使用单词,我必须为{em> new 帖子和 new 公司对象创建实例变量{ {1}}动作,而不是我以前做的索引动作。

然后在我的index.html.erb文件中,我可以执行posts#create并在create.js.erb中执行:

<%= render partial: 'form', locals: { post: @post, company: @company } %>

另外,我必须在posts控制器中定义两个参数,因为我实际上是在同时提交两个表单。我修改了<% if @post.valid? %> $(".js-posts").prepend("<%= escape_javascript(render(@post)) %>"); $(".js-post-form").html("<%= escape_javascript(render partial: 'form', locals: { post: @new_post, company: @new_company }) %>"); <% else %> $(".js-post-form").html("<%= escape_javascript(render partial: 'form', locals: { post: @post, company: @company }) %>"); <% end %> 标记,以我希望更合适的方式工作:

fields_for

现在我不必在表单中显式创建对象。虽然任何进一步的改进建议仍然受到赞赏。