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

时间:2013-10-18 09:50:10

标签: ruby-on-rails ajax ruby-on-rails-3.2 devise

我已使用this answer的多个模型实现设计注册。这样就可以从路径获得params 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

4 个答案:

答案 0 :(得分:0)

你在这里混淆了几件事。

  • 您的路线定义了默认

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

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

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

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

答案 1 :(得分:0)

在控制器文件中返回

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

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

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

答案 2 :(得分:0)

我认为你遇到了很多问题,但最重要的是:

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函数将变量发送到partial。

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

答案 3 :(得分:0)

当您尝试渲染部分时,您必须添加=:

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