viewcomponent / view_component Goto Github PK
View Code? Open in Web Editor NEWA framework for building reusable, testable & encapsulated view components in Ruby on Rails.
Home Page: https://viewcomponent.org
License: MIT License
A framework for building reusable, testable & encapsulated view components in Ruby on Rails.
Home Page: https://viewcomponent.org
License: MIT License
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)
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.
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
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:
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!
It would be wonderful if we could support the helpers
syntax in components, much like @rafaelfranca added for controllers in rails/rails#24866.
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.
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
Before I start playing around with this, I wanted to ask if there is a preferred way, best practice, or even a helper to render components for collections similar to the rails guide for partials
I think this would be a common use case, and would be willing to submit a PR for a documentation example.
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.
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.
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?
Per https://blog.saeloun.com/2019/09/17/ruby-2-7-module-const-source-location.html, we should be able to remove the requirement of having initializers in Ruby 2.7.
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!
In #149, I had to add a temporary patch to prevent better_html
from breaking on component render calls.
Let's revisit this patch and come up with a more robust solution.
cc @EiNSTeiN-
Presently they are.
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
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?
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.
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.
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.
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.
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
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
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 ๐)
Is there a way to access controller helper methods? (The ones created like this https://api.rubyonrails.org/classes/AbstractController/Helpers/ClassMethods.html#method-i-helper_method)
We may want to use controller helper methods like Devise's ones (current_user
, user_signed_in?
), or any other helper method defined by the application.
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.
The following line in the test helpers produces incorrect results depending on the template being rendered:
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"
๐ hello folks!
As we look towards upstreaming our work on actionview-component
into Rails, it feels like it might be time to think about working towards a major release.
I've added the v2
label to issues we think would make sense to get in ahead of this release: https://github.com/github/actionview-component/issues?q=is%3Aissue+is%3Aopen+label%3Av2
I'd love to hear what you think we should be sure to include in V2 โค๏ธ
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 ๐
It would be great to have some form of integration-level benchmark script that we can use to make sure changes to this library are not slowing things down.
I've used https://github.com/evanphx/benchmark-ips in the past, but am open to other options as well.
Motivated by #15 here's one report.
Definitely a nice to have, not entirely important, but would be great to have it.
Action::View::Template::Error is raised indicating the line in the component view file where the error was raised.
Action::View::Template::Error is raised indicating the line in the view file where I rendered the component.
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'
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' ?
@vinistock I heard you might have made some progress on this. Would you be interested in sharing what you have so far?
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.
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.
#106 introduced a generator for components. Might we update the main README to incorporate this new ability?
We should remove this line on the next major release:
require "action_view/component/railtie"
Per @inopinatus' comment, it probably makes sense to match the Rails convention around naming jobs/mailers/controllers and have component class names end in Component
.
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 ๐
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 %>
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)
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?
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?
We have two cases where we support old syntax in actionview-component
:
Let's remove these cases and the associated tests, merging right before we cut v2.0.0
.
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?
Per @inopinatus's comment, let's look at supporting render @model
, where @model
is an object that implements to_component_class
.
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.
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
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?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.