diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index c9b233a5e..9ab9a2296 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -10,6 +10,10 @@ nav_order: 5 ## main +* Add `use_helper` API. + + *Reegan Viljoen* + * Fix bug where the `Rails` module wasn't being searched from the root namespace. *Zenéixe* diff --git a/docs/guide/helpers.md b/docs/guide/helpers.md index 1407fe14a..172c3aea0 100644 --- a/docs/guide/helpers.md +++ b/docs/guide/helpers.md @@ -51,6 +51,26 @@ class UserComponent < ViewComponent::Base end ``` +## UseHelpers setter + +By default, ViewComponents don't have access to helper methods defined externally. The `use_helpers` method allows external helpers to be called from the component. + +To use the `use_helpers` method, include `ViewComponent::UseHelpers`. +`UseHelpers` defines the helper on the component and is similar in use to using `delegate` on helpers. + +```ruby +class UseHelpersComponent < ViewComponent::Base + include ViewComponent::UseHelpers + use_helpers :icon + + erb_template <<-ERB +
+ <%= icon :user %> +
+ ERB +end +``` + ## Nested URL helpers Rails nested URL helpers implicitly depend on the current `request` in certain cases. Since ViewComponent is built to enable reusing components in different contexts, nested URL helpers should be passed their options explicitly: diff --git a/lib/view_component/base.rb b/lib/view_component/base.rb index f5a91ac11..6a2538c42 100644 --- a/lib/view_component/base.rb +++ b/lib/view_component/base.rb @@ -12,6 +12,7 @@ require "view_component/slotable" require "view_component/translatable" require "view_component/with_content_helper" +require "view_component/use_helpers" module ViewComponent class Base < ActionView::Base diff --git a/lib/view_component/use_helpers.rb b/lib/view_component/use_helpers.rb new file mode 100644 index 000000000..32d13cd43 --- /dev/null +++ b/lib/view_component/use_helpers.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module ViewComponent::UseHelpers + extend ActiveSupport::Concern + + class_methods do + def use_helpers(*args) + args.each do |helper_method| + class_eval(<<-RUBY, __FILE__, __LINE__ + 1) + def #{helper_method}(*args, &block) + raise HelpersCalledBeforeRenderError if view_context.nil? + __vc_original_view_context.#{helper_method}(*args, &block) + end + RUBY + + ruby2_keywords(helper_method) if respond_to?(:ruby2_keywords, true) + end + end + end +end diff --git a/test/sandbox/app/components/use_helpers_component.html.erb b/test/sandbox/app/components/use_helpers_component.html.erb new file mode 100644 index 000000000..f24542007 --- /dev/null +++ b/test/sandbox/app/components/use_helpers_component.html.erb @@ -0,0 +1,3 @@ +
+ <%= message %> +
diff --git a/test/sandbox/app/components/use_helpers_component.rb b/test/sandbox/app/components/use_helpers_component.rb new file mode 100644 index 000000000..375c07db6 --- /dev/null +++ b/test/sandbox/app/components/use_helpers_component.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class UseHelpersComponent < ViewComponent::Base + include ViewComponent::UseHelpers + + use_helpers :message +end diff --git a/test/sandbox/test/rendering_test.rb b/test/sandbox/test/rendering_test.rb index 2f5069ea4..cd985beb6 100644 --- a/test/sandbox/test/rendering_test.rb +++ b/test/sandbox/test/rendering_test.rb @@ -1094,4 +1094,9 @@ def test_content_security_policy_nonce assert_selector("script", text: "\n//\n", visible: :hidden) end + + def test_use_helper + render_inline(UseHelpersComponent.new) + assert_selector ".helper__message", text: "Hello helper method" + end end