Skip to content

Allow to define the view_context without rendering the component #1926

Description

@mildred

Feature request

I'd like to be able to do something like:

comp = MyComponent.new().with_view_context(view_context)
comp.do_something_specific

That is, a way to associate the component with a view_context so I can do things like translations or access render-specific methods, but not perform the rendering step.

Motivation

I have a component that generates a HTML table. I want to make the table downloadable as a spreadsheet, but I'd like the table generation to happen within the component itself to keep all the things at the same place.

So in my rails controller, when I want to generate the spreadsheet, I create the component, then call the spreadsheet method on it that will generate the spreadsheet file. To do that I need translations, which requires the view_context.

Workaround

In my component I can define:

  def render?
    ! @skip_render
  end

  def with_no_render
    old = @skip_render
    @skip_render = true
    yield
    @skip_render = old
    self
  end

  def with_view_context(view_context)
    with_no_render do
      render_in(view_context)
    end
    self
  end

Then I can use my component like this:

    component = MyTableComponent.new(*args).with_view_context(view_context)
    add_table(spreadsheet, component.spreadsheet_table)

Activity

  1. reeganviljoen commented on Dec 8, 2023

    @reeganviljoen
    Collaborator

    @Spone, @camertron @joelhawksley what do you think about this,
    I have seen niche circumstances where by I would have benefited from passing in a custom view_context when rendering, I do think this is niche however, so I would like to hear your thoughts on it

  2. joelhawksley commented on Dec 8, 2023

    @joelhawksley
    Member

    @reeganviljoen @mildred my gut says that this kind of functionality isn't the responsibility of ViewComponent. ViewComponent is meant to build objects that have the Single Responsibility of being passed to Rails' render.

    I can see the appeal of using Slots to construct tabular data, but I think you might be better off creating some sort of PORO or model to represent your tabular data and rendering that via a ViewComponent for HTML and some other tooling for a spreadsheet format.

  3. camertron commented on Dec 13, 2023

    @camertron
    Contributor

    I agree with @joelhawksley, the motivation to add this doesn't feel particularly strong, since components aren't designed to be used like shared objects. I would personally refactor your example to extract the logic into a separate class.

  4. reeganviljoen commented on Dec 14, 2023

    @reeganviljoen
    Collaborator

    @camertron i feel the part about not rendering doesn't make sense but passing in a custom view_context when rendering may be useful in niche cases

  5. coorasse commented on Feb 28, 2025

    @coorasse

    I had recently the same need. I won't go too much in details about requirements, but I have a ViewComponent with a title method. The method uses a path helper as well.
    I had to make a CSV report "with what is displayed on the web page" and the simple and logic solution for me is to run MyComponent.new.title to get the actual title.

    The error message is (slightly) misleading
    "#controller can't be used during initialization, as it depends on the view context that only exists once a ViewComponent is passed to the Rails render pipeline."

    since I was not in an initializer, but I met this message before and knew that I had to convert path call into Rails.application.routes.url_helpers.my_path as I did with my I18n.t calls.

    I know, it's a niche case, and probably not how is supposed to be used, but I still find it ambiguous that I cannot render my title. I'd be fine in passing a view_context explicitly or be able to set a default one somehow.

  6. joelhawksley commented on Mar 10, 2025

    @joelhawksley
    Member

    @coorasse thanks for the report!

    I've opened #2217 to improve the error messaging in that case. You're totally right in that the warning is about referencing certain properties before rendering not during initialization.

    More broadly, it's always been the intention that the Single Responsibility of a ViewComponent is to render content, not to expose data to other objects. I'm hesitant to add functionality to the framework that encourages such behavior. If you need logic inside a ViewComponent elsewhere, I'd encourage folks to extract that logic into another class or module.

    Of course, I'm always happy to be proven wrong here ❤

  7. 23tux commented on Oct 29, 2025

    @23tux
    Contributor

    I had a similar issue and I introduced a setup_render test helper in my PR #2483

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions