Giter Club home page Giter Club logo

view_component's People

Contributors

asgerb avatar blakewilliams avatar boardfish avatar bobmaerten avatar camertron avatar czj avatar dependabot[bot] avatar dylnclrk avatar elia avatar frznk-tank avatar fugufish avatar g13ydson avatar joelhawksley avatar johannesengl avatar jonspalmer avatar juanmanuelramallo avatar manuelpuyol avatar maxbeizer avatar metade avatar nachiket87 avatar natashau avatar nicolas-brousse avatar reeganviljoen avatar rmacklin avatar simonrand avatar spone avatar tclem avatar vinistock avatar xronos-i-am avatar yhirano55 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

view_component's Issues

[1.5.0] Migration from version 1.4.0

Could you please tell about migration from version 1.4.0?

I updated the gem.

I changed this line:

require 'action_view/component/base'

To this:

require 'action_view/component'

After that, I can't start the project:

% rails s
=> Booting Puma
=> Rails 6.0.1 application starting in development
=> Run `rails server --help` for more startup options
Exiting
Traceback (most recent call last):
	77: from bin/rails:5:in `<main>'
	76: from bin/rails:5:in `load'
	75: from /Users/afuno/Projects/my.app/bin/spring:16:in `<top (required)>'
	74: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require'
	73: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require'
	72: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/spring-2.1.0/lib/spring/binstub.rb:11:in `<top (required)>'
	71: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/spring-2.1.0/lib/spring/binstub.rb:11:in `load'
	70: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/spring-2.1.0/bin/spring:49:in `<top (required)>'
	69: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/spring-2.1.0/lib/spring/client.rb:30:in `run'
	68: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/spring-2.1.0/lib/spring/client/command.rb:7:in `call'
	67: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/spring-2.1.0/lib/spring/client/rails.rb:28:in `call'
	66: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/spring-2.1.0/lib/spring/client/rails.rb:28:in `load'
	65: from /Users/afuno/Projects/my.app/bin/rails:11:in `<top (required)>'
	64: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.1/lib/active_support/dependencies.rb:325:in `require'
	63: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.1/lib/active_support/dependencies.rb:291:in `load_dependency'
	62: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.1/lib/active_support/dependencies.rb:325:in `block in require'
	61: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/bootsnap-1.4.5/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:30:in `require'
	60: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/bootsnap-1.4.5/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:21:in `require_with_bootsnap_lfi'
	59: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/bootsnap-1.4.5/lib/bootsnap/load_path_cache/loaded_features_index.rb:92:in `register'
	58: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/bootsnap-1.4.5/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:22:in `block in require_with_bootsnap_lfi'
	57: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/bootsnap-1.4.5/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:22:in `require'
	56: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/railties-6.0.1/lib/rails/commands.rb:18:in `<main>'
	55: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/railties-6.0.1/lib/rails/command.rb:46:in `invoke'
	54: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/railties-6.0.1/lib/rails/command/base.rb:65:in `perform'
	53: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/thor-0.20.3/lib/thor.rb:387:in `dispatch'
	52: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/thor-0.20.3/lib/thor/invocation.rb:126:in `invoke_command'
	51: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/thor-0.20.3/lib/thor/command.rb:27:in `run'
	50: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/railties-6.0.1/lib/rails/commands/server/server_command.rb:138:in `perform'
	49: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/railties-6.0.1/lib/rails/commands/server/server_command.rb:138:in `tap'
	48: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/railties-6.0.1/lib/rails/commands/server/server_command.rb:147:in `block in perform'
	47: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/railties-6.0.1/lib/rails/commands/server/server_command.rb:37:in `start'
	46: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/railties-6.0.1/lib/rails/commands/server/server_command.rb:77:in `log_to_stdout'
	45: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/rack-2.0.7/lib/rack/server.rb:354:in `wrapped_app'
	44: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/rack-2.0.7/lib/rack/server.rb:219:in `app'
	43: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/rack-2.0.7/lib/rack/server.rb:319:in `build_app_and_options_from_config'
	42: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/rack-2.0.7/lib/rack/builder.rb:40:in `parse_file'
	41: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/rack-2.0.7/lib/rack/builder.rb:49:in `new_from_string'
	40: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/rack-2.0.7/lib/rack/builder.rb:49:in `eval'
	39: from config.ru:in `<main>'
	38: from config.ru:in `new'
	37: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/rack-2.0.7/lib/rack/builder.rb:55:in `initialize'
	36: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/rack-2.0.7/lib/rack/builder.rb:55:in `instance_eval'
	35: from config.ru:5:in `block in <main>'
	34: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/bootsnap-1.4.5/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:48:in `require_relative'
	33: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.1/lib/active_support/dependencies.rb:325:in `require'
	32: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.1/lib/active_support/dependencies.rb:291:in `load_dependency'
	31: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.1/lib/active_support/dependencies.rb:325:in `block in require'
	30: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/zeitwerk-2.2.1/lib/zeitwerk/kernel.rb:23:in `require'
	29: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/bootsnap-1.4.5/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:30:in `require'
	28: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/bootsnap-1.4.5/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:21:in `require_with_bootsnap_lfi'
	27: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/bootsnap-1.4.5/lib/bootsnap/load_path_cache/loaded_features_index.rb:92:in `register'
	26: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/bootsnap-1.4.5/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:22:in `block in require_with_bootsnap_lfi'
	25: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/bootsnap-1.4.5/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:22:in `require'
	24: from /Users/afuno/Projects/my.app/config/environment.rb:7:in `<main>'
	23: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/railties-6.0.1/lib/rails/application.rb:363:in `initialize!'
	22: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/railties-6.0.1/lib/rails/initializable.rb:60:in `run_initializers'
	21: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/2.6.0/tsort.rb:205:in `tsort_each'
	20: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/2.6.0/tsort.rb:226:in `tsort_each'
	19: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/2.6.0/tsort.rb:347:in `each_strongly_connected_component'
	18: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/2.6.0/tsort.rb:347:in `call'
	17: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/2.6.0/tsort.rb:347:in `each'
	16: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/2.6.0/tsort.rb:349:in `block in each_strongly_connected_component'
	15: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/2.6.0/tsort.rb:431:in `each_strongly_connected_component_from'
	14: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/2.6.0/tsort.rb:350:in `block (2 levels) in each_strongly_connected_component'
	13: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/2.6.0/tsort.rb:228:in `block in tsort_each'
	12: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/railties-6.0.1/lib/rails/initializable.rb:61:in `block in run_initializers'
	11: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/railties-6.0.1/lib/rails/initializable.rb:32:in `run'
	10: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/railties-6.0.1/lib/rails/initializable.rb:32:in `instance_exec'
	 9: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/railties-6.0.1/lib/rails/application/finisher.rb:129:in `block in <module:Finisher>'
	 8: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.1/lib/active_support/lazy_load_hooks.rb:51:in `run_load_hooks'
	 7: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.1/lib/active_support/lazy_load_hooks.rb:51:in `each'
	 6: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.1/lib/active_support/lazy_load_hooks.rb:52:in `block in run_load_hooks'
	 5: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.1/lib/active_support/lazy_load_hooks.rb:67:in `execute_hook'
	 4: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.1/lib/active_support/lazy_load_hooks.rb:62:in `with_execution_control'
	 3: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.1/lib/active_support/lazy_load_hooks.rb:69:in `block in execute_hook'
	 2: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/actionview-component-1.5.0/lib/action_view/component/railtie.rb:52:in `block in <class:Railtie>'
	 1: from /Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/bootsnap-1.4.5/lib/bootsnap/load_path_cache/change_observer.rb:10:in `<<'
/Users/afuno/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/bootsnap-1.4.5/lib/bootsnap/load_path_cache/change_observer.rb:10:in `<<': can't modify frozen #<Class:#<Array:0x00007ff535a70be8>> (FrozenError)

Name conflict with ActionView for local variable "tag" in a component

In our application (rails 5.0.7.2, actionview-component: 1.5.2), we have a component which looks roughly as follows (simplified to highlight the issue):

class Tags::DeleteButtonComponent < ApplicationComponent
  def initialize(workspace:, tag:)
    @workspace = workspace
    @tag = tag
  end

  private
  attr_reader :workspace, :tag
end

View (haml):

= link_to tag_url(workspace, tag),
  class: 'btn btn-primary',
  method: :delete,
  data: { 'confirm-title' => 'Are you sure?',
  confirm: "This tag and all its data will be deleted." } do
  = image_tag 'icons/delete.svg', class: 'icon icon-tiny'
  Delete

Render (haml):

= render Tags::DeleteButtonComponent, workspace: @workspace, tag: @tag

With this setup, the call to image_tag in the view fails with:

wrong number of arguments (given 2, expected 0)

The stack trace points to this line in the image_tag method which calls a method named tag. Presumably, this being shadowed by our local variable named tag.

This is unexpected behaviour, since it does not happen when we use normal partials in the same way. I'll also add that if I instead call view_context.image_tag this doesn't happen. As you would expect, changing the name of the variable fixes it too.

Perhaps this is a rare edge case, and maybe we're better off with a different name anyway instead of clashing with the rails tag method. But maybe it points to something that could cause tests/views to break by unexpectedly shadowing rails internal method names.

Allow rendering components in the controller

Hi @joelhawksley

I am happy to see a view model lib, that integrates smoothly with rails. While playing around with ActionView::Component i noticed that it is not possible to render components in the controller. I saw, that you are monkey patching ActionView::Base#render. So i tried to override the render method in my ApplicationController in a similar way, but without success.

At the moment users are forced to write templates with a single line: <%= render(PostShowComponent, post: @post) %>. It would be great to have an API like this:

class PostsController < ApplicationController
  def show 
    post = Post.find(params[:id])
    render(PostShowComponent, post: post)
  end
end

I could provide a PR with a failing feature test, if that helps.

Thank you , ushi

P.S. Found a workaround. Maybe its a good starting point:

class ApplicationController
  private

  def render_component(component, args, **options)
    options = options.reverse_merge(layout: true)
    render(html: view_context.render(component, args), options)
  end 
end

Allow components to have multiple content areas

First of, this library looks awesome and I can't wait to start using it :)

Something that would be really nice is the ability for a component to have more than one output area, or "outlet" (for lack of a better term). Essentially, the idea is very similar to using yield(:foo) and content_for(:foo) in normal Rails views.

Often, the dynamic content within a component cannot be declared within a single block. One example is a modal window that has a title, body, and row of buttons:

image

This would be a really cool way to render this modal with a title (static string), body (block), and footer (block). The block could now be passed an instance of the component (or perhaps something like component builder) which could act to capture explicitly named blocks:

<%= render(ModalComponent, title: 'Are you sure?') do |modal| %>
  <%= modal.body do %>
    <p>This is some content that would go in the modal body.</p>
  <% end %>

  <%= modal.footer do %>
    <%= link_to 'Cancel', '#', class: 'btn btn-secondary', data: { dismiss: :modal } %>
    <%= button_to 'Confirm', confirmation_path, class: 'btn btn-primary' %>
  <% end %>
<% end %>

The component template might look something like this:

<!-- app/components/modal_component.html.erb -->
<div class="modal-dialog">
  <div class="modal-content">
    <div class="modal-header">
      <h4 class="modal-title"><%= title %></h4>
    </div>

    <div class="modal-body">
      <%= body %>
    </div>

    <div class="modal-footer">
      <%= footer %>
    </div>
  </div>
</div>

I think this would make components far more flexible, and allow us to write less repetitive markup when using the components. Without something like this, the developer must remember to include <div class="modal-body"> and <div class="modal-footer"> whenever the component is used:

<%= render(ModalComponent, title: 'Are you sure?') do %>
  <div class="modal-body">
    <p>This is some content that would go in the modal body.</p>
  </div>

  <div class="modal-footer">
    <%= link_to 'Cancel', '#', class: 'btn btn-secondary', data: { dismiss: :modal } %>
    <%= button_to 'Confirm', confirmation_path, class: 'btn btn-primary' %>
  </div>
<% end %>

The need to wrap content in these classes feels like an implementation detail that should be the responsibility of the component itself.

Now, this one occurrence might not seem so bad. But imagine this pattern repeated many, many places in the application. Developers must remember to mark up the content within the block in a specific way (thereby "leaking" some of the internals of the component), which can result in mistakes and make the code more difficult to maintain.

If there's support for something like this, I think the next step would be to discuss the API and syntax for outlets. I'd be happy to hack on this if there's interest!

ArgumentError - Missing host to link to on *_url helpers

The following exception occurs when trying to use a *_url-style route helper:

ArgumentError - Missing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to true:

This occurs when the Rails.application.routes.url_helpers has been included in a component and you try to use any sort of *_url method, like root_url or similar.

Unable to render forms/button_to from a component

Originally posted in #15, but also a problem for me:

Rendering a form/button_to from a component raises an error undefined method 'protect_against_forgery?' for #Happening::SignUpButton:0x00007f893937e7f8

we'd also need form_authenticity_token

Problems after upgrading to version 1.3.0

I used version 1.2.1. Everything was fine. Today I upgraded to version 1.3.0. And part of my components broke.

For example, there is such a component:

class ParagraphComponent < ActionView::Component::Base
  validates :content, presence: true

  def initialize(html: {})
    @html = html
  end

  private

  def additional_classes
    return '' if @html.empty?
    return '' if @html[:class].blank?

    @html[:class]
  end

  def classes
    [
      'm:text-18 m:leading-2',
      't:text-24 t:leading-1.5',
      'd:text-24 d:leading-1.5',
      additional_classes
    ].join(' ')
  end
end

And the template:

= content_tag :div, class: classes
  = content

Next, I use this component, for example, this way:

= render(ParagraphComponent, html: { class: 'flex flex-row mb-6' })
  = content_tag :div, class: 'mr-20'
    | First
  = content_tag :div
    | Second

As a result, in the code of the source page, I do not see the classes from the component. That is, I immediately see the code from the content variable.

In general, all the HTML code on the page (including Vue components) does not behave appropriately. HTML code breaks. Tags (elements, nodes) are simply incorrect.

The problem is precisely related to your gem. If I write the following in Gemfile:

gem 'actionview-component', '1.2.1'

Then there are no more problems. Everything works fine again.

But as soon as I upgrade to version 1.3.0, everything breaks down again.

I have never seen anything like this before.

Want to use Action Pack Variants.

If variants can be used, more uses.

for example

app/components/article_component.html.erb
app/components/article_component.html+amp.erb
app/components/article_component.html+sp.erb
app/components/article_component.rb

Currently only one view is supported.

Really please.

not an ActiveModel-compatible object. It must implement :to_partial_path.

Freshly installed actionview-component (version 1.3.5) on rails version 6.0 with the following setup...

config/application.rb

# snip...
require "action_view/component/base"
# snip...

app/components/application_component.rb

class ApplicationComponent < ActionView::Component::Base
end

app/components/example_component/example_component.rb

class ExampleComponent < ApplicationComponent
  validates :content, presence: true

  def initialize(message:)
    @message = message
  end

  attr_reader :message
end

app/components/example_component/example_component.slim

div
  = content
  = message

and in a view file...

body
  h2
    | Example Component:
  = render(ExampleComponent, message: "Hello, World!") do
    | This is an example component...

Results in....

'ExampleComponent' is not an ActiveModel-compatible object. It must implement :to_partial_path.

Any tips on what I might be missing here?

Lower ruby requirement to match Rails 5

Hey! Love what you're doing here!

We're testing this out in our app (running Rails 5) and I'm wondering if you would consider lowering the ruby requirement? Right now it's set to 2.5 + (which is great for Rails 6 but leaves out us Rails 5 folks who aren't as up-to-date).

I've been running this with Ruby 2.4 and works fine. Rails 5 has the requirement of ruby 2.2 or higher.

Happy to open a PR if you think it's a good idea!

Allow testing a "shallow" render

Currently it is challenging to test a component without rendering it deeply.
One thing that makes this challenging is the lack of something like rspec's stub_template method
It's also impossible to mock render on the controller.view_context as this is a new instance on every call.

I've found this workaround, but it's rather unwieldy:

  let(:instance) { MyComponent.new(my_params: 'val') }

  before do
    allow(MyComponent).to receive(:new).and_return(instance)
    allow(instance).to receive(:render)
  end

  it "calls render" do
    render_inline(MyComponent, my_params: 'val')
    expect(instance).to have_received(:render).with('catalog/facet_group', param1: 'foo')
  end

Best practice for CSS?

I have a component structure like so:

- /app
  - /components
    - /alert
      - alert.css
      - alert.html.erb
      - alert.rb

I'm importing the CSS file in Webpacker's application.css.

Unfortunately this setup gives me the error:

More than one template found for Alert. There can only be one sidecar template file per component.

I'm currently working around this by using _alert.css instead of alert.css, but it seems odd actionview-component is treating this CSS file as a template file?

Is there a best practice here I should be following? Or is this an issue actionview-component should fix?

Include test case class for components

Here is what I'm currently using on my test_helper.rb file.

class ActionView::ComponentTest < ActiveSupport::TestCase
  include ActionView::Component::TestHelpers
end

I think it's a good idea to provide this base for class for testing components.

[1.3.4] private method `controller' called

In version 1.3.3, everything was fine. There was a problem after upgrading to version 1.3.4. I checked - the problem is really in the new version.

One of my components started to throw this error:

private method `controller' called for #<My::Component::NameComponent:0x00007f8520434b10>
Did you mean?  controller=

The component has input values.

Allow rendering collection of components

When rendering jbuilder partials, the following can be used instead of explicitly writing a loop that renders each partial:

json.partial! "posts/post", collection: @posts, as: :post

It would be interesting if we could have something similar for components. The example below assumes a PostComponent is defined.

# posts_controller.rb

class PostsController < ApplicationController
  def index
    @posts = Post.scope_that_queries_the_right_posts(params)
  end
end

# app/views/posts/index.html.erb

<%= render @posts %>

In this case, for each record we would get the associated component and render it. I'd love some feedback and implementations ideas. I can work on some implementation suggestions.

Add 'strict' mode

It could be useful to have a flag/mode for components that disables access to the global Rails context.

Doing so would allow us to write "stateless" / "context-free" components that can be more confidently reused, especially outside of the request path.

1.3.3 release broken?

we had to step down to 1.3.1 release because our templates are not found on 1.3.3. our developer traced this to the following

in version 1.3.1. we have this:

sibling_files = Dir["#{filename_without_extension}."] - [filename]
It will find the files in the path: XYZ/app/components/page/paginate.

THIS IS WORKING, we have a file pagina.slim in the folder

but in 1.3.3 we have this

sibling_template_files = Dir["#{filename_without_extension}.????.{#{ActionView::Template.template_handler_extensions.join(',')}}"] - [filename]

it will find the files in the path: XYZ/app/components/page/paginate.????{arb,builder,coffee,erb,html,jbuilder,raw,ruby,slim}

THIS IS NOT WORKING, it does not find the file paginate.slim anymore

we are following the naming convention to not have .html.slim similar to the normal recommendations in rails and have .slim (and this was working)

related to these commits

fbbeab1#diff-4ea3d395eff742fa0d8952d23a16d377

fbbeab1#diff-4ea3d395eff742fa0d8952d23a16d377

Deprecation Warning

DEPRECATION WARNING: passing component instances to render has been deprecated and will be removed in v2.0.0. Use render MyComponent, foo: :bar

In view:

<%= render CardProfile::CardProfileComponent, influencer: @influencer, metadata: false do %>
   <h1>Super title</h1>
<% end %>

Depreciation is telling me to change render method to render. You might show a complete example?
Thx

[Contributing] How to run test app from the gem?

How are you guys running the test app from test/?

I've been using RAILS_ENV=test rackup but it's not working now, due to the error:

bundler: failed to load command: rackup (/Users/juanmanuel/.gem/ruby/2.6.5/bin/rackup)
NameError: uninitialized constant ActionView::Component::RenderMonkeyPatch
  /Users/juanmanuel/Desktop/Coding/actionview-component/lib/action_view/component/railtie.rb:43:in `block (2 levels) in <class:Railtie>'

If I comment out that line, the app starts correctly but whenever I visit localhost:9292 then I get

uninitialized constant ActionView::Component::Base

I don't know what's causing this, maybe some of you might know what's going on?
Or do you have a better way to run the app and check everything is working ok? (apart from running the tests ofc ๐Ÿ™ˆ)

ActionView::Component previews

It struck me; AV::Components ought to have previews the same way ActionMailer does.

I realise this might be overkill for a 1.0 release but I'll be happy to throw a first draft together if you folks think it's as good an idea as I do.

Otherwise I might just make it as a gem on its own.

render_inline produces bad output for some templates

The following line in the test helpers produces incorrect results depending on the template being rendered:

https://github.com/github/actionview-component/blob/99fc8dd285cf98f3121fff45f590d6be5da550bc/lib/action_view/component/test_helpers.rb#L7

This isn't a consistent thing though -- it chokes on certain types of input. Consider the following:

Nokogiri::HTML('<script>alert("OK")</script>').css('body > *')

This returns an empty string. This will similarly produce empty strings depending on the input -- using noframes, frame, various other elements will all produce weird input. Here's some other fun results:

Nokogiri::HTML("<script>alert</script><b>ok</b>").css("body > *").to_s
# => "<b>ok</b>"
Nokogiri::HTML("<script>alert</script>").css("body > *").to_s
# => ""
Nokogiri::HTML("hello world").css("body > *").to_s
# => "<p>hello world</p>"

I'd recommend doing the following instead perhaps:

Nokogiri::HTML.fragment(controller.view_context.render(component, args, &block))

Which produces the expected result of all of the above examples:

Nokogiri::HTML.fragment("<script>alert</script><b>ok</b>").to_s
# => "<script>alert</script><b>ok</b>"
Nokogiri::HTML.fragment("<script>alert</script>").to_s
# => "<script>alert</script>"
Nokogiri::HTML.fragment("hello world").to_s
# => "hello world"

Should variants fall back to default template?

Thanks a lot for the amazing work! I'm really happy to be using this gem, it's bringing some much needed clarity and testability to my views ๐Ÿ™‚

I'm finding the newly implemented variants logic a bit surprising though. Notably the :variant_exists validation, which forces me to add templates for every variant.

ActionPack::Variants simply falls back to the default template, in case there is no template defined for the variant and I find this to be a nicer way to go about it as it's a more flexible approach, allowing you to define variant templates only for the components that need it.

What are your thoughts? I'm very open to submitting a PR, but wanted to hear you out first ๐Ÿ™‚

Better error backtrace

Motivated by #15 here's one report.

Definitely a nice to have, not entirely important, but would be great to have it.

Steps to reproduce

  1. Render a component on a page
  2. Use an inexistent image on the component view

Expected

Action::View::Template::Error is raised indicating the line in the component view file where the error was raised.

Actual

Action::View::Template::Error is raised indicating the line in the view file where I rendered the component.

Example

app[web.1]: Completed 500 Internal Server Error in 100ms (ActiveRecord: 44.5ms)
app[web.1]: DEBUG -- : The asset "landing_pages/linkedin-icon.png" is not present in the asset pipeline.
app[web.1]: FATAL -- : ActionView::Template::Error (The asset "landing_pages/linkedin-icon.png" is not present in the asset pipeline.):
app[web.1]: FATAL -- : 
10: [redacted]
11: [redacted]
12:   <% else %>
13:     <%= render page_component.component, page_component.locals %>
14:   <% end %>
15: <% end %>
app[web.1]: FATAL -- : app/views/marketing/pages/show.html.erb:13:in `block in _app_views_marketing_pages_show_html_erb___4484640677596836862_47259959656180'

relative i18n paths

i'm getting an error that relative I18n paths are not available (Cannot use t(".register") shortcut because path is not available). Is there a know 'workaround' ?

Compile components on application boot

In production, it would probably make sense to compile all descendents of ActionView::Component::Base, so that components render quickly on the first request, instead of compiling lazily as they do now.

[1.3.6] Could not find a template file for BodyComponent

For version 1.3.5 everything is fine. The problem appeared in version 1.3.6. Rails 6.0.0.


I have a simple component. Only content is transferred to it. After upgrading to version 1.3.6, I started getting the following error:

NotImplementedError - Could not find a template file for BodyComponent.

Initializer crashes when eager loading is enabled

When eager loading is enabled, one of the initializers:
https://github.com/github/actionview-component/blob/1aad6f4b9ccdd87c15491d31785dd1c1da5b992e/lib/action_view/component/railtie.rb#L44-L48
crashes with an error:

5: from /activesupport-6.0.1/lib/active_support/lazy_load_hooks.rb:67:in `execute_hook'
4: from /activesupport-6.0.1/lib/active_support/lazy_load_hooks.rb:62:in `with_execution_control'
3: from /activesupport-6.0.1/lib/active_support/lazy_load_hooks.rb:74:in `block in execute_hook'
2: from /activesupport-6.0.1/lib/active_support/lazy_load_hooks.rb:74:in `instance_eval'
1: from /actionview-component-1.5.1/lib/action_view/component/railtie.rb:46:in `block (2 levels) in <class:Railtie>'
/actionview-component-1.5.1/lib/action_view/component/railtie.rb:46:in `each': undefined method `action_methods' for EagerComponent:Class (NoMethodError)

It makes sense, since #action_methods isn't implemented on ActionView::Component::Base.

Can you shed some light on this, @juanmanuelramallo? Was the intention to mimic the caching in AbstractController? Thanks ๐Ÿ™Œ

Unable to mock methods on the view config

It appears the view config is different:

   34:     before do
   35:       byebug
=> 36:       allow(controller.view_context).to receive_messages(facet_group_names: [nil], facet_field_names: [:facet_field_1], facet_limit_for: 10)
   37:     end
(byebug) controller.view_context.object_id
70206343712180
(byebug) c
[1, 10] in /Users/jcoyne85/workspace/projectblacklight/blacklight/app/views/catalog/_facet_group.html.erb
    1: <% # main container for facets/limits menu -%>
    2: <% byebug %>
=>  3: <% if has_facet_values? facet_field_names(groupname), response %>
(byebug) self.object_id
70206343979280

So the mocks are never called.

This may be because my component calls a regular view render.

e.g.:
app/components/list.html.erb:

<% # container for facet groups -%>
<% facet_group_names.each do |groupname| %>
  <%= render 'catalog/facet_group', groupname: groupname, response: response %>
<% end %>

1.3.5 release resolves paths wrongly?

sorry ... duplicate from previous ticket ... seems the framework still is expecting .html.slim extension and not .slim extension (which is painful because in other places we actually have issues with .html.slim as extension where we need to make it .slim)

Expose controller context in components

Per @metade's comment, let's look at exposing the controller context inside components.

It might be worth considering making this something that can be disabled on a per-component basis (without_controller_context macro perhaps?), as coupling components to controllers makes them trickier to test and more difficult to use outside of a request.

@metade if you're still up for this, I'd welcome a PR! If nothing else, perhaps one with some failing test cases for us to look at?

image_url and image_tag ignoring configured asset_host value

When I call image_url("some_image.png") directly in my views, I am getting a relative path vs a full asset url (which in our case is a CDN).

If I call view_context.image_url("some_image.png") it works fine and generates the full url.

Similar issues for image_tag which delegates to the same method.

I have temporarily added a delegate :image_url, :image_tag, to: :view_context in my component, but this feels wrong.

Is there something I am missing about how to configure this, or is this an actual bug?

simple path helpers not working, need explicit id passing

we are implementing the current release into our project, which we recently have brought to rails 6. path helpers require for some reason that you explicitly pass the id, so we need to change edit_instrument_path(instrument) into edit_instrument_path(id: instrument.id).

this feels like unintended / a bug. any fixes?

Nesting components?

Hi!

Good work. I was wondering if it would be possible to maybe nest components to? Sometime we want to create component trees in DSL like fashion for quick admins like ActiveAdmin for instance.

Is that a use case that can be covered with this? A bit similar to how react itself allow you to build trees.

Table.new do |t|
  t.columns << TableColumn.new(name:"Hello")
end

And then

<%=render table%>

Or is this totally out of scope? Which I could also understand, but any brainstorm/rubberduck would be appreciated.

Version 1.5.2 on Rails 6.0.1 causing deprecation warning

Rails 6.0.1
Gem version 1.5.2

In config/application.rb,
require "action_view/component"

Causing:

DEPRECATION WARNING: Initialization autoloaded the constants ActionText::ContentHelper and ActionText::TagHelper.

Being able to do this is deprecated. Autoloading during initialization is going
to be an error condition in future versions of Rails.

Reloading does not reboot the application, and therefore code executed during
initialization does not run again. So, if you reload ActionText::ContentHelper, for example,
the expected changes won't be reflected in that stale Module object.

These autoloaded constants have been unloaded.

Please, check the "Autoloading and Reloading Constants" guide for solutions.
 (called from <main> at /Users/
USER/Projects/rails-app-test/config/environment.rb:5)

at development.log

Which is highly difficult to track

Related issue rails/rails#36546

Make the controller instance in a test case be accessible.

In my component I'm using the cancancan gem. This defines a controller method can? in the controller context. So my component does:

  delegate :can?, to: :controller

In my test, I'd love to mock out the controller.can? method, but I don't think ActionView::Component::TestHelpers gives a way to get the controller instance. Is it possible to expose that?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.