Turbo Stream动作中after_action回调无法执行的解决方案求助
问题
我有一个ThaaliTakhmeens控制器,其中部分动作拥有对应的turbo_stream模板,通过Hotwire结合Pagy实现数据库实例的懒加载分页。为遵循DRY原则,我将这些动作中的重复逻辑抽取到after_action回调中,但该回调完全未执行(已通过debugger验证),导致页面无法渲染实例,还出现模板错误。
相关控制器代码:
after_action :set_pagy_thaalis_total, only: [:complete, :pending, :all] def complete @tt = ThaaliTakhmeen.includes(:sabeel).completed_year(@year) end def pending @tt = ThaaliTakhmeen.includes(:sabeel).pending_year(@year) end def all @tt = ThaaliTakhmeen.includes(:sabeel).in_the_year(@year) end private def set_pagy_thaalis_total @total = @tt.count @pagy, @thaalis = pagy_countless(@tt, items: 8) debugger end
访问complete动作时的日志:
Started GET "/takhmeens/2022/complete" for ::1 at 2023-01-21 10:07:35 +0530 Processing by ThaaliTakhmeensController#complete as HTML Parameters: {"year"=>"2022"} Rendering layout layouts/application.html.erb Rendering thaali_takhmeens/complete.html.erb within layouts/application Rendered shared/_results.html.erb (Duration: 2.4ms | Allocations: 2088) Rendered thaali_takhmeens/complete.html.erb within layouts/application (Duration: 3.7ms | Allocations: 2396) Rendered layout layouts/application.html.erb (Duration: 3.9ms | Allocations: 2477) Completed 500 Internal Server Error in 6ms (ActiveRecord: 0.0ms | Allocations: 3027) ActionView::Template::Error (undefined method `any?' for nil:NilClass '.freeze; if instances.any? ^^^^^): 1: <%= turbo_frame_tag :results, data: { turbo_action: "advance" } do %> 2: <div class="container text-center mt-5"> 3: <% if instances.any? %> 4: <%= render partial: "theader" %> 5: <div id=<%="#{id}"%> ></div> 6: <%= turbo_frame_tag :pagination, loading: :lazy, src: path %> %> app/views/shared/_results.html.erb:3 app/views/shared/_results.html.erb:1 app/views/thaali_takhmeens/complete.html.erb:8
错误原因是after_action未执行,导致@thaalis(即模板中的instances)未被赋值。这些动作同时具备HTML和turbo_stream模板,若不使用回调直接在动作内编写逻辑,内容可正常加载,但会产生重复代码。请问有什么可行的解决方法?是否有其他代码重构方式,或需要在回调中设置特定内容使其执行?
解决方案
1. 改用before_action替代after_action
模板渲染发生在after_action之前,这是回调未生效的核心原因。把回调改成before_action,就能确保在动作执行后、模板渲染前完成分页逻辑的处理:
before_action :set_pagy_thaalis_total, only: [:complete, :pending, :all] # 其余动作和私有方法代码不变
2. 提取共用逻辑到私有方法,动作内显式调用
如果不想依赖回调,可把分页逻辑封装成私有方法,在每个动作里直接调用,既保证逻辑复用,又避免回调时机问题:
def complete @tt = ThaaliTakhmeen.includes(:sabeel).completed_year(@year) set_pagy_thaalis_total end def pending @tt = ThaaliTakhmeen.includes(:sabeel).pending_year(@year) set_pagy_thaalis_total end def all @tt = ThaaliTakhmeen.includes(:sabeel).in_the_year(@year) set_pagy_thaalis_total end private def set_pagy_thaalis_total @total = @tt.count @pagy, @thaalis = pagy_countless(@tt, items: 8) end
3. 利用around_action(可选)
around_action可以包裹动作的执行过程,确保分页逻辑在模板渲染前完成,相对前两种方式稍复杂:
around_action :wrap_pagy_logic, only: [:complete, :pending, :all] def complete @tt = ThaaliTakhmeen.includes(:sabeel).completed_year(@year) end # 其余动作代码不变 private def wrap_pagy_logic yield # 先执行动作代码 set_pagy_thaalis_total # 动作执行完后处理分页 end def set_pagy_thaalis_total @total = @tt.count @pagy, @thaalis = pagy_countless(@tt, items: 8) end
关键说明
after_action的执行时机是动作执行完成、模板渲染结束之后,所以模板渲染时根本拿不到回调里赋值的变量。改用before_action或者动作内显式调用,就能解决这个时机问题。
内容的提问来源于stack exchange,提问作者Juzer Shakir
相关产品推荐
相关产品推荐

