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

Issue with Stimulus Reflex and ViewComponent #284

Closed
alliedarmour opened this issue Jul 31, 2020 · 5 comments
Closed

Issue with Stimulus Reflex and ViewComponent #284

alliedarmour opened this issue Jul 31, 2020 · 5 comments

Comments

@alliedarmour
Copy link

alliedarmour commented Jul 31, 2020

I'm experiencing an issue which may be related to the coexistence with wicked pdf, but I'm not sure. In my reflex I want to re-render a view component with the morph directive, like so:

 def order_success(user_id)
    morph "#weekly-meal-list, ApplicationController.render(MealComponent.new(user_id: user_id.to_i))
  end

Then my view component is initialized with the user record like this:

  def initialize(user_id:)
    @user = User.find(user_id)
  end

I tried to pass the user instance directly aswell, but still same issue.
Now, in my view (cause of wicked pdf) I have to render the component with the render_component directive instead of just render.

<tbody id="weekly-meal-list">
      <%= render_component(MealComponent.new(user_id: user.id)) %>
</tbody>

Now, when I want to re-render just this table with the reflex above, I get the following error:

StimulusReflex::Channel Failed to invoke Order#order_success! http://localhost:3000/users/1/order_cards/new '#<MealComponent:0x000055fe47a95e98 @user=#<User id: 1, name: "Admin User", email: "example@braunfels.org", password_digest: "$2a$10$sx3fPbyQoPfIP4j/iIiGeOYbNw2laFIE.iVwzCxP7ts...", twoFactorEnabled: "enabled", canDoGuestOrder: true, commentedToday: true, hasCommentedThisWeek: true, otp_secret_key: "ZZOJYZGT6ENI2HADWXPELKVAGREEW2VS", created_at: "2020-07-18 11:36:03", updated_at: "2020-07-24 17:18:03", reset_digest: nil, reset_sent_at: nil, workplace_id: 2, has_ldap_connection: nil, salt: "zhxBMJfAmPVW4MiXvCu3", remember_me_token: "T1fgN4zhqdW17juCH6K4", remember_me_token_expires_at: "2020-08-06 07:51:43", user_accountable_type: "ExternalClinicAccount", user_accountable_id: 1>>' is not an ActiveModel-compatible object. It must implement :to_partial_path. /home/mkrs/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/actionview-6.0.3.2/lib/action_view/renderer/partial_renderer.rb:493:in partial_path'`

But as I checked inside the initialize method of the component, it IS implementing :to_partial_path:

(byebug) user.respond_to?(:to_partial_path)
true

Any idea what is happening here? If I just let the whole page reload it works cause it calls the controller action, but it doesn't work like this.

@leastbad
Copy link
Contributor

leastbad commented Jul 31, 2020

That is indeed a weird one! I have essentially zero experience with ViewComponent, but I do have two things for you to try.

First, if you load up a byebug breakpoint again and actually get it to return user.to_partial_path, what is the output?

Second, could you try adding include ActiveModel::Conversion to your ViewComponent class and seeing if that does anything?

@alliedarmour
Copy link
Author

Okay it seems I found the problem, it really seems to be the monkey patched render_component. I posted it in the view component issues aswell, until then I will replace the render method for wicked_pdf :).

@leastbad
Copy link
Contributor

leastbad commented Aug 1, 2020

I'm glad you got sorted out, but is this something that could impact other people? If so, it would be nice if you could explain the problem and solution in more detail as about 50% of our users are ViewComponent users, too.

@alliedarmour
Copy link
Author

alliedarmour commented Aug 2, 2020

So I'll try to explain it. In my application I use wicked_pdf to create pdf for orders the users create, so it can be printed out. Because both libraries (wicked_pdf aswell as ViewComponents) are monkey patching the ActionController::Base#render method built into Rails, it causes problems like you can't even start the application.

So I went to the ViewComponents repository and found a rather hacky solution by replacing the render action for the components with render_component, which doesn't use the monkey patch (you can disable the option in the application.rb file).
This way both gems can work together in one application.

It's described here

Then the next problem popped up, for which I created this thread. You can't rerender a view component with the base render method from a reflex.

My solution (until there's a better fix for it either from wicked_pdf or when Rails 6.1 comes out) was to create an initializer (I got the hint from this PR which is linked in the according issue over at wicked_pdf):

class WickedPdf
  module PdfHelper
    def render(*args)
      options = args.first
      if options.is_a?(Hash) && options.key?(:pdf)
        render_with_wicked_pdf(options)
      else
        super
      end
    end

    def render_to_string(*args)
      options = args.first
      if options.is_a?(Hash) && options.key?(:pdf)
        render_to_string_with_wicked_pdf(options)
      else
        super
      end
    end
  end
end

With this in place, you can use the "standard" render method again to render (and rerender) the view components in the views and from a Reflex. Wicked_Pdf is also working for me. But maybe I'll change to Grover or something like this in the near future.

Hope it helps somebody who maybe got the same problems :).

@leastbad
Copy link
Contributor

leastbad commented Aug 2, 2020

@alliedarmour Thanks for doing this. Really appreciate it.

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

2 participants