inertiajs / inertia-rails

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

[Feature Request] Ability to render raw HTML (ERB, HAML, Slim, ...) inside frontend framework #113

Closed james-em closed 6 months ago

james-em commented 7 months ago

I will refer to Svelte in the following text because it's the one I currently use, however it's interchangeable with any supported frontend works such as React, Vue, ...

Goal

Allow to render Svelte page components only when really needed in order to save development cost and time.

Per controller action, it would be great to be able to render static HTML/ERB/Slim/HAML view inside a Svelte component.

Tools such as simple_form can be a life saver when it comes to development speed and this introduce the ability to keep using these powerful librairies and all Rails view engine features

Proposal

Updating the render inertia: ... syntax without introducing any breaking changes.

render inertia: true                            # No change
render inertia: "projects/show"                 # No change
render inertia: { component: true }             # Same as true. Just there for the sake of completeness
render inertia: { component: "projects/show" }  # Save as "projects/show". Just there for the sake of completeness
render inertia: { static: true }                # Will render an HTML/ERB view using controller_path/action_name
render inertia: { static: "projects/show" }     # Will render an HTML/ERB view with the given path

How static would work

It would work exactly the same as the current behavior. It would still render the same HTML layout and still render the same HTML/JSON response without any extra HTML tag:

<div id="app" data-page="...">

However, the page structure would change just a little

{
    :component=>"static", <-- This would render a Svelte's component that receives the HTML (body) as a props
    :component=>nil, <-- This wouldn't render any component, however if Inertia renders a layout, that layout will receive the HTML (body) as a props
    :props=>{
      body: "HTML encoded partial view with layout: false of the static ERB view"
      body: "&lt;p&gt;My static view&gt;" <-- <p>My static view</p>
    }, 
    :url=>"/", 
    :version=>"cdf95f49b18b571fa55f57e90df7fb7f84865fe1"
}

A static svelte component (could be a layout or a view) would be this simple:

<script lang="ts">
  export let body: string;
</script>

{@html body}

Testing

I have written a renderer override to allow testing the idea. Simply put this in an initializer

module InertiaRails
  module RendererExtension
    def initialize(component, controller, request, response, render_method, props: nil, view_data: nil, deep_merge: nil)
      super

      component.deep_symbolize_keys! if component.is_a?(Hash)

      @static = component.is_a?(Hash) && component.key?(:static)
      @component = extract_component(component)
    end

    private

    def page
      super.deep_merge(
        component: @static ? inertia_static_component : component,
        props: @static ? static_rendered_view : {}
      )
    end

    def extract_component(component)
      value = component.is_a?(Hash) ? component[:component] || component[:static] : component

      return "#{@controller.controller_path}/#{@controller.action_name}" if value.is_a?(TrueClass)

      value
    end

    def static_rendered_view
      return nil unless @static

      { body: @controller.render_to_string(component, layout: false) }
    end

    def inertia_static_component
      return @controller.send(:inertia_static_component) if @controller.respond_to? :inertia_static_component

      nil
    end
  end
end

InertiaRails::Renderer.prepend InertiaRails::RendererExtension
buhrmi commented 7 months ago

To render some static ERB, why not:

# projects_controller.rb
def show
  render inertia: 'static', props: { 
    body: render_to_string
  }
end
bknoles commented 6 months ago

I agree with @buhrmi , this should be possible with current functionality! Closing, but appreciate the thorough writeup!

james-em commented 6 months ago

Indeed it was only to DRY out a bit more the code. Understood! Thanks for taking the time to check it out