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

Allow slots to be rendered by default #614

Closed
manuelpuyol opened this issue Feb 10, 2021 · 10 comments
Closed

Allow slots to be rendered by default #614

manuelpuyol opened this issue Feb 10, 2021 · 10 comments
Labels
help wanted slots Related to the Slots feature

Comments

@manuelpuyol
Copy link
Contributor

Feature request

It'd be helpful to allow some slots to be rendered even when they aren't explicitly called in the template.
e.g:

rb:

class MyComponent < ViewComponent::Base
  renders_one :slot, required: true, "RequiredSlot"
  renders_one :other_slot, "NotRequiredSlot"

  class RequiredSlot < ViewComponent::Base
  end

  class NotRequiredSlot < ViewComponent::Base
  end
end

erb:

<div class="slot">
  <%= slot %>
</div>
<%= other_slot %>

call:

<%= render MyComponent.new %>

This should render slot but not other_slot

Motivation

Some components always have to render a slot, so the burden goes to the user to make sure to always call the required slot in the call block. Since most of the times they will use the default arguments for the slot, the explicit call could be removed.

Current workaround

We can use before_render to reach this behavior:

def before_render
  slot(args...) unless slot
end
@hjhart
Copy link
Contributor

hjhart commented Nov 18, 2021

Does anyone else have any other strategies for this? I suppose we could create an abstraction that does the same thing that @manuelpuyol, but this seems like a common enough use-case that we could have some documentation surrounding it in the Slots docs.

cc @joelhawksley

@boardfish
Copy link
Collaborator

I think it's also possible to roll with <%= slot || render(ExampleComponent.new) %> in the template. This makes more sense to me because you're also declaring the default content to go in that place inside of the template.

@joelhawksley
Copy link
Member

@manuelpuyol what do you think of @boardfish's idea?

@manuelpuyol
Copy link
Contributor Author

I think @boardfish's idea is a good workaround, but I still feel like this should be native to slots to avoid duplication.
It's quite common to set parameters in the renders_one/many lambda call. Something like:

https://github.com/primer/view_components/blob/482c7cfd70584fd3ede8be6963cf4a433b1ac5c2/app/components/primer/navigation/tab_component.rb#L20-L39

Using<%= slot || render(ExampleComponent.new(arg1: val1, arg2: val2, ...) %> makes it harder to sync all the desired parameters

@Spone Spone added the slots Related to the Slots feature label Jan 2, 2023
@Spone
Copy link
Collaborator

Spone commented Jan 2, 2023

Would something like that solve the issue?

class MyComponent < ViewComponent::Base
  renders_one :slot, "RequiredSlot"
  renders_one :other_slot, "NotRequiredSlot"

  def slot
    super || render(RequiredSlot.new)
  end

  class RequiredSlot < ViewComponent::Base
  end

  class NotRequiredSlot < ViewComponent::Base
  end
end

@cj
Copy link

cj commented Feb 5, 2023

@Spone just tried this with the latest version of ViewComponent 2.82.0 and it gives a no superclass method error.

@Spone
Copy link
Collaborator

Spone commented Apr 24, 2023

@cj sorry I missed your reply. Which line causes the no superclass error?

@dirkkelly
Copy link

@cj sorry I missed your reply. Which line causes the no superclass error?

@Spone I received that same issue with 2.82.2

super: no superclass method `slot for #<MyComponent

It was caused by the super call in the example above

  def slot
    super || render(RequiredSlot.new)
  end

I was able to resolve the exception locally by removing the super call, eg:

  def slot
    render(RequiredSlot.new)
  end

This resulted in the expected behavior of the component rendering inline.

@jalyna
Copy link

jalyna commented Sep 25, 2023

Hi!

We are also running into this issue quite often.

The fix suggested by @dirkkelly did not work for me since it wasn't possible to overwrite the required slot's defaults anymore.

A good workaround though is calling get_slot:

def slot
  get_slot(:slot) || render(RequiredSlot.new)
end

but a better approach is the current workaround from @manuelpuyol but with using the with_* setter so that lambda slots are still applied:

def before_render
  with_slot unless slot?
end

From my point of view it would be very helpful if this could be a default behaviour on the Slotable module.

jalyna added a commit to jalyna/view_component that referenced this issue Sep 25, 2023
Fixes ViewComponent#614

Add `required` option to `renders_one` to indicate that there is a default slot to be rendered, which automatically applies lambda options. This only works with lambda or Component slots (and not for passthrough slots). It can be used with polymorphic slots but exactly one specific option can be required, otherwise an error is shown.
joelhawksley added a commit that referenced this issue Jul 30, 2024
joelhawksley added a commit that referenced this issue Jul 30, 2024
joelhawksley added a commit that referenced this issue Aug 1, 2024
* Add support for default slot values

Closes #1861 #614

* Fix final line endings

* add test case for overriding slot default value

* lints

* look up slot default method at compile time

* fix indent

* lints

---------

Co-authored-by: GitHub Actions Bot <41898282+github-actions[bot]@users.noreply.github.com>
@joelhawksley
Copy link
Member

Closed by #2063

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted slots Related to the Slots feature
Projects
None yet
Development

No branches or pull requests

8 participants