Rails: Communication between View & Controller question (theory, practice, answer, anything!)

后端 未结 2 1846
伪装坚强ぢ
伪装坚强ぢ 2021-01-07 00:04

I need a straightforward, easy to understand answer on this one. I\'m building a user control panel for administrative users to create/edit/delete users of the system throu

相关标签:
2条回答
  • 2021-01-07 00:51

    As you haven't said otherwise and it looks right, I'm going to assume that clicking an "Add new #{type}" link works as it should. It also looks like you can successfully create Users. So I'm going to move on to address the problem with unsuccessful saves.

    The controller renders the new template on a failed create action, but doesn't define the same instance variables as the new action. So we need to define them again. I've added them to the create method in the failure block. I've also made a subtle change to new that will make your form cleaner.

    app/controllers/users_controller.rb:

     def new
       @user = User.new
       @type = params[:type]
       @roles = get_all_roles.reject{|r| r.name == @type}
       @cancel = users_path
     end
    
     def create
        @user = User.new(params[:user])
        @assigned_roles = params[:user][:assigned_roles].select{|k,v| ! v.nil?}
        if @user.save
          update_user_roles(@user,roles)
          if current_user.is_admin_or_root?
            flash[:message] = "User \"#{@user.username}\" created."
            redirect_to users_path
          else
            flash[:message] = "Congrats! You’re now registered, #{@user.username}!"
            redirect_to app_path
          end
        else
          @type = params[:type]
          @roles = get_all_roles.reject{|r| r.name == @type}
          @cancel = users_path
          render :action => 'new'
        end
      end
    

    However we're only part way done, with just that change in the controller we'll list the roles properly, but the type won't be assigned. So we have to modify the form_for call to pass the type parameter to the create call. We also need to change the form so it keeps roles selected after the failure. By removing type from @roles in the controller, the specified type isn't listed in the form. It's automatically applied, as a hidden_field. I've also taken the liberty of restructuring the section around the roles checkboxes, so that the %li containing the roles section only appears if there are roles to show.

    app/views/users/new.html.haml

    - form_for @user, :url => {:action => :create, :type => @type) do |f| 
      %ol{ :class => 'form' }
        %li
          = f.label :username
          = f.text_field :username
        %li
          = f.label :email
          = f.text_field :email
        %li
          = f.label :password
          = f.password_field :password
        %li
          = f.label :password_confirmation
          = f.password_field :password_confirmation
    
        - if @roles
          %li
            - @assigned_roles ||= []
            - @roles.each do |role| 
              = label_tag role
              = check_box_tag 'user[assigned_roles][]', role, @assigned_roles.include?(role)
    
        = hidden_field_tag 'user[assigned_roles][]', @type
        %li{ :class => 'submit' }
          = f.submit 'Register'
          = link_to 'cancel', @cancel
    

    With these quick changes things should work the way you expect.

    0 讨论(0)
  • 2021-01-07 00:53

    Basically everything EmFi suggested, plus a few tweaks:

    app/views/users/new.html.haml

    - if @roles
      %li
        - @assigned_roles ||= []
        - @roles.each do |role| 
          = label_tag role
          = check_box_tag 'user[assigned_roles][]', role, (@roles & @assigned_roles ).include?(role)
    

    removed:

    = hidden_field_tag 'user[assigned_roles][]', @type
    

    (The parameter I wanted to carry over is supplied by:

    - form_for @user, :url => {:action => :create, :type => @type) do |f| ...
    

    Since i only used it for presentation, I don't need to store it in the form.)

    app/controllers/users_controller.rb:

    def new
       @user = User.new
       @type = params[:type]
       @roles = get_all_roles # don't need to reject anything here either, since the :type is no longer part of this array
       @cancel = users_path
    end
    
    def create
       @user = User.new(params[:user])
       @assigned_roles = params[:user][:assigned_roles] # already has the roles I need; unchecked checkboxes are not passed into this; only checked ones
        if @user.save
          update_user_roles(@user,@assigned_roles)
          if current_user.is_admin_or_root?
            flash[:message] = "User \"#{@user.username}\" created."
            redirect_to users_path
          else
            flash[:message] = "Congrats! You’re now registered, #{@user.username}!"
            redirect_to app_path
          end
        else
          @type = params[:type]
          @roles = get_all_roles # don't need to reject anything, since the :type is no longer part of this array
          @cancel = users_path
          render :action => 'new'
        end
      end
    

    Thanks, EmFi, for setting me straight on this! The @assigned_roles logic and

    - form_for @user, :url => {:action => :create, :type => @type) do |f| ...
    

    were the keys to this puzzle I was searching for!

    0 讨论(0)
提交回复
热议问题