Search code examples
javascriptruby-on-railsstimulusjsturboimport-maps

Rails 7 stimulus controller not registering


After pinning the library ./bin/importmap pin stimulus-library, the config/importmap.rb reads as

pin "application", preload: true
pin "@hotwired/turbo-rails", to: "turbo.min.js", preload: true
pin "@hotwired/stimulus", to: "https://ga.jspm.io/npm:@hotwired/stimulus@3.2.1/dist/stimulus.js"
pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true
pin_all_from "app/javascript/controllers", under: "controllers"
pin_all_from "app/javascript/plugin", under: "plugin"
pin "@rails/ujs", to: "https://ga.jspm.io/npm:@rails/ujs@7.0.3/lib/assets/compiled/rails-ujs.js"
pin "pixabay-javascript-autocomplete", to: "https://ga.jspm.io/npm:pixabay-javascript-autocomplete@1.0.4/auto-complete.min.js"

pin "stimulus-library", to: "https://ga.jspm.io/npm:stimulus-library@1.0.0-alpha.2/dist/index.js"
pin "@stimulus-library/controllers", to: "https://ga.jspm.io/npm:@stimulus-library/controllers@1.0.0-alpha.2/dist/index.js"
pin "@stimulus-library/mixins", to: "https://ga.jspm.io/npm:@stimulus-library/mixins@1.0.0-alpha.2/dist/index.js"
pin "@stimulus-library/utilities", to: "https://ga.jspm.io/npm:@stimulus-library/utilities@1.0.0-alpha.2/dist/index.js"
pin "date-fns/formatDistanceToNow", to: "https://ga.jspm.io/npm:date-fns@2.29.3/esm/formatDistanceToNow/index.js"
pin "date-fns/formatDuration", to: "https://ga.jspm.io/npm:date-fns@2.29.3/esm/formatDuration/index.js"
pin "date-fns/intervalToDuration", to: "https://ga.jspm.io/npm:date-fns@2.29.3/esm/intervalToDuration/index.js"
pin "date-fns/isPast", to: "https://ga.jspm.io/npm:date-fns@2.29.3/esm/isPast/index.js"
pin "date-fns/toDate", to: "https://ga.jspm.io/npm:date-fns@2.29.3/esm/toDate/index.js"
pin "mitt", to: "https://ga.jspm.io/npm:mitt@3.0.0/dist/mitt.mjs"

They appear rendered in the page's header tag (snippet)

    <script type="importmap" data-turbo-track="reload">{
  "imports": {
    "application": "/assets/application-68dcc25bc90a7361aed5b18b180afe8896d59f25457955f7e66fc7f5ba16a7cc.js",
    "@hotwired/turbo-rails": "/assets/turbo.min-f309baafa3ae5ad6ccee3e7362118b87678d792db8e8ab466c4fa284dd3a4700.js",
    "@hotwired/stimulus": "https://ga.jspm.io/npm:@hotwired/stimulus@3.2.1/dist/stimulus.js",
    "@hotwired/stimulus-loading": "/assets/stimulus-loading-1fc59770fb1654500044afd3f5f6d7d00800e5be36746d55b94a2963a7a228aa.js",
    "@rails/ujs": "https://ga.jspm.io/npm:@rails/ujs@7.0.3/lib/assets/compiled/rails-ujs.js",
    "pixabay-javascript-autocomplete": "https://ga.jspm.io/npm:pixabay-javascript-autocomplete@1.0.4/auto-complete.min.js",
    "stimulus-library": "https://ga.jspm.io/npm:stimulus-library@1.0.0-alpha.2/dist/index.js",
    "@stimulus-library/controllers": "https://ga.jspm.io/npm:@stimulus-library/controllers@1.0.0-alpha.2/dist/index.js",
[...]
    "controllers": "/assets/controllers/index-2db729dddcc5b979110e98de4b6720f83f91a123172e87281d5a58410fc43806.js",
    "controllers/password_confirm_controller": "/assets/controllers/password_confirm_controller-8ea783d0072b93ac6f100b6a96f506a63e1c28eb1911408fb3e79f070f868ada.js",
    "controllers/password_peek_controller": "/assets/controllers/password_peek_controller-af10a607c8a79b59e721de8ac1ff21f1e981257fd3c7e5b06edc4484d3c2e8eb.js",
 

yet the browser console reports why the controller is not firing:

Failed to register controller: password-confirm (controllers/password_confirm_controller)  
TypeError: e.controllerConstructor is undefined  
Failed to register controller: password-peek (controllers/password_peek_controller)  
TypeError: e.controllerConstructor is undefined

javascript/application.js defines

// Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails
import "@hotwired/turbo-rails"
import "controllers"
import Rails from '@rails/ujs'
import PixabayJavascriptAutocomplete from "pixabay-javascript-autocomplete"
//import "@zxing/library"

while javascript/controllers/application.js defines

import { Application } from "@hotwired/stimulus"

const application = Application.start()

// Configure Stimulus development experience
application.debug = false
window.Stimulus   = application

export { application }

index.js:

import { application } from "controllers/application"

// Eager load all controllers defined in the import map under controllers/**/*_controller
import { eagerLoadControllersFrom } from "@hotwired/stimulus-loading"
eagerLoadControllersFrom("controllers", application)

While the controller iteself defines

import { Application } from "@hotwired/stimulus";
import { PasswordConfirmController } from "stimulus-library";

const application = Application.start();

application.register("password-confirm", PasswordConfirmController);

What is missing/wrong here to allow the controller to fire ?


Solution

  • When you make a controller in controllers/password_confirm_controller.js, Stimulus expects to get a controller class as an export from it. You don't need to create another controller file to register a controller.

    // app/javascript/controllers/index.js
    
    import { application } from "controllers/application"
    // NOTE: ^ this is a stimulus application instance from controllers/application.js
    //       you don't want to start another instance by calling 
    //       Application.start() twice
    
    // these are your controllers, autoloaded from importmaps
    // https://github.com/hotwired/stimulus-rails/blob/v1.2.1/app/assets/javascripts/stimulus-loading.js#L8
    import { eagerLoadControllersFrom } from "@hotwired/stimulus-loading"
    eagerLoadControllersFrom("controllers", application)
    
    // can't use eagerLoadControllersFrom because there are no importmaps for 
    // each controller, they come all in one bundle, and you don't want to load
    // all of them anyway
    import {
      PasswordConfirmController,
      PasswordPeekController,
    } from "stimulus-library";
    application.register("password-confirm", PasswordConfirmController);
    application.register("password-peek", PasswordPeekController);
    

    Also, remove any controllers you created: password_confirm_controller, password_peek_controller.