Rails 7:在ActiveJob API调用后更新HTML元素

huangapple go评论63阅读模式
英文:

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[&quot;lastPrice&quot;].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 &lt;%= turbo_stream_from current_user, :positions %&gt; to my view. I gave each &lt;tr&gt; 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: &quot;holding_#{position.id}&quot;, partial: &quot;shared/portfolio_positions&quot;, 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: &quot;&lt;turbo-stream action=\&quot;replace\&quot; target=\&quot;holding_7513\&quot;&gt;&lt;template&gt;&lt;tr class=\&quot;border-t border-gray-300 text-sm\&quot; id=\&quot;holding_7513\&quot;&gt;\n  &lt;td class=\&quot;whitespace-nowrap py-4 pl-4 pr-3 sm:pl-6\&quot;&gt;\n    &lt;div class=\&quot;inline\&quot;&gt;TEST&lt;/div&gt;\n    &lt;div class=\&quot;inline\&quot;&gt;&lt;/div&gt;\n  &lt;/td&gt;\n  &lt;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 &lt;div&gt;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 of broadcast_replace_later_to.
  • I used [user, &quot;positions&quot;] instead of user, &quot;positions&quot; 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, &quot;positions&quot;], target: &quot;holding_#{position.id}&quot;, partial: &quot;shared/portfolio_positions&quot;, locals: { position: position }

Index view:

&lt;%= turbo_stream_from [current_user, &quot;positions&quot;] %&gt;

Partial:

&lt;tr class=&quot;border-t border-gray-300 text-sm&quot; is=&quot;turbo-frame&quot; id=&quot;&lt;%= dom_id(position) %&gt;&quot;&gt;...&lt;/tr&gt;

huangapple
  • 本文由 发表于 2023年6月5日 23:50:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/76408089.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定