Hotwire Discussion

Dynamic DOM Elements in Rails w/ Stimulus

I am using Stimulus (via stimulus-rails 0.1.2) in a Rails (6.0.3.4) application.

I have an ajax call via fetch in a Stimulus controller (which works great). The results of the fetch add some elements to the dom. These elements contain data-controller and data-action attributes on the elements.

However, the action does not fire on the newly added DOM elements. Is there something that I need to do on the front end to get these new dom elements to be “hooked into” the Stimulus lifecycle?

Example of a dom element that is added:

<div data-controller="addresses">
  <a data-action="click->addresses#copy" href="#">Copy</a>
</div>

AddressesController:

// app/assets/javascripts/controllers/addresses_controller.js

import { Controller } from "stimulus"

export default class extends Controller {
  connect() {
    console.log("Addresses Controller connected")
  }

  initialize() {
    console.log("Addresses Controller initialized")
  }

  copy(event) {
    event.preventDefault()

    console.log("Copy the address...")
  }
}

None of the console.log statements above are executed.

I feel like I must be missing something very basic but I do not see any documentation nor none of the related topics seem to help me.

Any idea?

AFAIK app/assets is used by the asset pipeline, so you probably want to use app/javascript/ instead, with app/javascript/controllers/index.js:

// Load all the controllers within this directory and all subdirectories.
// Controller files must be named *_controller.js.

import { Application } from "stimulus"
import { definitionsFromContext } from "stimulus/webpack-helpers"

const application = Application.start()
const context = require.context("controllers", true, /_controller\.js$/)
application.load(definitionsFromContext(context))

Then your controller would go to app/javascript/controllers/addresses_controller.js. Does that solve your issue?

It seems like stimulus-rails will setup Stimulus to loaded by Asset Pipeline when running rails stimulus:install. I don’t think it is necessary to move everything into app/javascript/.

Has everything been setup properly as described in the installation section of the README? If so, it seems like everything should work “automagically”

With the Stimulus include tags added, you’ll automatically have activated Stimulus through the controller autoloader. You can now easily add new Stimulus controllers that’ll be loaded via ESM dynamic imports.

Do you see the console.log messages if the page renders an HTML element with data-controller='addresses' on initial load rather than asynchronously?

Sounds similar to this: Stimulus controller not connecting inside of lazy loaded turbo frame The (temporary) solution in the linked Github issue worked for me.

Other controllers that are bound when the DOM is rendered are working fine, so I don’t believe this would be the issue.

Yes. That is correct. Any of the controllers bound via data-controller are loaded and work just fine when the DOM is initially loaded.