用于显示操作的Rails form_tag

时间:2015-06-27 12:53:23

标签: ruby-on-rails forms ruby-on-rails-3

我有一个Rails应用程序,我有一个部分,我想要一个下拉菜单,所以当选择一个用户时,它将对urls / timecards /:user_id执行get方法,这将是控制器的show方法传递给用户的id字段。我在form_tag的路径上遇到了困难,可以使用一些帮助。

以下是我在视图中的部分内容:

<%= form_tag timecard_path, :method => :get do %>
  <%= select_tag options_from_collection_for_select(User.employee.order("username ASC"), :id, :username) %>
  <%= submit_tag "View Employee", class: "btn btn-primary" %>
<% end %>

从rake路线我得到以下输出:

timecards GET    /timecards(.:format)                 timecards#index
                           POST   /timecards(.:format)                 timecards#create
              new_timecard GET    /timecards/new(.:format)             timecards#new
             edit_timecard GET    /timecards/:id/edit(.:format)        timecards#edit
                  timecard GET    /timecards/:id(.:format)             timecards#show
                           PUT    /timecards/:id(.:format)             timecards#update
                           DELETE /timecards/:id(.:format)             timecards#destroy

这是我的控制器:timecards_controller.rb

class TimecardsController < ApplicationController
before_filter :disallow_clients, :disallow_medics, :disallow_employee, :disallow_supervisor

  def index
    @clock_events = ClockEvent.includes(:user).search(params[:search])
    respond_to do |format|
        format.html do
          @clock_events = @clock_events.paginate(:per_page => params[:per_page] || 20, :page => params[:page]).order('users.username asc').order('clock_in desc')
        end
        format.csv { send_data ClockEvent.to_csv(@clock_events.order('users.username asc').order('clock_in desc')) }
      end
  end

  def new
    @clock_event = ClockEvent.new
  end

  def create
    parse_times!
    @clock_event = ClockEvent.new(params[:clock_event])

     if @clock_event.save
       redirect_to timecard_path(@clock_event.user.id), notice: "Entry added for #{@clock_event.user.username}".html_safe
      else
       render :new, notice: "Time Card Entry failed to Save".html_safe
      end
  end

  def show
    @user = User.find(params[:id])
    @clock_events = @user.clock_events.search(params[:search])
      respond_to do |format|
        format.html do
          @clock_events = @clock_events.paginate(:per_page => params[:per_page] || 5, :page => params[:page]).order('clock_in DESC')
        end
        format.csv { send_data ClockEvent.to_csv(@clock_events.order('clock_in desc')) }
        format.pdf do
          pdf = TimeCardPdf.new(@clock_events, @user)
          send_data pdf.render, filename: "timecard-#{@user.username}",
                                type: "application/pdf",
                                disposition: "inline"
         end
      end
  end

  def edit
    @user = User.find(params[:id])
    @clock_events = @user.clock_events.search(params[:search]).order("clock_in ASC").paginate(:per_page => 10, :page => params[:page])
  end

  def update
    parse_times!
    @clock_event = ClockEvent.find(params[:clock_event][:id])
    if @clock_event.update_attributes(params[:clock_event])
        redirect_to edit_timecard_path(@clock_event.user.id), notice: "Updated Successfully".html_safe
    else
        redirect_to :back, notice: "Woops.".html_safe
    end
  end

  private

  def parse_times!
    params[:clock_event].parse_time_select! :clock_in if params[:clock_event].has_key? 'clock_in(5i)'
    params[:clock_event].parse_time_select! :clock_out if params[:clock_event].has_key? 'clock_out(5i)'
  end

end

所以我相信我正在form_tag中正确地调用路径,但是当我加载页面时,我收到错误:No route matches {:action=>"show", :controller=>"timecards"}即使timecards_controller中有show动作。

我认为我必须为显式网址设置form_tag,并以某种方式传递参数中用户的:id。但我对如何做到这一点有点困惑。

总结一下。当我有下拉列表时,我选择一个用户,单击“查看员工”,这应该转到timecards_controller.rb中的show动作,其中url为/ timecards / 3(作为示例)。我之前从未使用过form_tag,因此传递路径或显式网址对我来说有点陌生。

3 个答案:

答案 0 :(得分:4)

一个简单的修补程序:

最简单的解决方法是将表单更改为一堆链接。

<%= form_tag timecodes_path, :method => :get, :id => 'timecode_employee' do %>
  <%= select_tag options_from_collection_for_select(User.employee.order("username ASC"), :id, :username) %>
  <%= submit_tag "View Employee", class: "btn btn-primary" %>
<% end %>

否则,您可以使用Javascript简单地重定向表单:

$("#timecode_employee").submit(function(e){
  var form = $(this);
  // redirect to timecards/:id
  window.location = form.attr('action') + form.find('select').val();
  e.preventDefault();
});
ClockEvents

重新设计的提示

通过添加基础TimeCard模型,可以从根本上改善您的设计。

这是一个非常常见的案例,告诉你原因:

  

客户决定让经理按时签字   卡每个月。

哦,亲爱的。现在我们需要获取该范围内的所有clock_events.signed_off_by_id并更新&#39; clock_events.state&#39;在每个。

然后客户也想知道谁签了卡。因此,您添加class ClockEvent < ActiveRecord::Base enum status: [:clocked_in, :clocked_out] has_many :users belongs_to :time_card end class TimeCard < ActiveRecord::Base belongs_to :user has_many :clock_events accepts_nested_attributes_for :clock_events end class User < ActiveRecord::Base has_many :time_cards has_many :clock_events, through: :time_cards end 并更新所有时钟事件。然后他们希望三位经理签字等等。

另类设计

请注意,这是一个自以为是的通用示例。

resources :time_cards
end
resources :clock_events do
end
resources :users, shallow: true do
  resources :clock_events do
  end
  resources :time_cards do
  end
end

每个月可能会自动发出一个TimeCard,或者如果你不想改变,那么每个用户只需坚持使用一个TimeCard。 让我们来看看这里的一些传统路线:

POST /clock_events { user_id: 1, time_card_id: 5 }

现在想象我们有一个经典的穿孔卡时钟。

我们会用:

PATCH /clock_events/1 { status: :clocked_out }

然后打出:

GET /users/1/time_cards
GET /users/1/clock_events

为你提供REST。

Nesto Presto

我们为每个用户设置了时间卡和clock_events的嵌套路由:

UserTimeCardController

从我们可以选择构建一个专用的class TimeCardsController def index @time_cards = TimeCard.all @time_cards = @time_cards.where(user: params[:user_id]) if params[:user_id] @users = scope.all end end ,或者我们可以通过TimeCardsController中的用户id param来扩展。

class TimeCardsController
  def index
    @time_cards = TimeCard.all
    @time_cards = @time_cards.where(user: params[:user_id]) if params[:user_id] 
    if params[:filters]
       @time_cards = @time_cards.search(params[:search])
    end
  end
end

过滤

但想象一下,如果我们希望管理者能够过滤他在索引上看到的员工数量 - 一个好的架构看起来像这样:

<%= form_tag(time_cards_path, method: :get) %>
  <%= select_tag options_from_collection_for_select(User.employee.order("username ASC"), :id, :username), multiple: true %>
  <%= submit_tag "Filter", class: "btn btn-primary" %>
<% end %>

在我们的索引页面上,我们将添加如下表单:

{{1}}

答案 1 :(得分:0)

您定义的路线:

timecard GET    /timecards/:id(.:format)             timecards#show

需要id才能显示正确的考勤卡。但是当您在form_tag中调用它时,您只是发送timecard_path而没有id。因此,您需要发送idtimecard对象,Rails会自动从中提取id

所以,应该是:

form_tag @timecard do
  # other code
end

@timecard必须在呈现部分的动作中实例化,并且必须是有效的TimeCard对象。

答案 2 :(得分:0)

我肯定会接受@ maxcal的建议并重写这个东西,但由于我必须快速发货,我想出了一个丑陋的UI黑客“工作”。我不喜欢它,但它完全符合我的需要。

<ul class="dropdown">
  <a href="#" class="dropdown-toggle" data-toggle="dropdown">
    <button class="btn btn-medium btn-primary">View Employees</button>
    <b class="caret"></b>
  </a>
  <ul class="dropdown-menu">
    <% @users.each do |u| %>
      <li><%= link_to "#{u.username}", timecard_path(u) %>
    <% end %>
  </ul>
</ul>

我不喜欢所有的加载,这真的很脏,但在这个例子中,因为我打破了各种Rails惯例,并且必须在今天下午运送它,直到我可以回来并使它变得更好。

让笑声开始。 :)