inertiajs / inertia-rails

The Rails adapter for Inertia.js.
https://inertia-rails.dev/
MIT License
574 stars 45 forks source link

Dynamic Root Template #37

Closed azal-io closed 3 years ago

azal-io commented 4 years ago

Inertia Rails automatically uses your default application layout. If you'd like to change that, you can do so via the Inertia config.

How I can set root layout dynamically?

I have two devise models, user, and customer

Based on the type I use different layouts but inertia sets root layout at config, is it possible to change the layout dynamically at a controller level?

Thanks

bknoles commented 4 years ago

@BrandonShar i don't think there's a clean way to change the inertia layout inside a controller, is there?

what about adding a layout: key to the renderer that could dynamically override the layout?

BrandonShar commented 4 years ago

You can change the config on the fly, would that cover this use case?

We do this in one of our apps with a before action on a controller:

before_action -> {
      InertiaRails.configure do |config|
        config.layout = 'hq'
      end
    }
azal-io commented 4 years ago

@BrandonShar that is also what I did, but it feels like a hackish style, I would prefer more rails way of changing the layout either per controller or per action.

azarcore commented 4 years ago

There is also one issue when root template changes, Inertia doesn't just load the new template, you need to do a full page refresh. An expectation would be that Inertia recognizes root template changes and force refresh so two templates don't collide with each other.

before_action -> { InertiaRails.configure do |config| config.layout = 'hq' end }

This only changes server-side but Inertia has no idea about the change until you refresh the page.

So, it is critical to have client-side change as well.

BrandonShar commented 4 years ago

@codecourse-cc that's an interesting use case; it sounds like it would make a great feature.

Adding something like a layout key to the inertia renderer would be simple enough, but in order to support dynamic reloads, we'd need to have the inertia client library send up the current layout on every request (so that we can be sure it actually changed).

This seems reasonable to me, but we'd need to get that client PR accepted and tagged before moving forward with the backend changes.

azal-io commented 4 years ago

@BrandonShar yes, that would be one way to do it. I have a Rails app that has multiple user roles. And for each user role we load a different UI Framework. Main App uses heavy JS libraries and we are ok with that, but customer side uses less of them and sometimes totally different ones. For example, we use Vuetify on main app but on customer side we only have like 3-4 views just showing them reports that main dashboard generates, and it uses custom Vue UIs. We can do multi user sign ins by devise but the main feature of app is that a user can have multiple roles and hence access multiple layouts but without manual refresh user will just see the previously used UI (or whatever the layout gives.). I tried to send redirect with inline location.reload() but it just keeps showing modal error page (in development. has not checked in prod). AS you know in traditional Rails, we can change the layout and the app will just work or show it since it is a full request.

bknoles commented 4 years ago

this is good discussion... one other thing to watch out for: the configuration is stored globally on the InertiaRails module... the 1.4.0 update stores this on the thread, but if you get a single thread handling repeated requests, any change you make to the InertiaRails configuration will carry over to the the request. you might need to manually reset the value, or set it explicitly on every controller action.

bknoles commented 4 years ago

https://github.com/inertiajs/inertia-laravel/issues/114

fwiw, multiple root templates seems to be difficult in Laravel, as well

azal-io commented 4 years ago

@bknoles I am going to try https://github.com/inertiajs/inertia-laravel/issues/57#issuecomment-570581851 force conflict which I guess will full reload the page. Do you think this might work?

Thanks

azal-io commented 4 years ago

If this doesn't work as expected, then I guess I will just create a regular rails root page and have users select their login page, either engineer which is main app, or customer which is the smaller one, and based on that the Rails should load the appropriate Inertia root layout (before action).

azal-io commented 4 years ago

This seems working on Rails: But I need to check to more. This does load the right UI for the user role, just a full page reload after login. But I would prefer the Inertia way of things. this seems like a hack.

def create
    if (self.resource = warden.authenticate(auth_options))
      set_flash_message!(:notice, :signed_in)
      sign_in(resource_name, resource)
      yield resource if block_given?
      # respond_with resource, location: after_sign_in_path_for(resource)
      head 409
      response.set_header('X-Inertia-Location', after_sign_in_path_for(resource))
    else
      flash.alert = 'Invalid email or password!'
      redirect_to new_user_session_path
    end
  end
bknoles commented 4 years ago

I think the way to do this with the current version of things would be:

  1. Commit to setting the template in every controller that has Inertia rendering via something like @codecourse-cc said before_action -> { InertiaRails.configure do |config| config.layout = 'hq' end }
  2. Manually ensure that any action which would cause the Inertia template to "switch" either gets a '409' header added to the response to trigger a full refresh

@BrandonShar can you check this logic?

bknoles commented 4 years ago

yea @azal-io i think what we are learning is that there is (currently) no "Inertia" way at all to deal with multiple root templates... we have to implement that logic within the actual app.

i will continue to chew on this, though... perhaps we can come up with something to the gem to help support this