diff --git a/docs/ui-in-pure-ruby/components/component-api.md b/docs/ui-in-pure-ruby/components/component-api.md index e166adec..62f412c9 100644 --- a/docs/ui-in-pure-ruby/components/component-api.md +++ b/docs/ui-in-pure-ruby/components/component-api.md @@ -125,6 +125,42 @@ class SomeComponent < Matestack::Ui::Component end ``` +## Render? + +Use the `render?` method to conditionally render the component based on custom rules: + +```ruby +class AdminComponent < Matestack::Ui::Component + required :user + + def render? + context.user.admin? + end + + def response + div id: "admin-component" do + plain "This component should only get rendered for admins" + end + end +end +``` + +This is particularly useful to avoid plastering your views with conditional statements like `if` and `unless`. + +Instead of: + +```ruby + <% if current_user.admin? %> + <%= Components::AdminComponent.(user: current_user) %> + <% end %> +``` + +You can just use: + +```ruby + <%= Components::AdminComponent.(user: current_user) %> +``` + ## Prepare Use a prepare method to resolve instance variables before rendering a component if required. diff --git a/lib/matestack/ui/core/base.rb b/lib/matestack/ui/core/base.rb index 33ae19e6..4b70d2f7 100644 --- a/lib/matestack/ui/core/base.rb +++ b/lib/matestack/ui/core/base.rb @@ -12,6 +12,8 @@ class Base attr_accessor :html_tag, :text, :options, :parent, :escape, :bind_to_parent def initialize(html_tag = nil, text = nil, options = {}, &block) + return unless render? + self.bind_to_parent = ([:without_parent].include?(html_tag) ? false : true) self.slots = self.options.delete(:slots) if self.options # extract_options(text, options) is called in properties @@ -26,6 +28,12 @@ def initialize(html_tag = nil, text = nil, options = {}, &block) self end + # can be optionally overwritten in subclass + # in order to conditionally render the component + def render? + true + end + # check if text is given and set text and options accordingly def extract_options(text, options) if text.is_a? Hash diff --git a/spec/test/base/core/component/conditional_rendering_spec.rb b/spec/test/base/core/component/conditional_rendering_spec.rb new file mode 100644 index 00000000..7123d7ac --- /dev/null +++ b/spec/test/base/core/component/conditional_rendering_spec.rb @@ -0,0 +1,105 @@ +require_relative "../../../support/utils" +include Utils + +describe "Component", type: :feature, js: true do + + before :all do + class ComponentTestController < ActionController::Base + include Matestack::Ui::Core::Helper + layout "application" + + def my_action + render ExamplePage + end + end + + Rails.application.routes.append do + scope "component_conditional_rendering_spec" do + get '/component_test', to: 'component_test#my_action', as: 'conditional_component_test_action' + end + end + Rails.application.reload_routes! + end + + describe "conditional component" do + + it "renders by default if '.render?' method is not overrided" do + class DefaultRenderingComponent < Matestack::Ui::Component + def response + div id: "my-component" do + plain "default rendering component" + end + end + + register_self_as(:default_rendering_component) + end + + class ExamplePage < Matestack::Ui::Page + def response + div id: "div-on-page" do + default_rendering_component + end + end + end + + visit "component_conditional_rendering_spec/component_test" + expect(page).to have_xpath('//div[@id="div-on-page"]/div[@id="my-component" and contains(.,"default rendering component")]') + end + + it "renders if '.render?' method is overrided and returns true" do + class ConditionalRenderingComponent < Matestack::Ui::Component + def response + div id: "my-component" do + plain "conditional rendering component" + end + end + + def render? + true + end + + register_self_as(:conditional_rendering_component) + end + + class ExamplePage < Matestack::Ui::Page + def response + div id: "div-on-page" do + conditional_rendering_component + end + end + end + + visit "component_conditional_rendering_spec/component_test" + expect(page).to have_xpath('//div[@id="div-on-page"]/div[@id="my-component" and contains(.,"conditional rendering component")]') + end + + it "doesn't render if '.render?' method is overrided and returns false" do + class ConditionalRenderingComponent < Matestack::Ui::Component + def response + div id: "my-component" do + plain "conditional rendering component" + end + end + + def render? + false + end + + register_self_as(:conditional_rendering_component) + end + + class ExamplePage < Matestack::Ui::Page + def response + div id: "div-on-page" do + conditional_rendering_component + end + end + end + + visit "component_conditional_rendering_spec/component_test" + expect(page).to_not have_xpath('//div[@id="div-on-page"]/div[@id="my-component" and contains(.,"conditional rendering component")]') + end + + end + +end