From 1ac002cccbc104d39decb235cbf47addb6ea77fe Mon Sep 17 00:00:00 2001 From: Andrew Janssen Date: Wed, 4 Dec 2024 08:47:14 -0800 Subject: [PATCH] [Rails] Explain how to render without a view context --- reference/rails/rendering-out-of-context.md | 36 +++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 reference/rails/rendering-out-of-context.md diff --git a/reference/rails/rendering-out-of-context.md b/reference/rails/rendering-out-of-context.md new file mode 100644 index 0000000..5ba1d82 --- /dev/null +++ b/reference/rails/rendering-out-of-context.md @@ -0,0 +1,36 @@ +Most Rails renders are synchronous responses to user requests. In these synchronous contexts, the `render` method is defined and an `ActionView::Context` is present. We need these two things to render Phlex components. + +However, these two things are not always present. How can we render Phlex components in an ActiveRecord callback? How can we render in a background job? The answer is to use `ApplicationController.render_to_string`. + +Example 1: rendering a Phlex component for a Turbo Stream in response to an ActiveRecord callback: + +``` +class Post < ApplicationRecord + after_create_commit { + model = self + component = Components::MyPostComponent.new(post: model) + # Setting the request is not always required, but common Rails features + # including path helpers fail without it. + fake_rack_request = Rack::MockRequest.env_for("http://localhost", method: :get) + controller.request = ActionDispatch::Request.new(fake_rack_request) + html = controller.render_to_string(component, layout: false) + broadcast_prepend_to("my-streamable", html:) + } +end +``` + +Example 2: rendering a Phlex component in a background job: +``` +class MailJob < ApplicationJob + queue_as :default + + def perform(*args) + model = Post.create! + component = Components::MyPostComponent.new(post: model) + fake_rack_request = Rack::MockRequest.env_for("http://localhost", method: :get) + controller.request = ActionDispatch::Request.new(fake_rack_request) + html = controller.render_to_string(component, layout: false) + HelloMailer.with(html:).hello_email.deliver_now + end +end +```