Rails form_tag 显示动作
Rails form_tag to show action
我有一个 Rails 应用程序,其中有一个我想要下拉列表的部分,所以当用户被 selected 时,它将对 urls 执行一个获取方法/timecards/:user_id 这将是传递给用户 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"}
即使 [=39 中有一个显示操作=].
我认为我必须为显式 url 设置 form_tag
并以某种方式在参数中传递用户的 :id
。但我对如何做到这一点有点困惑。
总结一下。当我有下拉列表时,我 select 一个用户,单击 "View Employee",然后应该使用 /timecards/3 的 url 进入 timecards_controller.rb 中的显示操作(举个例子)。我以前从未以这种方式使用过 form_tag,所以传递路径或显式 url 对我来说有点陌生。
您定义的路线:
timecard GET /timecards/:id(.:format) timecards#show
需要 id
才能显示正确的时间卡。但是当您在 form_tag
中调用它时,您只是在发送 timecard_path
而没有 id
。所以你确实需要发送一个 id
或 timecard
对象,Rails 会自动从中提取 id
。
所以,应该是:
form_tag @timecard do
# other code
end
@timecard
必须在呈现部分的操作中实例化,并且它必须是有效的 TimeCard
对象。
一个简单的修补程序:
最简单的解决方法是将表单更改为一堆链接。
<%= User.employee.order("username ASC").each |u| %>
<%= link_to u.username, timecard_path %>
<% end %>
否则你可以使用 Javascript 简单地使表单重定向:
<%= 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 %>
$("#timecode_employee").submit(function(e){
var form = $(this);
// redirect to timecards/:id
window.location = form.attr('action') + form.find('select').val();
e.preventDefault();
});
重新设计的技巧
可以通过添加底层 TimeCard 模型从根本上改进您的设计。
这是一个非常常见的案例,可以告诉您原因:
The client decides that they want to have managers sign off on time
cards every month.
哦,该死。现在我们需要获取该范围内的所有 ClockEvents
并更新每个 'clock_events.state'。
但是客户也想知道谁签了名片。所以你添加了一个 clock_events.signed_off_by_id
并更新了所有的时钟事件。然后他们要三位经理签字等
替代设计
请注意,这是一个自以为是的通用示例。
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
可能会每月自动发放一张 TimeCard,或者如果您不想更改,只需坚持为每个用户使用一张 TimeCard。
让我们在这里走一些传统路线:
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
现在想象一下我们有一个经典的打卡打卡时钟。
我们会打卡:
POST /clock_events { user_id: 1, time_card_id: 5 }
然后打出:
PATCH /clock_events/1 { status: :clocked_out }
这就是您的 REST。
内斯托快板
我们有时间卡的嵌套路线,每个用户 clock_events:
GET /users/1/time_cards
GET /users/1/clock_events
我们可以选择构建专用的 UserTimeCardController
或者我们可以通过 TimeCardsController 中的用户 ID 参数来确定范围。
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
过滤
但是想象一下,如果我们希望经理能够过滤他在索引中看到的员工数量——一个好的架构应该是这样的:
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 %>
我肯定会采纳@maxcal 的建议并重写这个东西,但由于我必须快速发布,所以我想出了一个丑陋的 UI 黑客 "works"。我不喜欢它,但它完全符合我的需要。
<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 惯例并且必须在今天下午发货,所以它会一直工作直到我来回来让它变得更好。
开始大笑吧。 :)
我有一个 Rails 应用程序,其中有一个我想要下拉列表的部分,所以当用户被 selected 时,它将对 urls 执行一个获取方法/timecards/:user_id 这将是传递给用户 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"}
即使 [=39 中有一个显示操作=].
我认为我必须为显式 url 设置 form_tag
并以某种方式在参数中传递用户的 :id
。但我对如何做到这一点有点困惑。
总结一下。当我有下拉列表时,我 select 一个用户,单击 "View Employee",然后应该使用 /timecards/3 的 url 进入 timecards_controller.rb 中的显示操作(举个例子)。我以前从未以这种方式使用过 form_tag,所以传递路径或显式 url 对我来说有点陌生。
您定义的路线:
timecard GET /timecards/:id(.:format) timecards#show
需要 id
才能显示正确的时间卡。但是当您在 form_tag
中调用它时,您只是在发送 timecard_path
而没有 id
。所以你确实需要发送一个 id
或 timecard
对象,Rails 会自动从中提取 id
。
所以,应该是:
form_tag @timecard do
# other code
end
@timecard
必须在呈现部分的操作中实例化,并且它必须是有效的 TimeCard
对象。
一个简单的修补程序:
最简单的解决方法是将表单更改为一堆链接。
<%= User.employee.order("username ASC").each |u| %>
<%= link_to u.username, timecard_path %>
<% end %>
否则你可以使用 Javascript 简单地使表单重定向:
<%= 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 %>
$("#timecode_employee").submit(function(e){
var form = $(this);
// redirect to timecards/:id
window.location = form.attr('action') + form.find('select').val();
e.preventDefault();
});
重新设计的技巧
可以通过添加底层 TimeCard 模型从根本上改进您的设计。
这是一个非常常见的案例,可以告诉您原因:
The client decides that they want to have managers sign off on time cards every month.
哦,该死。现在我们需要获取该范围内的所有 ClockEvents
并更新每个 'clock_events.state'。
但是客户也想知道谁签了名片。所以你添加了一个 clock_events.signed_off_by_id
并更新了所有的时钟事件。然后他们要三位经理签字等
替代设计
请注意,这是一个自以为是的通用示例。
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
可能会每月自动发放一张 TimeCard,或者如果您不想更改,只需坚持为每个用户使用一张 TimeCard。 让我们在这里走一些传统路线:
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
现在想象一下我们有一个经典的打卡打卡时钟。
我们会打卡:
POST /clock_events { user_id: 1, time_card_id: 5 }
然后打出:
PATCH /clock_events/1 { status: :clocked_out }
这就是您的 REST。
内斯托快板
我们有时间卡的嵌套路线,每个用户 clock_events:
GET /users/1/time_cards
GET /users/1/clock_events
我们可以选择构建专用的 UserTimeCardController
或者我们可以通过 TimeCardsController 中的用户 ID 参数来确定范围。
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
过滤
但是想象一下,如果我们希望经理能够过滤他在索引中看到的员工数量——一个好的架构应该是这样的:
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 %>
我肯定会采纳@maxcal 的建议并重写这个东西,但由于我必须快速发布,所以我想出了一个丑陋的 UI 黑客 "works"。我不喜欢它,但它完全符合我的需要。
<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 惯例并且必须在今天下午发货,所以它会一直工作直到我来回来让它变得更好。
开始大笑吧。 :)