or: How to load Rails HTML content with Stimulus JS using good old Rails format.js, .js.erb files without any JSON woodoo or: Stimulous with SJR (Server-Generated Javascript)

This is subarticle of Stimulus JS Cheat Sheet

I also wrote Rails.ajax the render_to_stringway (so exact oposit of this solution)

Rails stuff

# config/routes.rb
# ...
resources :entries_search, only: [] do
  post :load_sub_category, on: :collection
end
# ...
# /app/controllers/entries_search_controller.rb
class EntriesSearchController < ApplicationController
  def load_sub_category
    @main_category = Category.find(params[:main_category_id])

    respond_to do |format|
      format.js { render  'load_sub_category' }
    end
  end
end
-# /app/views/entries_search/index.html.slim
div data-controller="entries-search" data-entries-search-categories-load-path="#{load_sub_categories_entries_search_index_path}"
  #sub-categories
    -# here the response will get loaded

.chip.hoverable data-action="click->entries-search#loadSubCategories" data-main-category-id="123"
   | Load "Puppies" Sub Categories
.chip.hoverable data-action="click->entries-search#loadSubCategories" data-main-category-id="345"
   | Load "Kittens" Sub Categories

note: load_sub_categories_entries_search_index_path is being translated to /entries_search/load_sub_categories by Rails routes

-# /app/views/entries_search/_categories.html.slim
- main_category.sub_categories.each do |sub_category|
  .chip= sub_category.title
// /app/views/entries_search/load_sub_categories.js.erb

if @main_category
$('#sub-categories').html("<%= j(render('entries_search/categories', locals: {  main_category: @main_category})) %>");
else
  alert('Error: no Category match this ID');
end

JS stuff

//package.json
{
  "dependencies": {
    # ...
    "@rails/ujs": "^6.0.0-alpha",
    "stimulus": "^1.1.1",
    # ...
  }
}

import { Controller } from "stimulus"
import Rails from "@rails/ujs";

export default class extends Controller {
  loadSubCategories(e) {
    let categoriesLoadPath = this.data.get('categories-load-path');
    let mainCategoryId = e.currentTarget.dataset.mainCategoryId;

    Rails.ajax({
      type: "post",
      dataType: 'script',
      url: categoriesLoadPath,
      data: `main_category_id=${mainCategoryId}&location=Slovakia`,
    })
  }
}

Note how in Rails.ajax we don’t do the success: or error: parts.

With RJS the success / error is being handled by the JS response comming from Rails controller response.

e.g. if there is success .js.erb file relpace the HTML content of element with independent JS execution outside Stimulous controller. Same apply for errors response.

Stringify query

as you may notice Rails.ajax is using stringified query as the data parameter

if you want to stringify the query from hash object, with IE6 you can do:


import { Controller } from "stimulus"
import Rails from "@rails/ujs";
const querystring = require('querystring');

export default class extends Controller {
  loadSubCategories(e) {

    let data = {chip_type: 'category', chip_id: 1234}
    let queryStr = querystring.stringify(data)  // chip_type=category&chip_id=1234

    Rails.ajax({
      type: "post",
      dataType: 'script',
      url: categoriesLoadPath,
      data: queryStr
    })
  }
}

Other sources