Rails 设计多态 - 使用 Ajax 获取部分渲染

Posted

技术标签:

【中文标题】Rails 设计多态 - 使用 Ajax 获取部分渲染【英文标题】:Rails Devise Polymorphic - Get Render Partial Using Ajax 【发布时间】:2013-10-27 02:49:50 【问题描述】:

我已经使用来自this answer 的多个模型实现了设计注册。这个 tuts 从路径中获取参数 user_type。 我想用选择user_type 来改变它。因此,当我在 select_tag 上选择一个值时,将获得一个参数 user_type。

我有一些代码看起来像:

routes.rb

   namespace :cp do
    devise_scope :user do
     match '/add_user' => 'registrations#new'
     match '/select/admin' => 'registrations#selectuser', :user =>  :usertype => 'admin' 
     match '/select/player' => 'registrations#selectuser', :user =>  :usertype => 'player' 
    end
   end

registrations_controller.rb

  def selectuser

    respond_to do |format|
      format.js
    end
  end

new.html.erb

<h2>Add User</h2>

<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f| %>
  <%= devise_error_messages! %>

  <div><%= f.label :username, "Username" %><br />
  <%= f.text_field :username %></div>

  <div><%= f.label :email, "Email" %><br />
  <%= f.email_field :email %></div>

  <div><%= f.label :password, "Password" %><br />

  <%= f.password_field :password %></div>

  <div><%= f.label :password_confirmation, "Password Confirmation" %><br />
  <%= f.password_field :password_confirmation %></div>

  <div><%= f.label :usertype, "Select User Type" %><br />
  <%= f.select(:usertype, options_for_select([['-- User Type --', nil], ['Admin', 'admin'], ['Player', 'player']], selected: '-- User Type --' )) %>
   </div> 

   <div id="selectuser">
   </div>
  <% end %>

  <div><%= f.submit "Submit" %></div>
<% end %>

<script type="text/javascript">
$("#user_usertype").on('change', function() 
   var s = $(this).val();
       $.ajax(
              type: 'GET',
              url: 'http://localhost:3000/cp/select/' + s,
              dataType: "html"
              );
);
</script>

selectuser.js.erb

<%  params[:user][:usertype] ||= 'admin'

    if ["admin", "player"].include? params[:user][:usertype].downcase
      child_class_name = params[:user][:usertype].downcase.camelize
      usertype = params[:user][:usertype].downcase
    else
      child_class_name = "Admin"
      usertype = "admin"
    end


   nesteds = fields_for child_class_name.constantize.new do |rf|
     render :partial => child_class_name.underscore + '_fields', :locals => :f => rf
   end
%>
$("#selectuser").append("<%= j nesteds %>");

当我选择管理员值时,记录:

Started GET "/cp/select/admin" for 127.0.0.1 at 2013-10-21 17:00:04 +0700
Processing by Cp::RegistrationsController#selectuser as HTML
  Parameters: "user"=>"usertype"=>"admin"
  Rendered cp/registrations/_admin_fields.html.erb (4.0ms)
  Rendered cp/registrations/selectuser.js.erb (7.0ms)
Completed 200 OK in 22ms (Views: 22.0ms | ActiveRecord: 0.0ms)

但是_admin_fields.html.erb没有出现在#selectuser

【问题讨论】:

如果性别都是男是女,就不用碰服务器,直接在模板里写代码就行了。 @BillyChan:我不知道你是什么意思? 【参考方案1】:

你在这里混淆了几件事。

您的路线定义了一个默认值

如果您需要从下拉列表中选择,则无需在此处定义默认值。它只会使事情复杂化

 match '/add_user' => 'registrations#new', :user =>  :usertype => nil 
 match '/select/admin' => 'registrations#selectuser', :user =>  :usertype => 'admin' 
 match '/select/player' => 'registrations#selectuser', :user =>  :usertype => 'player' 
您没有使用正确的表单助手

如果您想获得params[:user][:usertype] 的输出,您需要使用f.select。如果您的模型没有这样的属性,您可以将其作为attr_accessor 添加到您的模型中。

<%= label :usertype, "Select User Type" %><br />
<%= select_tag(:usertype, options_for_select([['-- User Type --', nil], ['Admin', 'admin'], ['Player', 'player']], selected: '-- User Type --' )) %>

【讨论】:

感谢 phoet,我已经尝试过了,但没有任何变化,仍然无法正常工作。 我在用户模型上有属性 usertype【参考方案2】:

我认为你有很多问题,但最大的问题在这里:

nesteds = fields_for child_class_name.constantize.new do |rf|
render :partial => child_class_name.underscore + '_fields', :locals => :f => rf

您正在尝试在变量中呈现“fields_for”帮助程序。问题是这个变量被视为一个字符串,这意味着它不再是一个助手,并且不会导致 fields_for 元素出现。这进一步说明了我们不知道您系统的哪个部分无法正常工作--

您的 Ajax 触发了吗?您的路由是否正确发送请求?


就我个人而言,我会重新考虑你系统的这部分是如何工作的

我会从您呈现的 selectuser.js.erb 中删除逻辑,并将其放入控制器中,如下所示:

  def selectuser

  params[:user][:usertype] ||= 'admin'

    if ["admin", "player"].include? params[:user][:usertype].downcase
      child_class_name = params[:user][:usertype].downcase.camelize
      usertype = params[:user][:usertype].downcase
    else
      child_class_name = "Admin"
      usertype = "admin"
    end

    respond_to do |format|
      format.js  @render = child_class_name.underscore + '_fields', @rf = rf
    end
  end

然后在你的 JS 中,你就可以让 JS 做它的诞生:

$("#selectuser").html("<%=j render :partial => @render, locals: :f = @rf %>");

这意味着在你的 child_class_name_fields.html.erb 中,你需要有这样的东西:

<%= f.fields_for child_class_name.constantize.new do |rf| %>
    <%= rf.text_field :name %>
<% end %>

更新

我现在实际上正在为此苦苦挣扎,因为我会以完全不同的方式去做。我会将整个逻辑移动到控制器中,并且只通过 :render 函数将变量发送到部分。

我不喜欢你试图动态生成“f.fields_for”的想法——它应该已经设置好了,你只更改该块内的字段。这是一个被称为DRY (don't repeat yourself) 编程的原则。如果你愿意,我可以为你写我喜欢的版本,而不是尝试monkeypatch这个?

【讨论】:

感谢rich peck,我看到你的代码会出错,看到nesteds = fields_for child_class_name.constantize.new do |rf| wirhout end ? 嗯,我没看到!老实说,我会以不同的方式编写代码^_^ 但是当你尝试你的代码时,得到了像syntax error, unexpected $end, expecting keyword_end这样的错误。如果nesteds 不使用,则nesteds 将用于什么变量。 感谢 rick peck,但在您回答之前,我已经尝试过了。修复错误但仍然无法正常工作。 嗯好吧,让我看看有什么办法可以解决【参考方案3】:

在你的控制器文件中返回

render json: "fields" => render_to_string(partial: "admin_fields") 

当控制器返回 json 给你的 ajax 函数时,注入以下内容 -

$('#selectuser').html(fields)

【讨论】:

【参考方案4】:

当你试图渲染你的部分时,你必须添加一个 =:

<%=  params[:user][:usertype] ||= 'admin'
# ^ This makes whatever is being returned in this block to be added to the output

【讨论】:

以上是关于Rails 设计多态 - 使用 Ajax 获取部分渲染的主要内容,如果未能解决你的问题,请参考以下文章

Rails 4:部分渲染后无法使用javascript(使用ajax)

Rails - 在ajax之后使用部分更新引导弹出内容

使用 AJAX 刷新 Rails 部分时的未定义方法

AJAX 日历更新页面 rails 4

在 rails 3.2.2 中使用 $.ajax() 异步获取数据

Rails 跨域 ajax 获取请求 (CORS)