Stimulus Controllerの中でHTMLを生成することは可能です。JavaScriptにはそのためのメソッドが多数用意されています。しかしJavaScriptによるHTML生成を自主的に避けるのがStimulusの流儀です。
その理由を私なりに紹介し、いつならば許容できるかを考えます。
Rail/ERBではサーバサイドでHTMLをレンダリングするのが基本となります。同じHTMLをStimulusでも生成してしまうと、2箇所で同じを記述することになってしまいます。これはコードの重複になり、メンテナンス上の問題になります。 Stimulus Controllerの中でHTMLレンダリングを避けるのは、このコード重複を避けるためです。
すでにJSONが用意されている場合ならHTMLの生成は簡単です。JSXにJSONを流し込むだけで十分です。しかしブラウザでHTMLをレンダリングする際にしばしば問題になるのはJSONの準備です。簡単なものであれば問題ありませんが、大きなHTMLをブラウザで生成する場合、特にSPAの場合はJSONの準備が大きな負担になります。
コード重複の影響が少ない場合や、コード重複が避けられる場合はStimulus controllerでHTMLを生成しても問題ありません。
<template>タグ <template>タグを利用すると、サーバ側でHTMLのテンプレートを事前にレンダリングすることが可能です。コードの重複は発生しませんので、問題ありません。Stimulusでは主にCSSでHTML要素の表示状態を制御し、HTML生成を不要にします。実際のコードを見ながら解説します。
function Item({ name, isPacked }) { if (isPacked) { return <li className="item">{name} ✅</li>; } return <li className="item">{name}</li>; } export default function PackingList() { return ( <section> <h1>Sally Ride's Packing List</h1> <ul> <Item isPacked={true} name="Space suit" /> <Item isPacked={true} name="Helmet with a golden leaf" /> <Item isPacked={false} name="Photo of Tam" /> </ul> </section> ); }
Itemコンポーネントに渡すisPacked propsで表示の有無を制御します。Itemコンポーネントの中ではisPackedのtrue/falseを見て、異なるDOMをレンダーします。<style> .item__check { display: none; } [data-checked="true"] .item__check { display: inline-block; } </style> <section> <h1>Sally Ride's Packing List</h1> <ul> <li class="item" data-checked="true"> Space suit<span class="item__check">✅</span> </li> <li class="item" data-checked="true"> Helmet with a golden leaf<span class="item__check">✅</span> </li> <li class="item"> Photo of Tam<span class="item__check">✅</span> </li> </ul> </section>
<li>のdata-checked属性を変更することでitem__checkの表示のオン・オフを切り替えています。<li>を描画するかしないかを制御するのではなく、data-checked属性を切り替えて、表示・非表示を切り替えています。なおdata-*属性を変えるのではなく、Stimulusから直接CSSクラスを書き換える方法もあります。しかし表示に関するCSSクラスと状態に関するCSSクラスの見分けがつかなくなる問題点があります。data-*属性を使って、表示とステートを分けることをお勧めします。