phlex-ruby/phlex-rails

JSON response uses the ApplicationLayout

Closed this issue · 7 comments

Given the following Rails controller:

class MyController < ApplicationController
  layout -> { ApplicationLayout }

  def index
    respond_to do |format|
      format.json
      format.html { render(MyPhlexView.new) }
    end
  end
end

When requesting the JSON content, the AppliactionLayout will be rendered

Hey @albertorestifo, I’m not able to reproduce this. Here's what I’m doing:

class ExamplesController < ApplicationController
  layout -> { ApplicationLayout }

  def index
    respond_to do |format|
      format.json { render json: { message: "Hello" } }
      format.html { render Examples::IndexView.new }
    end
  end
end

When I render /examples, I get the HTML view wrapped in the HTML template. When I render /examples.json, I get just the { "message" : "Hello" } JSON without the template.

@joeldrapper the difference in your example is that you're rendering a json payload directly.

In my rails app, I'm following the "default" pattern of having view files like: index.json.jbuilder, which then render a JSON partial.

That's why I'm not specifying any block to the format.json call, to let Rails fall down the default path.

In this case, the expected behaviour should be that the AppliactionLayout is ignored, as it's a JSON response, and rails follows the default search pattern for a view template.

Thank you. I'm hoping there's an interface for specifying the format of a template object in Rails. It's not documented but I expect it's there somewhere.

It doesn't look like there's an interface for a layout to specify which formats it supports. I think Rails manages this when the layout is a string because it looks for a layout file with the same extension as the template.

We need some layer above the layout class that is able to select a layout depending on the format. I'm not sure the best way to do this. 🤔

Right now, an easy workaround is to keep using a ERB template as the layout instead of using a Phlex layout

Another workaround and actually my preferred way of doing layouts is to specify layout false and then create a layout component that your views render as the first thing in the view template.

This is less "railsy" but in some ways it's a lot better because it means the top of your layout is rendered before the page so you could use the upcoming streaming features when they're ready.

I still want to find a fix for this issue though because supporting standard layouts is important for the intermediate phase where you may still have some ERB views.

@albertorestifo you should be able to configure your layout like this.

layout -> (controller) { controller.formats.include?(:html) ? ApplicationLayout : false }