Strange tab behaviour in avo 3.x
Describe the bug
In avo 3.x tabs behave differently to avo 2.x. I've found two related bugs:
- If you define tabs inside the fields method of a resource, the breadcrumbs and actions are not shown on the show and edit views.
- If you define the tabs inside a
main_panelblock, the actions and breadcrumbs will appear (on show and view) buthas_manyfields raise an error (see below).
On avo 2.x main_panel wasn't necessary to make actions and breadcrumbs appear. I've only mentioned it because I found that (as a hack) that makes tabs usable... except when you have a has_many field.
Steps to Reproduce
Steps to reproduce the behavior:
- Inside the fields method, put field definitions inside a tabs block, like this
tabs { tab "foo" { field :bar, as: :text } } - Load up avo and go to the show view for a resource
- The actions and breadcrumbs no longer appear.
Expected behavior & Actual behavior
The actions and breadcrumbs should appear the same as they do for the same resource without tabs.
Models and resource files
I've made a sample app that reproduces the issue as described, both with and without a main_panel block.
https://github.com/iainbeeston/avo-tabs-test
System configuration
Avo version: 3.3.3
Rails version: 7.0.8
Ruby version: 3.2.1
License type:
- [x] Community
- [ ] Pro
- [ ] Advanced
Are you using Avo monkey patches, overriding views or view components?
- [ ] Yes. If so, please post code samples.
- [x] No
Screenshots or screen recordings
This is what it looks like if I use tabs without a main panel block.
This is what it looks like if I use tabs inside a main panel block.
Additional context
This is the backtrace of the error (when using tabs inside a main panel):
NoMethodError (undefined method `use_resource' for nil:NilClass):
avo (3.3.3) app/controllers/avo/associations_controller.rb:103:in `set_attachment_resource'
activesupport (7.0.8) lib/active_support/callbacks.rb:400:in `block in make_lambda'
activesupport (7.0.8) lib/active_support/callbacks.rb:180:in `block (2 levels) in halting_and_conditional'
actionpack (7.0.8) lib/abstract_controller/callbacks.rb:34:in `block (2 levels) in <module:Callbacks>'
activesupport (7.0.8) lib/active_support/callbacks.rb:181:in `block in halting_and_conditional'
activesupport (7.0.8) lib/active_support/callbacks.rb:595:in `block in invoke_before'
activesupport (7.0.8) lib/active_support/callbacks.rb:595:in `each'
activesupport (7.0.8) lib/active_support/callbacks.rb:595:in `invoke_before'
activesupport (7.0.8) lib/active_support/callbacks.rb:116:in `block in run_callbacks'
i18n (1.14.1) lib/i18n.rb:322:in `with_locale'
avo (3.3.3) app/controllers/avo/application_controller.rb:263:in `set_avo_locale'
activesupport (7.0.8) lib/active_support/callbacks.rb:127:in `block in run_callbacks'
actiontext (7.0.8) lib/action_text/rendering.rb:20:in `with_renderer'
actiontext (7.0.8) lib/action_text/engine.rb:69:in `block (4 levels) in <class:Engine>'
activesupport (7.0.8) lib/active_support/callbacks.rb:127:in `instance_exec'
activesupport (7.0.8) lib/active_support/callbacks.rb:127:in `block in run_callbacks'
activesupport (7.0.8) lib/active_support/callbacks.rb:138:in `run_callbacks'
actionpack (7.0.8) lib/abstract_controller/callbacks.rb:233:in `process_action'
actionpack (7.0.8) lib/action_controller/metal/rescue.rb:23:in `process_action'
actionpack (7.0.8) lib/action_controller/metal/instrumentation.rb:67:in `block in process_action'
activesupport (7.0.8) lib/active_support/notifications.rb:206:in `block in instrument'
activesupport (7.0.8) lib/active_support/notifications/instrumenter.rb:24:in `instrument'
activesupport (7.0.8) lib/active_support/notifications.rb:206:in `instrument'
actionpack (7.0.8) lib/action_controller/metal/instrumentation.rb:66:in `process_action'
actionpack (7.0.8) lib/action_controller/metal/params_wrapper.rb:259:in `process_action'
activerecord (7.0.8) lib/active_record/railties/controller_runtime.rb:27:in `process_action'
actionpack (7.0.8) lib/abstract_controller/base.rb:151:in `process'
actionview (7.0.8) lib/action_view/rendering.rb:39:in `process'
actionpack (7.0.8) lib/action_controller/metal.rb:188:in `dispatch'
actionpack (7.0.8) lib/action_controller/metal.rb:251:in `dispatch'
actionpack (7.0.8) lib/action_dispatch/routing/route_set.rb:49:in `dispatch'
actionpack (7.0.8) lib/action_dispatch/routing/route_set.rb:32:in `serve'
actionpack (7.0.8) lib/action_dispatch/journey/router.rb:50:in `block in serve'
actionpack (7.0.8) lib/action_dispatch/journey/router.rb:32:in `each'
actionpack (7.0.8) lib/action_dispatch/journey/router.rb:32:in `serve'
actionpack (7.0.8) lib/action_dispatch/routing/route_set.rb:852:in `call'
railties (7.0.8) lib/rails/engine.rb:530:in `call'
railties (7.0.8) lib/rails/railtie.rb:226:in `public_send'
railties (7.0.8) lib/rails/railtie.rb:226:in `method_missing'
actionpack (7.0.8) lib/action_dispatch/routing/mapper.rb:19:in `block in <class:Constraints>'
actionpack (7.0.8) lib/action_dispatch/routing/mapper.rb:48:in `serve'
actionpack (7.0.8) lib/action_dispatch/journey/router.rb:50:in `block in serve'
actionpack (7.0.8) lib/action_dispatch/journey/router.rb:32:in `each'
actionpack (7.0.8) lib/action_dispatch/journey/router.rb:32:in `serve'
actionpack (7.0.8) lib/action_dispatch/routing/route_set.rb:852:in `call'
actionpack (7.0.8) lib/action_dispatch/middleware/static.rb:23:in `call'
rack (2.2.8) lib/rack/static.rb:161:in `call'
rack (2.2.8) lib/rack/tempfile_reaper.rb:15:in `call'
rack (2.2.8) lib/rack/etag.rb:27:in `call'
rack (2.2.8) lib/rack/conditional_get.rb:27:in `call'
rack (2.2.8) lib/rack/head.rb:12:in `call'
actionpack (7.0.8) lib/action_dispatch/http/permissions_policy.rb:38:in `call'
actionpack (7.0.8) lib/action_dispatch/http/content_security_policy.rb:36:in `call'
rack (2.2.8) lib/rack/session/abstract/id.rb:266:in `context'
rack (2.2.8) lib/rack/session/abstract/id.rb:260:in `call'
actionpack (7.0.8) lib/action_dispatch/middleware/cookies.rb:704:in `call'
activerecord (7.0.8) lib/active_record/migration.rb:638:in `call'
actionpack (7.0.8) lib/action_dispatch/middleware/callbacks.rb:27:in `block in call'
activesupport (7.0.8) lib/active_support/callbacks.rb:99:in `run_callbacks'
actionpack (7.0.8) lib/action_dispatch/middleware/callbacks.rb:26:in `call'
actionpack (7.0.8) lib/action_dispatch/middleware/executor.rb:14:in `call'
actionpack (7.0.8) lib/action_dispatch/middleware/actionable_exceptions.rb:17:in `call'
actionpack (7.0.8) lib/action_dispatch/middleware/debug_exceptions.rb:28:in `call'
web-console (4.2.1) lib/web_console/middleware.rb:132:in `call_app'
web-console (4.2.1) lib/web_console/middleware.rb:28:in `block in call'
web-console (4.2.1) lib/web_console/middleware.rb:17:in `catch'
web-console (4.2.1) lib/web_console/middleware.rb:17:in `call'
actionpack (7.0.8) lib/action_dispatch/middleware/show_exceptions.rb:29:in `call'
railties (7.0.8) lib/rails/rack/logger.rb:40:in `call_app'
railties (7.0.8) lib/rails/rack/logger.rb:25:in `block in call'
activesupport (7.0.8) lib/active_support/tagged_logging.rb:99:in `block in tagged'
activesupport (7.0.8) lib/active_support/tagged_logging.rb:37:in `tagged'
activesupport (7.0.8) lib/active_support/tagged_logging.rb:99:in `tagged'
railties (7.0.8) lib/rails/rack/logger.rb:25:in `call'
sprockets-rails (3.4.2) lib/sprockets/rails/quiet_assets.rb:13:in `call'
actionpack (7.0.8) lib/action_dispatch/middleware/remote_ip.rb:93:in `call'
actionpack (7.0.8) lib/action_dispatch/middleware/request_id.rb:26:in `call'
rack (2.2.8) lib/rack/method_override.rb:24:in `call'
rack (2.2.8) lib/rack/runtime.rb:22:in `call'
activesupport (7.0.8) lib/active_support/cache/strategy/local_cache_middleware.rb:29:in `call'
actionpack (7.0.8) lib/action_dispatch/middleware/server_timing.rb:61:in `block in call'
actionpack (7.0.8) lib/action_dispatch/middleware/server_timing.rb:26:in `collect_events'
actionpack (7.0.8) lib/action_dispatch/middleware/server_timing.rb:60:in `call'
actionpack (7.0.8) lib/action_dispatch/middleware/executor.rb:14:in `call'
actionpack (7.0.8) lib/action_dispatch/middleware/static.rb:23:in `call'
rack (2.2.8) lib/rack/sendfile.rb:110:in `call'
actionpack (7.0.8) lib/action_dispatch/middleware/host_authorization.rb:138:in `call'
railties (7.0.8) lib/rails/engine.rb:530:in `call'
puma (6.4.2) lib/puma/configuration.rb:272:in `call'
puma (6.4.2) lib/puma/request.rb:100:in `block in handle_request'
puma (6.4.2) lib/puma/thread_pool.rb:378:in `with_force_shutdown'
puma (6.4.2) lib/puma/request.rb:99:in `handle_request'
puma (6.4.2) lib/puma/server.rb:464:in `process_client'
puma (6.4.2) lib/puma/server.rb:245:in `block in run'
puma (6.4.2) lib/puma/thread_pool.rb:155:in `block in spawn_thread'
Impact
- [x] High impact (It makes my app un-usable.)
- [ ] Medium impact (I'm annoyed, but I'll live.)
- [ ] Low impact (It's really a tiny thing that I could live with.)
I can't upgrade from avo 2.x to 3.x because of this issue.
Urgency
- [x] High urgency (I can't continue development without it.)
- [ ] Medium urgency (I found a workaround, but I'd love to have it fixed.)
- [ ] Low urgency (It can wait. I just wanted you to know about it.)
We have a similar issue that appeared when we upgraded from 3.0.1.beta19 to 3.1.0.
Upon upgrading, the actions disappeared on views where:
- no
main_panelblock is defined, and - no top-level
fields are visible
In these views, there are panel blocks containing visible fields. I would expect the actions to render since there are visible fields on the page, but they don't.
Minimal reproducible example (actions will not show):
class Avo::Resources::ExampleResource < Avo::BaseResource
def fields
panel name: 'Panel' do
field :id, as: :text
end
end
end
Adding a main_panel block definition the fields block will cause the actions to show again.
Minimal reproducible example (actions will show):
class Avo::Resources::ExampleResource < Avo::BaseResource
def fields
main_panel {}
panel name: 'Panel' do
field :id, as: :text
end
end
end
We also found related unexpected behaviour with fields defined by blocks. If the block returns nil, but the field is "visible" from Avo's point of view (based on the visibility attributes, such as show_on), the actions are rendered even though the field is not. I would expect the actions not to render in this case.
Minimal reproducible example (actions will show):
class Avo::Resources::ExampleResource < Avo::BaseResource
def fields
field :id, as: :text do
nil
end
end
end
I'm also hitting this issue. Sorry I have no reproduction right now, just wanted to add my +1 to this and explain where i'm experiencing it.
I have a has_many field showing in a tab. It works fine but if I wrapp my fields in a main_panel so I can add a sidebar, I get the same error.
Without the main_panel it works perfectly.
I recommend to start with a 5 minutes read of this docs page https://docs.avohq.io/3.0/resource-panels.html#resource-panels.
The Main Panel is the primary container for fields in a resource. It typically includes the resource's title, action buttons, and fields that are part of the resource's core data.
The Main Panel is automatically created by Avo based on your resource's field definitions.
There is this section that explains how do avo compute panels.
Basically the main_panel is computed and build by standalone fields (fields without panel).
Since associations fields and tabs do have their own panel and your resources have only panel-full items the main_panel is computed as empty so is not rendered and this behavior is intentional.
If you have a resource that computes the main_panel as empty and the "header" do not appear you can force it to be rendered by declaring main_panel do end wherever in your resource, before other panels, between them or even after them.
You may be thinking, why?
This allow so much more resource customization like:
def fields
tool Avo::ResourceTools::UserTool
main_panel do end
tabs id: :second_tabs_group do
field :post, as: :has_one, name: "Main post"
field :posts, as: :has_many, show_on: :edit
field :comments, as: :has_many
field :comment, as: :has_one, name: "Main comment"
end
end
OR
def fields
panel do
field :email, as: :gravatar, link_to_record: true, as_avatar: :circle
field :birthday, as: :date
field :heading, as: :heading, label: ""
field :active, as: :boolean, name: "Is active"
sidebar panel_wrapper: false do
tool Avo::ResourceTools::SidebarTool, render_panel: true
end
end
tabs id: :second_tabs_group do
field :post, as: :has_one, name: "Main post"
field :posts, as: :has_many, show_on: :edit
field :comments, as: :has_many
field :comment, as: :has_one, name: "Main comment"
end
main_panel do end
end
Tabs have their own independent panel (https://docs.avohq.io/3.0/tabs.html) and should not be defined inside a panel or main_panel that's why you're experiencing some errors. Try this instead:
def fields
main_panel do end
tabs { tab "foo" { field :bar, as: :text } }
end
OR
def fields
tabs { tab "foo" { field :bar, as: :text } }
main_panel do end
end
Let us know if there is something unclear or missing from the panel / tabs docs.
Thanks @Paul-Bob that's a really thorough explanation and that's allowed me to fix the issue in my own app. I do have two questions about your answer though:
Tabs have their own independent panel (https://docs.avohq.io/3.0/tabs.html) and should not be defined inside a panel or main_panel that's why you're experiencing some errors.
That makes sense, but has_many fields also have their own independent panel and can be defined inside tabs. Why doesn't defining a has_many (panel) inside a tab (panel) cause a problem?
Also, if this is expected behaviour, should avo throw an error if you try to define a tab inside a panel? (Or every time you try to define a panel inside another panel) That would really help users to figure this out.
This issue has been marked as stale because there was no activity for the past 15 days.
Closing this because there was no activity for the past 15 days. Feel free to reopen if new information pops up ✌️