英文:
Rails 7: Update HTML element after ActiveJob API call
问题
在页面加载时,我正在对第三方服务进行多次API调用以获取要显示在页面上的信息,这会减慢页面加载速度。为了缓解这个问题,我添加了一个ActiveJob类,用于执行API调用并将其添加到后台作业处理器中。页面加载速度要快得多,我可以看到后台中执行的作业。
@derivatives.each { |d| CallLastPriceJob.perform_later(d.id) }
我的问题是,如何在页面上获取更新后的API信息?
在ActiveJob类中,我正在使用API数据更新实例的属性。
@position.last_price = option.first["lastPrice"].to_f
我尝试在ActiveJob类中添加after_perform
回调来将更新后的实例发送给模型。
after_perform do |job|
Holding.notify_last_price_processed(@position)
end
我认为我需要使用Turbo Frames或Streams,但由于我是Rails 7的新手,所以不确定。我尝试在Holding.notify_last_price_processed
中使用broadcasts_to
方法,但它的工作方式不如预期。任何帮助将不胜感激。
谢谢!
编辑:
在CallLastPriceJob
中的after_perform
回调正确地将position
发送回模型。
如下Les在下面的评论中建议,我在视图中添加了<%= turbo_stream_from current_user, :positions %>
。我使用dom_id
助手方法为每个<tr>
行分配了唯一的ID。每行都是使用部分shared/portfolio_positions
渲染的。
在Holding.notify_last_price_processed
方法中,我使用了broadcast_replace_to
方法。
Turbo::StreamsChannel.broadcast_replace_to [user, :positions], target: "holding_#{position.id}", partial: "shared/portfolio_positions", locals: { position: position }
在我的延迟作业日志中,我可以看到部分被渲染,并且ActionCable正在广播。
Rendered shared/_portfolio_positions.html.erb (Duration: 0.7ms | Allocations: 409)
[ActionCable] Broadcasting to Z2lkOi8vdGhldGFmbG93L1VzZXIvMQ:positions: "<turbo-stream action=\"replace\" target=\"holding_7513\"><template><tr class=\"border-t border-gray-300 text-sm\" id=\"holding_7513\">...
似乎流名称与我的服务器日志中显示的内容相同。
Turbo::StreamsChannel is streaming from Z2lkOi8vdGhldGFmbG93L1VzZXIvMQ:positions
...但是网页上的内容没有更新...
编辑2:
我遇到了与使用Turbo Streams更新表元素相关的问题,但我测试了建议的解决方法,并且还将所有元素更改为<div>
,但仍然无法更新页面内容。
再次验证在我的延迟作业日志文件中,我可以看到ActionCable广播消息,一切似乎都连接正确。唯一看不到的是开发工具中的电缆消息。
英文:
I have a Rails 7.0 application. On page load, I'm making multiple API calls to a third party service to retrieve information to display on the page, which slows the page load time down. To alleviate this, I added an ActiveJob class that performs the API call and adds this to my background job processor. The page load time is much faster and I can see the jobs being executed in the background.
@derivatives.each { |d| CallLastPriceJob.perform_later(d.id) }
My question is, how do I get the API information updated on page?
In the ActiveJob class, I'm updating the instance's attribute with the API data.
@position.last_price = option.first["lastPrice"].to_f
I tried adding the after_perform
callback in the ActiveJob class to send the updated instance to the model.
after_perform do |job|
Holding.notify_last_price_processed(@position)
end
I assume I need to use Turbo Frames or Streams but I'm not sure since I'm new to Rails 7. I tried using the broadcasts_to
method in Holding.notify_last_price_processed
but it wasn't working as expected. Any help is appreciated.
Thanks!
EDIT:
The after_perform
callback in CallLastPriceJob
is sending the position
back to the model correctly.
As suggested by Les in the comments below, I added <%= turbo_stream_from current_user, :positions %>
to my view. I gave each <tr>
row a unique ID using the dom_id
helper method. Each row is being rendered using a partial shared/portfolio_positions
.
In the Holding.notify_last_price_processed
method I'm using the broadcast_replace_to
method.
Turbo::StreamsChannel.broadcast_replace_to [user, :positions], target: "holding_#{position.id}", partial: "shared/portfolio_positions", locals: { position: position }
In my delayed_job log I can see the partial being rendered and ActionCable broadcasting
Rendered shared/_portfolio_positions.html.erb (Duration: 0.7ms | Allocations: 409)
[ActionCable] Broadcasting to Z2lkOi8vdGhldGFmbG93L1VzZXIvMQ:positions: "<turbo-stream action=\"replace\" target=\"holding_7513\"><template><tr class=\"border-t border-gray-300 text-sm\" id=\"holding_7513\">\n <td class=\"whitespace-nowrap py-4 pl-4 pr-3 sm:pl-6\">\n <div class=\"inline\">TEST</div>\n <div class=\"inline\"></div>\n </td>\n <td cl...
Performed CallLastPriceJob (Job ID: 20f8a4de-c6b8-473e-8b49-db66c87d845b) from DelayedJob(default) in 153.72ms
It appears the stream name is the same as what's being displayed in my server log.
Turbo::StreamsChannel is streaming from Z2lkOi8vdGhldGFmbG93L1VzZXIvMQ:positions
...but the contents on the webpage aren't updating...
EDIT 2:
I ran across this issue https://github.com/hotwired/turbo/issues/48 related to updating table elements with Turbo Streams, but I tested with the workaround that was suggested and in addition, changed all elements to <div>
s and it still isn't updating the page contents.
Again, I verify that in my delayed_job log file I can see the ActionCable broadcast message and everything appears to be connected correctly. The only thing I can't see is the cable messages in dev tools.
答案1
得分: 1
- 通过迁移而不是将其作为虚拟属性来添加
last_price
属性到我的模型。 - 使用
broadcast_update_later_to
方法而不是broadcast_replace_later_to
。 - 在模型和视图中,使用
[user, "positions"]
而不是user, "positions"
作为流名称。
做出这些更改后,页面上的内容开始更新。
最终设置
控制器:
@derivatives.each { |d| CallLastPriceJob.perform_later(d.id) }
任务:
after_perform do |job|
Holding.notify_last_price_processed(@position)
end
模型:
Turbo::StreamsChannel.broadcast_update_later_to [user, "positions"], target: "holding_#{position.id}", partial: "shared/portfolio_positions", locals: { position: position }
索引视图:
<%= turbo_stream_from [current_user, "positions"] %>
部分:
<tr class="border-t border-gray-300 text-sm" is="turbo-frame" id="<%= dom_id(position) %>">...</tr>
英文:
I was able to figure this out and get it working, after making a few changes to my code.
- I added the
last_price
attribute to my model via migration instead of having it as a virtual attribute. - I used the
broadcast_update_later_to
method instead ofbroadcast_replace_later_to
. - I used
[user, "positions"]
instead ofuser, "positions"
for the stream name in the Model and in View.
After making these changes the contents on the page started updating.
Final setup
Controller:
@derivatives.each { |d| CallLastPriceJob.perform_later(d.id) }
Job:
after_perform do |job|
Holding.notify_last_price_processed(@position)
end
Model:
Turbo::StreamsChannel.broadcast_update_later_to [user, "positions"], target: "holding_#{position.id}", partial: "shared/portfolio_positions", locals: { position: position }
Index view:
<%= turbo_stream_from [current_user, "positions"] %>
Partial:
<tr class="border-t border-gray-300 text-sm" is="turbo-frame" id="<%= dom_id(position) %>">...</tr>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论