Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Losing slots after reflex #95

Open
omarluq opened this issue Jan 24, 2023 · 7 comments
Open

Losing slots after reflex #95

omarluq opened this issue Jan 24, 2023 · 7 comments

Comments

@omarluq
Copy link
Contributor

omarluq commented Jan 24, 2023

In my app I have the following dropdown menu component
when the open function is triggered the menu component is losing all of its slots, am I missing something? I've never encountered this issue before

module Ui
  module Dropdown
    class MenuComponent < ApplicationComponent
      renders_one :trigger, -> { Ui::Dropdown::TriggerComponent.new(data: reflex_data_attributes(:open).merge({ action: 'keydown->application-controller#stopProp' })) }
      renders_many :sections, ->(**system_arguments) { Ui::Dropdown::SectionComponent.new(**system_arguments) }

      def initialize(classes: '')
        @classes = classes
        @role = 'menu'
        @hidden = 'hidden'
        @transition = 'transition ease-out duration-300'
        @transform = 'translate-y-2 opacity-0 scale-95'
      end

      def collection_key
        SecureRandom.hex(16)
      end

      def open
        @hidden = ''
        @transition = 'transition ease-in duration-600'
        @transform = 'translate-y-0 opacity-100 scale-100'
      end

      def close
        @hidden = 'hidden'
        @transition = 'transition ease-out duration-300'
        @transform = 'translate-y-2 opacity-0 scale-95'
      end

      def menu_key
        "menu_for_#{key}"
      end
    end
  end
end
<%= component_controller do %>
  <div class="relative inline-block text-left">
    <%= trigger %>
    <div id="<%= menu_key %>" class="<%= "#{@hidden} #{@transition} #{@transform} absolute right-0 mt-2 origin-top-right divide-y divide-gray-100 rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none w-max" %>" role="menu" aria-orientation="vertical" aria-labelledby="user-menu-button" tabindex="-1" style="z-index:999999">
        <% sections.each do |section| %>
          <%= section %>
        <% end %>
    </div>
    <div id='hidden-close-button-for-active-dropdown' class="<%= "#{@hidden} fixed inset-0 w-full h-full" %>" data-reflex="click->Ui::Dropdown::MenuComponent#close" data-key="<%= key %>" style="z-index:9997"></div>
  </div>
<% end %>
@omarluq
Copy link
Contributor Author

omarluq commented Jan 24, 2023

P.S I can fix the issue by adding data-reflex-permanent tags on a div a wrap the trigger and the sections with it but is that an optimal solution?

@omarluq
Copy link
Contributor Author

omarluq commented Jan 25, 2023

@julianrubisch @joshleblanc any ideas?

@joshleblanc
Copy link
Owner

I talked with Omar awhile ago, and I think he found a workaround. Leaving this open because I'm pretty sure it's still a bug.

@omarluq
Copy link
Contributor Author

omarluq commented Jun 1, 2023

After you agreed to whitelist the cable_ready helper on the components I ended up switching the page morphs to add_css_class and remove_css_class. Another fix that I found at the time was using printing tags when calling the with_slot_name helper ex:

<%= render(Component.new) do |c|%> 
  <%= c.with_slot %>
<% end %>

But the ViewComponent docs recommend using non-printing tags for slots!

@Laykou
Copy link
Contributor

Laykou commented Jun 2, 2023

I see slots are currently still not supported, as ViewComponentReflex ignores any @__vc instance variable.

I assume that's because it's problematic to serialize/deserialize them:

https://github.com/joshleblanc/view_component_reflex/blob/master/lib/view_component_reflex/component.rb#L243

 def safe_instance_variables
  instance_variables.reject { |ivar| ivar.start_with?("@__vc") } - unsafe_instance_variables - omitted_from_state
end

@Laykou
Copy link
Contributor

Laykou commented Jun 2, 2023

My quick workaround:

  • put the content of the block into the separate partial file
    • having the convenience of HTML/HAML syntax in the view file instead of somehow generating an HTML string in Ruby
  • put only the path (string) as a state to the ViewComponent
    • which is easily serializable
  • call "render" of the partial inside component which fills in all component.with_xxxx methods

Example:

# my_component.rb
class MyComponent < ApplicationViewComponent
  renders_one :hello_world

  def initialize(partial_path:, other_state: {})
    @partial_path = partial_path
    @other_state = other_state
  end

  def before_render
      super

      render partial: @partial_path, locals: { component: self } if @partial_path
  end
end
- # my_component.html.haml
= hello_world
- # show.html.haml
= render MyComponent.new(partial_path: '_partial_component_content', other_state: {})
- # _partial_component_content.html.haml
- component.with_hello_world do
  Hello world!

@omarluq
Copy link
Contributor Author

omarluq commented Jun 2, 2023

@Laykou I think that this workaround is very similar to using printing tags to call your slots, both solutions force a recall of the slots block on every refresh pretty much

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants