Rails: связь между вопросом View & Controller (теория, практика, ответ, что угодно!) - PullRequest
2 голосов
/ 12 октября 2009

Мне нужен простой и понятный ответ на этот вопрос. Я создаю пользовательскую панель управления для административных пользователей, чтобы создавать / редактировать / удалять пользователей системы через веб-интерфейс (поверх Authlogic и rails-authorization).

В представлении /users я перечисляю всех пользователей, а затем делю их по ролям.

В конце каждого списка ролей у меня есть ссылка «Добавить новый {тип пользователя}».

Теперь я ХОЧУ:

  • Когда нажата ссылка "Добавить нового {пользовательский тип}", вы попадаете в форму «Новый пользователь», адаптированную к этому {пользовательскому типу}, причем все используют действие app/controllers/users_controller.rb new.
  • Если форма заполнена неправильно, она отображает встроенные формы проверки, но ПОМНИТ, какой тип пользователя вы пытались создать.

Я пытался сделать это, передав параметры через ссылку и затем получив доступ к ним через params[], но безуспешно. Мои назначения ролей НЕ являются частью модели User, что, как я знаю, усложняет ситуацию.

В настоящее время, когда форма отправляется неудачно, она закрывает :type и отображает ванильную (не настроенную) форму.

Кто-нибудь может сделать из этого головы или хвосты? Дай мне направление? Я полагаю, что я воспроизвел соответствующие фрагменты моего кода ниже (любой, кто хочет ответить, чтобы просто очистить код, тоже был бы благодарен!)


Просмотры

app/views/users/index.html.haml

.tabs
  %ul
    %li
      %a{ :href => '#all' }
        %span All Users
    %li
      %a{ :href => '#employees' }
        %span Employees
    %li
      %a{ :href => '#clients' }
        %span Clients
    %li
      %a{ :href => '#prospects' }
        %span Prospects
    %li
      %a{ :href => '#affiliates' }
        %span Affiliates
  #all
    = render :partial => 'users', :locals => { :users => @users, :type => 'All' }
  #employees
    = render :partial => 'users', :locals => { :users => @employees, :type => 'Employee' }
  #clients
    = render :partial => 'users', :locals => { :users => @clients, :type => 'Client' }
  #prospects
    = render :partial => 'users', :locals => { :users => @prospects, :type => 'Prospect' }
  #affiliates
    = render :partial => 'users', :locals => { :users => @affiliates, :type => 'Affiliate' }

app/views/users/_users.html.haml

- if users.empty?
  %p{ :class => 'notice' }= "No #{type.pluralize} Found"
  %p{ :class => 'controls' }= link_to "Add a new #{type}", new_user_path(:type => type)
- else
  %table
    %tr
      %th Username
      %th Email
      %th Roles
    - users.each do |user| 
      %tr{ :class => cycle('even','odd') }
        %td=h user.username
        %td=h user.email
        %td= user.list_roles.collect{ |role| "<span class=\"role #{role}\">#{role}</span>" }.sort
        %td= link_to 'Edit', edit_user_path(user, :type => type)
        - if user.is_root?
          %td Can&rsquo;t delete root
        - else
          %td= link_to 'Delete', user, :confirm => 'Are you sure you wish to delete this user? This is irreversible!', :method => :delete
  %p{ :class => 'controls' }= link_to "Add a new #{type}", new_user_path(:type => type)

app/views/users/new.html.haml

- title 'New User'
- form_for @user do |f| 
  = f.error_messages
  %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
    %li
      - if @roles
        - @roles.each do |role| 
          = label_tag role
          = check_box_tag 'user[assigned_roles][]', role
      - else
        = hidden_field_tag 'user[assigned_roles][]', @type
    %li{ :class => 'submit' }
      = f.submit 'Register'
      = link_to 'cancel', @cancel

Контроллеры

app/controllers/users_controller.rb

class UsersController < ApplicationController
  permit 'admin', :only => 'index', :get_user_method => 'current_user'

  def index
    @users = User.all
    @employees = find_all_users_with_roles(['root','admin'])
    @clients = find_all_users_with_roles(['client'])
    @prospects = find_all_users_with_roles(['prospect'])
    @affiliates = find_all_users_with_roles(['affiliate'])
  end

  def new
    @user = User.new
    @roles = get_all_roles
    @type = params[:type]
    @cancel = users_path
  end

  def create
    @user = User.new(params[:user])
    type = params[:type]
    roles = params[:user][:assigned_roles]
    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&rsquo;re now registered, #{@user.username}!"
        redirect_to app_path
      end
    else
      params[:type] = type
      render :action => 'new'
    end
  end

  ...

  private

  def get_all_roles
    roles = []
    Role.find(:all).each do |role|
      roles << role.name
    end
    roles
  end

  # code cleanup (using '.roles' ?)
  def find_all_users_with_roles(find_roles)
    users = []
    find_roles.each do |find_role|
      user_role_id = Role.find_by_name(find_role).id unless Role.find_by_name(find_role).nil?
      RolesUser.find_all_by_role_id(user_role_id).each do |role|
        users << User.find(role.user_id) unless users.include?(User.find(role.user_id))
      end
    end
    users
  end

  # cleanup - virtual attribute?? couldn't get that to work last time
  def update_user_roles(user,roles)
    # add new roles
    roles.each do |role|
      user.has_role role unless user.has_role? role
    end

    # delete roles
    (user.list_roles - roles).each do |role|
      user.has_no_role role
    end
  end

end

Ответы [ 2 ]

1 голос
/ 12 октября 2009

Поскольку вы не сказали иначе, и это выглядит правильно, я собираюсь предположить, что нажатие на ссылку "Добавить новый # {тип}" работает как должно. Похоже, что вы можете успешно создавать пользователей. Итак, я собираюсь перейти к решению проблемы с неудачным сохранением.

Контроллер отображает новый шаблон при неудачном действии create, но не определяет те же переменные экземпляра, что и новое действие. Таким образом, мы должны определить их снова. Я добавил их в метод создания в блоке сбоя . Я также внес небольшое изменение в новое, что сделает вашу форму чище.

приложение / контроллеры / 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&rsquo;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

Тем не менее, мы только частично проделали это, только с этим изменением в контроллере мы правильно перечислим роли, но тип не будет назначен. Поэтому мы должны изменить вызов form_for, чтобы передать параметр type в вызов create. Нам также нужно изменить форму, чтобы она оставляла роли выбранными после сбоя. При удалении типа из @roles в контроллере указанный тип не отображается в форме. Он автоматически применяется как скрытое поле. Я также взял на себя смелость реструктурировать раздел вокруг флажков ролей, так что% li, содержащий раздел ролей, появляется только при наличии ролей, которые нужно показать.

приложение / просмотров / пользователей / 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

С этими быстрыми изменениями все должно работать так, как вы ожидаете.

0 голосов
/ 12 октября 2009

В основном все, что предлагает EmFi, плюс несколько настроек:

приложение / просмотров / пользователей / 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)

Удалено:

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

(параметр, который я хотел перенести, предоставляется:

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

Поскольку я использовал его только для презентации, мне не нужно хранить его в форме.)

приложение / контроллеры / 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&rsquo;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

Спасибо, EmFi, за то, что объяснил мне это! @assigned_roles логика и

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

были ключи к этой головоломке, которую я искал!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...