Giter Site home page Giter Site logo

stimulus-rails's People

Contributors

abevoelker avatar adrienpoly avatar afcapel avatar andersklenke avatar bborn avatar chadrschroeder avatar danott avatar davegudge avatar davidcolby avatar dependabot[bot] avatar dhh avatar exterm1nate avatar forsbergplustwo avatar ianneub avatar igorkasyanchuk avatar ikanade avatar javan avatar kennyadsl avatar kevinberthier avatar koheisg avatar kuei0221 avatar lewispb avatar liaden avatar lunich avatar petergoldstein avatar rubys avatar seanpdoyle avatar shafy avatar terracatta avatar wnm avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

stimulus-rails's Issues

controller files *.js.erb silently fail (with new loader `stimulus-loading`)

Hello,

New loader module stimulus-loading.js, when used in default eager loading mode, silently fails. My setup shows only hello-controller on the list of app assets in dev console. out of 3 controller files, 2 of them are with .erb. When switched to lazy loading, produces warning

Failed to autoload controller: output
Error: Unable to resolve specifier 'controllers/output_controller' from http://localhost:3003/assets/stimulus-loading-685d40a0b68f785d3cdbab1c0f3575320497

My output_controller.jr.erb is using erb code and worked fine before updating to new loader in 0.7.1

importmap.rb:
pin_all_from "app/javascript/controllers", under: "controllers"
sprockets manifest.js:
//= link_tree ../../javascript .js

I am not sure if this is a bug or something wrong in my app setup.

If I use rails stimulus:manifest:update task generated imports are using .erb in the import names, which is not working. But after correcting import names to end with .js they work.

Mutation Observer not recognising DOM change when using Turbo Streams

From a Rails controller I use Turbo Streams to replace a Form element. The form is connected to a StimulusJS controller (input controller) and it was my understanding that the controller should disconnect and reconnect as the DOM element is replaced.

However, it doesn't appear to detect any DOM change and consequently does not connect the controller again after the Form element is replaced. My understanding is StimulusJS uses the DOM MutationObserver API to detect the DOM changes.

Why is it not picking up the DOM change when I use Turbo Streams?

Form element:

<%= form_with model: @game, method: :patch, html: { autocomplete: "off" },  data: { controller: "input" }, id: dom_id(@game), class: "grid justify-center justify-items-center grid-cols-5 gap-y-2 gap-x-2 max-w-md mx-auto mt-2" do |form| %>
    <%= form.fields_for :guesses, @game.guesses.order(:created_at) do |ff| %>
        <% if ff.object.value != '' %>
            <div id="input_<%= dom_id(ff.object)%>" class="w-16 h-16 ">
                <%= tag.div :value, class: ["flex justify-center items-center text-center uppercase text-3xl text-white rounded focus:border-cyan-400 w-full h-full", "bg-green-400": ff.object.result == 'match', "bg-orange-400": ff.object.result == 'occurs', "bg-gray-400": ff.object.result == 'miss' ] do %>
                    <%= ff.object.value %>
                <% end %>
            </div>
        <% else %>
            <div id="input_<%= dom_id(ff.object)%>" class="w-16 h-16 ">
                <%= ff.text_field :value, class: ["text-center uppercase caret-transparent text-3xl text-white bg-slate-800 border rounded focus:border-cyan-400 w-full h-full", "bg-green-400": ff.object.result == 'match', "bg-orange-400": ff.object.result == 'occurs', "bg-gray-400": ff.object.result == 'miss' ], data: {action: '', input_target: 'inputBox'}, maxlength: "1", autofocus: "true" %>
            </div>
        <% end %>
    <% end %> 
    <%= form.submit "Submit", class: "hidden" %>
<% end %>

Stimulus Controller:

import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  static targets = ["inputBox"]

  connect() {
    this.index = 0
    this.focusInput()
  }

  
  focusInput() {
    console.log("focusInput called")
  }
}

Connect running multiple times

Simple install on new Rails 6.1 app. As can be seen on page load, connect occurs twice every time. Running Ruby 3.0.0 and Rails 6.1.1 and latest stimulus-rails gem.

Screen Shot 2021-02-03 at 9 51 11 AM

Application.js

import "../stylesheets/application";
import $ from 'jquery';
global.$ = jQuery;

import Rails from "@rails/ujs"
import Turbolinks from "turbolinks"
import * as ActiveStorage from "@rails/activestorage"
import "channels"
import "controllers"

import { stripeElements } from "./subscriptions";
global.stripeElements = stripeElements;

global.toastr = require("toastr")

Rails.start()
Turbolinks.start()
ActiveStorage.start()

var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'))
var tooltipList = tooltipTriggerList.map(function(tooltipTriggerEl) {
    return new bootstrap.Tooltip(tooltipTriggerEl)
})

var popoverTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="popover"]'))
var popoverList = popoverTriggerList.map(function(popoverTriggerEl) {
    return new bootstrap.Popover(popoverTriggerEl)

Index.html.haml

.container
  %h1 Home#index
  %p Find me in app/views/home/index.html.haml

  %div{"data-controller" => "home"}
    %input{"data-hello-target" => "name", :type => "text"}/
    %button{"data-action" => "click->hello#greet"}
      Greet
    %span{"data-hello-target" => "output"}

Package.js

{
  "name": "subscription",
  "private": true,
  "dependencies": {
    "@rails/actioncable": "^6.0.0",
    "@rails/activestorage": "^6.0.0",
    "@rails/ujs": "^6.0.0",
    "@rails/webpacker": "5.2.1",
    "jquery": "^3.5.1",
    "moment": "^2.29.1",
    "stimulus": "^2.0.0",
    "toastr": "^2.1.4",
    "tooltip": "^1.6.1",
    "turbolinks": "^5.2.0"
  },
  "version": "0.1.0",
  "devDependencies": {
    "rails-erb-loader": "^5.5.2",
    "webpack-dev-server": "^3.11.2"
  }
}

Environment.js

const { environment } = require('@rails/webpacker')
const erb = require('./loaders/erb')

const webpack = require('webpack');
environment.plugins.append("Provide",
    new webpack.ProvidePlugin({
        $: 'jquery',
        jQuery: 'jquery',
        Rails: ['@rails/ujs']
    }))

environment.loaders.prepend('erb', erb)
module.exports = environment

How to import vendor library to use in Stimulus controller, Asset Pipeline version

I'm using Stimulus in Rails 6 via the Asset Pipeline and want to use either momentjs or dayjs library within my controller. How do I import those?

I've tried saving the dayjs.min.js file to vendors/assets/javascripts/dayjs/dayjs.min.js, but do I need to add anything to app/assets/config/manifest.js? or maybe app/assets/javascripts/importmap.json.erb

manifest.js looks like this:

//= link_tree ../javascripts
//= link_tree ../images
//= link_directory ../javascripts .js

importmap.json.erb looks like this:

{
  "imports": { 
    "turbo": "<%= asset_path "turbo" %>",
    <%= importmap_list_with_stimulus_from "app/assets/javascripts/controllers", "app/assets/javascripts/libraries" %>
  }
}

This is the bit where I'm stuck. I get the same error:

Failed to autoload controller: timepicker 
Error: Unable to resolve specifier 'dayjs' from 
http://my.127.0.0.1.xip.io/assets/controllers/timepicker_controller-b2e267b01d1aad58218fa00a049e456870db49eda347728b16443be29c59682f.js

I think then once the library is "available", I should be import it to be available in my Stimulus controller with ๐Ÿ™ :

  import { Controller } from "stimulus"
+ import dayjs from 'dayjs'
  export default class extends Controller {
  ...

If I try the following with importmap.json.erb:

  {
    "imports": { 
      "turbo": "<%= asset_path "turbo" %>",
+    "dayjs": "<%= asset_path "dayjs/dayjs.min.js" %>",
      <%= importmap_list_with_stimulus_from "app/assets/javascripts/controllers", "app/assets/javascripts/libraries" %>
    }
  }

The error changes to the following when I reload the page:

Failed to autoload controller: timepicker SyntaxError: import not found: default

Add comprehensive engines support

There are a number of outstanding issues when using this gem with engines. I think it would be helpful to try to collect all the issues so that it's clear what needs fixing, and what's being fixed, and what already works.

  • the importmap_list_from helper assumes relative paths are relative to Rails.root, as opposed to the root of an engine (#39 partially fixes this by allowing absolute paths, but it shouldn't be necessary to use absolute paths in the first place). #50 attempts to fix this by introducing new Sprockets directives.
  • import maps cannot be in a namespaced path: the import map is currently expected to always be in app/javascripts/importmap.json. Because of this, if multiple engines or an engine and an app provide an import map, one will overwrite the other(s). #41 allows import maps to be included from custom locations
  • import maps cannot be composed: it currently isn't possible to have multiple import maps on the same page. It isn't inconceivable that because of this (potentially multiple) engines and the mounting app would want to use make use of the same import map. Not really sure how common this situation is (it depends how you use engines). #50 fixes this by allowing importmaps to be merged by Sprockets.
  • the install task doesn't work in engines: the install task currently uses tasks and references paths which only apply to main apps, not engines. #17 attempts to fix this (though needs conflicts resolving)

Console error with lazyLoadControllersFrom and external controller

I've noticed that when I load a page with the autocomplete controller I get an error in the console:

Failed to autoload controller: autocomplete Error: Unable to resolve specifier 'controllers/autocomplete_controller' imported from http://localhost:3000/assets/stimulus-loading-e367296568a6df104dc84fcfe5d1aafae5076fc0.js
    throwUnresolved es-module-shims.js:394
    _resolve es-module-shims.js:350
    importShim es-module-shims.js:384
    loadController stimulus-loading-e367296568a6df104dc84fcfe5d1aafae5076fc0.js:70
    lazyLoadExistingControllers stimulus-loading-e367296568a6df104dc84fcfe5d1aafae5076fc0.js:39
    lazyLoadExistingControllers stimulus-loading-e367296568a6df104dc84fcfe5d1aafae5076fc0.js:39
    lazyLoadControllersFrom stimulus-loading-e367296568a6df104dc84fcfe5d1aafae5076fc0.js:34
    <anonymous> index-26693b7cb3885f2275520ffa4aafbe6ea299e699.js:6

I have a pretty basic config:

pin "@hotwired/stimulus", to: "stimulus.min.js", preload: true
pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true
pin "@hotwired/turbo-rails", to: "turbo.min.js", preload: true
pin "application", preload: true
pin "stimulus-autocomplete", to: "https://ga.jspm.io/npm:[email protected]/src/autocomplete.js"
pin_all_from "app/javascript/controllers", under: "controllers"
pin_all_from "app/javascript/lib", under: "lib"
import { Application } from "@hotwired/stimulus"
import { Autocomplete } from "stimulus-autocomplete"

const application = Application.start()

application.register("autocomplete", Autocomplete)

application.debug = false
window.Stimulus   = application

export { application }
import { application } from "controllers/application"

import { lazyLoadControllersFrom } from "@hotwired/stimulus-loading"
lazyLoadControllersFrom("controllers", application)

As soon as I switch to the eager loading the error goes away

import { eagerLoadControllersFrom } from "@hotwired/stimulus-loading"
eagerLoadControllersFrom("controllers", application)

In both cases the autocomplete is working, but it's just noisy to get that error in the console. Should manually registered controllers be excluded by the lazy loading under the controllers path?

I've also tried to remove the preload from pin "application", preload: true since application includes a controller, but nothing changed

Autoloading skips over turbo-frame loaded content

Hi ๐Ÿ‘‹

I noticed that when I receive new HTML content in a turbo-frame element, my stimulus connect() method wasn't being called for a controller that wasn't previously referenced in the page. When I refreshed the page, everything worked fine. After doing a bit of investigation, I believe it's due to the autoloader only listening to the turbo:load event.

I'm happy to open a PR to fix this, but I'm not sure what the best approach is. On the turbo events page, there doesn't seem to be an event that is triggered after turbo-frame content is loaded into the DOM.

A thought I had for how to solve this is to use a mutation observer -- A cool side effect of that route is that if you just edited the DOM and added a data-controller="mycontroller" element, it should be picked up & autoloaded as well. Not sure if there's edge cases there that I'm missing though (admittedly, I haven't yet used the MutationObserver API ๐Ÿ˜… ). Also, doesn't stimulus already have a mutation observer on the entire document tree? Would it make more sense to just hook in there?

Has anyone else ran into this? In the meantime, I can workaround by disabling autoloading ๐Ÿ™‚

Thanks!

Make stimulus-rails aware of Sprockets transformers that produce JavaScript

... things like CoffeeScript, etc.

The following works for me:

module Stimulus::ImportmapHelper
  def find_javascript_files_in_tree(path)
     exts = {'.js' => '.js', '.jsm' => '.jsm'}.merge(
       Sprockets.mime_exts.map {|key, value|
         next unless Sprockets.transformers[value]["application/javascript"]
         [key, '.js']
       }.compact.to_h)

     Dir[path.join('**/*')].map {|file|
       file_ext, web_ext = Sprockets::PathUtils.match_path_extname(file, exts)
       next unless file_ext

       next unless File.file? file

       Pathname.new(file.chomp(file_ext) + web_ext)
     }.compact
  end
end

See https://www.ruby2js.com/examples/rails/stimulus_sprockets for a complete walkthrough.

Additional stimulus controller "lookup" paths in importmap_helper.rb

Firstly, thanks for this library.. it feels like the future! It's also been a great intro to ESM and how that works.

We're running into an issue using this together with a Rails engine, because the importmap_helper.rbfile is hardcoded to look in the Rails.root folder only, when resolving the paths for stimulus controllers.

  def importmap_list_from(*paths)
    Array(paths).flat_map do |path|
      if (absolute_path = Rails.root.join(path)).exist?
        find_javascript_files_in_tree(absolute_path).collect do |filename|
          module_filename = filename.relative_path_from(absolute_path)
          module_name     = importmap_module_name_from(module_filename)
          module_path     = asset_path(absolute_path.basename.join(module_filename))

          %("#{module_name}": "#{module_path}")
        end
      end
    end.compact.join(",\n")
  end

if (absolute_path = Rails.root.join(path)).exist?

We can work around this by manually adding our engine's stimulus controllers to the importmap.json file like so:

{
  "imports": {
    "turbo": "<%= asset_path "turbo" %>",
    <%= importmap_list_with_stimulus_from "app/assets/javascripts/controllers", "app/assets/javascripts/libraries" %>,
    "slideshow_controller": "<%= asset_path "slideshow_controller" %>"
  }
}

where slideshow_controller resides under the engine.

It would be great if we could specify which root paths to use in this helper, so we can include AppKit::Engine.root in the helper path, for example and have it work automatically.

Would this be a welcome option if we gave it a go for a PR? A bit unsure how best to do it, while also keeping the interface simple like it is now: importmap_list_from(*paths) so open to ideas and suggestions.

Should controller names be registered as camelCase?

First off, thank you all for such a lovely framework. I've been using it in Rails projects and it's been an absolute joy.

Something that I came across during development the other day was a naming convention-related misunderstanding, where I'd be trying to reference a controller with camelCase in a data-action and nothing fired. The way I fixed this, of course, was to use dash-case in the data-action, but it made me wonder if there's merit to letting Stimulus Rails recognize controllers by the camelCase equivalent as well, given that's a standard convention in JavaScript.

For example. if we have a controller declared as:

// app/javascript/controllers/multi_word_controller.js
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  logHello() {
    console.log("Hello!")
  }
}

And we try to use it in a view like:

<div data-controller="multi-word">
  <button data-action="click->multi-word#logHello>Log hello</button>
</div>

This will correctly log "Hello!" in the console when the button is clicked. However, if the button element is declared as:

<button data-action="click->multiWord#logHello">Log hello</button>

The logHello method will not fire. Intuitively, I want to write the action declaration the second way, where I'm using camelCase for both the controller name as well as the method name.

Would love to hear a maintainer's take on this!

Uncaught TypeError: __require.context is not a function

I get this trying to install Stimulus using the new esbuild-rails (which I guess has just been replaced by jsbundling-rails, but I think the issue holds

Uncaught TypeError: __require.context is not a function, it's from the controllers/index.js line

const context = require.context("controllers", true, /_controller\.(js|ts)$/)

I'm not sure whether this is a case where the install is giving me the wrong thing (and I should be using the node install and importing all my files one-by-one) or whether there's a way to make the require work.

Thanks

manifest:update configuration for controller segmentation

I looked through the manifest.rb and noted that there is only a single flow of logic for the creation of the manifest for controllers. I actually had 2 separate questions here for this.

  1. I have what are basically 3 sections of my application, the homepage, the app UI itself for a logged in user, and then the admin backend. Ideally, in my mind, it would make sense to keep the controllers for these separated out and only load what is necessary for each, so I've been keeping them logically separated within the controllers folder:

    • controllers/admin/*_controller.js
    • controllers/app/*_controller.js
    • controllers/homepage/*_controller.js

    Just given how stimulus-rails and the manifest works, is this a wrong approach? Is the tradeoff of separation of the JS not enough of a benefit versus just loading all of the controllers at once?

  2. If the answer to the above is that there is a benefit, are there any plans (or aversion to a PR) for allowing for the manifest update task to know, either via an argument or configuration, that certain folder(s) should be delegated out to their own root controller files?

A quick 30s brainstorming session gave me the rough idea of something such as an optional file within the config folder, stimulus.yml with a format such as:

admin:
  - admin
  - foo
app:
homepage:

Such that when rails stimulus:manifest:update is run, it would check for the configuration file. If it doesn't exist, it would work as normal, but if it does exist it would work such that (in the example of the above) it would generate the following:

admin.js

// This file is auto-generated by ./bin/rails stimulus:manifest:update
// Run that command whenever you add a new controller or create them with
// ./bin/rails generate stimulus controllerName

import { application } from "./application"

import Admin__BarController from "./admin/some_controller.js"
application.register("admin--bar", Admin__BarController);

import Foo__BazController from "./foo/bar_controller.js"
application.register("foo--bar", Foo__BarController);

app.js

// This file is auto-generated by ./bin/rails stimulus:manifest:update
// Run that command whenever you add a new controller or create them with
// ./bin/rails generate stimulus controllerName

import { application } from "./application"

import App__FeatureController from "./app/feature_controller.js"
application.register("app--feature", App__FeatureController);

homepage.js

// This file is auto-generated by ./bin/rails stimulus:manifest:update
// Run that command whenever you add a new controller or create them with
// ./bin/rails generate stimulus controllerName

import { application } from "./application"

import Homepage__FeatureController from "./homepage/feature_controller.js"
application.register("homepage--feature", Homepage__FeatureController);

index.js

// This file is auto-generated by ./bin/rails stimulus:manifest:update
// Run that command whenever you add a new controller or create them with
// ./bin/rails generate stimulus controllerName

import { application } from "./application"

import SharedFeatureController from "./shared_feature_controller.js"
application.register("shared-feature", SharedFeatureController);

Presumably the stimulus.yml parsing would need to be intelligent enough to raise an error in the case that application or index were passed since those are reserved, but that wouldn't be terribly difficult.

Autoloader eats errors

Since work is currently being done on the autoloader (#3, #11) I thought I'd ask - any interest in letting autoloader errors just happen?

The autoloader transforms import errors into a "Failed to autoload controller: ..." message. While writing a stimulus controller, I made a SyntaxError by importing a wrong module name, and had to stick a debugger into the catch statement to dig it out.

IMHO if something fails to load, swallowing the error a) confuses development, and b) will almost certainly break something downstream anyways unless your controller was entirely cruft.

At the minimum, it should console.log the error, no?

Failing to register controllers does not bubble the error to handleError

Whenever a controller fails to register (for instance if a targetNameConnected() method throws an error), stimulus will catch the error and display a console message. However that means we can never be alerted of that error, because it doesn't bubble the error to application.handleError, which I believe is the hook point to setup exception monitoring (appsignal, etc.)

Is it by design, or are you open to a fix?

.catch(error => console.error(`Failed to register controller: ${name} (${path})`, error))

Update installation README

The instructions in the README did not work for Rails 6.1 until I ran this:

rails webpacker:install:stimulus

You may want to update.

Error while running `hotwire:install`, which itself runs `stimulus:install`

Description

After adding the gem hotwire-rails to my Gemfile and running bundle exec rails hotwire:install, I get the following output containing an error (Errno::ENOENT: No such file or directory - bin/bundle):

Copying Stimulus JavaScript
create app/assets/javascripts
create app/assets/javascripts/controllers/hello_controller.js
create app/assets/javascripts/controllers/index.js
create app/assets/javascripts/importmap.json.erb
create app/assets/javascripts/libraries
create app/assets/javascripts/libraries/.keep
Add app/assets/javascripts to asset pipeline manifest
append app/assets/config/manifest.js
Add Stimulus include tags in application layout
insert app/views/layouts/application.html.erb
Turn off development debug mode
gsub config/environments/development.rb
Turn off rack-mini-profiler
gsub Gemfile
run bin/bundle from "."
rails aborted!
Errno::ENOENT: No such file or directory - bin/bundle
/app/bin/rails:5:in <top (required)>' /app/bin/spring:10:in require'
/app/bin/spring:10:in block in <top (required)>' /app/bin/spring:7:in <top (required)>'
Tasks: TOP => app:template
(See full trace by running task with --trace)
Yield head in application layout for cache helper
insert app/views/layouts/application.html.erb
Add Turbo include tags in application layout
insert app/views/layouts/application.html.erb
Add Turbo to importmap
insert app/assets/javascripts/importmap.json.erb
Enable redis in bundle
gsub Gemfile
Switch development cable to use redis
gsub config/cable.yml
Turbo successfully installed โšก๏ธ

Steps to Reproduce

  1. Create a new Rails app with rails new app123 --skip-javascript and add hotwire-rails gem to the Gemfile.
  2. Run bundle && bundle exec rails hotwire:install
  3. See the error

Workaround

Running bundle binstubs bundler creates the missing bin/bundle in my project. I guess the rake task stimulus:install shouldn't assume that bin/bundle is present. This is the line which is causing the issue:

run "bin/bundle", capture: true

Failed to autoload controller popper js

Using popper.js, stimulus.js, rails 6, hotwire and tailwind.

Failed to autoload controller: popper Error: Unable to resolve specifier '@popperjs/core' from http://0.0.0.0:3000/assets/controllers/popper_controller-9551ce655873804eb01eade8d7ef77bc460af5cf2da4ebeaa24dbe05675c13e0.js at throwUnresolved (es-module-shims-c4493e644afb380789c8f0d44266900b8631ec442f8f95886997dac9f98893f1.js:472:11) at resolve (es-module-shims-c4493e644afb380789c8f0d44266900b8631ec442f8f95886997dac9f98893f1.js:468:99) at es-module-shims-c4493e644afb380789c8f0d44266900b8631ec442f8f95886997dac9f98893f1.js:427:26 at Array.map (<anonymous>) at es-module-shims-c4493e644afb380789c8f0d44266900b8631ec442f8f95886997dac9f98893f1.js:426:39 at async loadAll (es-module-shims-c4493e644afb380789c8f0d44266900b8631ec442f8f95886997dac9f98893f1.js:237:5) at async topLevelLoad (es-module-shims-c4493e644afb380789c8f0d44266900b8631ec442f8f95886997dac9f98893f1.js:256:5)

popper_controler.js:

`import { Controller } from "stimulus";
import { createPopper } from "@popperjs/core";

export default class extends Controller {
static get targets() { return [ "element", "tooltip"] }
static values = {
placement: { type: String, default: "top" },
offset: { type: Array, default: [0, 8] },
};

connect() {
    // Create a new Popper instance
    this.popperInstance = createPopper(this.elementTarget, this.tooltipTarget, {
        placement: this.placementValue,
        modifiers: [
            {
                name: "offset",
                options: {
                    offset: this.offsetValue,
                },
            },
        ],
    });
}

show(event) {
    this.tooltipTarget.setAttribute("data-show", "");

    // We need to tell Popper to update the tooltip position
    // after we show the tooltip, otherwise it will be incorrect
    this.popperInstance.update();
}

hide(event) {
    this.tooltipTarget.removeAttribute("data-show");
}

// Destroy the Popper instance
disconnect() {
    if (this.popperInstance) {
        this.popperInstance.destroy();
    }
}

}`

app/javascript/packs/application.js:

`// This file is automatically compiled by Webpack, along with any other files
// present in this directory. You're encouraged to place your actual application logic in
// a relevant structure within app/javascript and only use these pack files to reference
// that code so it'll be compiled.

import "@hotwired/turbo-rails"
import * as ActiveStorage from "@rails/activestorage"
import "channels"
import "../stylesheets/application.scss"
import "../stylesheets/popper.scss"
// require("../stylesheets/application.scss")

ActiveStorage.start()

require("trix")
require("@rails/actiontext")
`

spp/javascript/stylesheets/application.scss:
`@tailwind base;
@tailwind components;
@tailwind utilities;

@import "../stylesheets/actiontext.scss";

/* block out first/last segment of background HR */
section.order-workflow ol li {
position: relative;
flex: 1;
> * {
position: relative;
}
// blockers for left/right sides
&:first-child::before {
content: "";
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 50%;
background: white;
}
&:last-child::before {
content: "";
position: absolute;
top: 0;
bottom: 0;
left: 50%;
right: 0;
background: white;
}
}
}
`

404 on assets/stimulus-loading.js with config.assets.digest set to false

When turning the asset digests off (https://guides.rubyonrails.org/asset_pipeline.html#turning-digests-off) in the development environment by setting config.assets.digest to false in config/environments/development.rb the file assets/stimulus-loading.js will not load and you will get an HTTP 404 for that file:

image

Changing config/importmap.rb to pin to stimulus-loading instead of stimulus-loading.js will make it work in the environments where the asset digests are turned off, the file will then however not be included in the production assets when precompiling them.

I have created a new Rails app where you can reproduce this issue here: https://github.com/mrkcor/stimulus-loading-issue

For anyone looking for a workaround for this issue: you can put a copy of the file stimulus-loading.js in vendor/javascript and pin the file as is indicated in the README.

Log Import Failures at a Higher Log Level Than Debug

If I'm using importmap-rails and make a mistake while importing a module into a Stimulus controller, the error message related to this is logged with console.debug here:

// app/assets/javascripts/stimulus-loading.js
import(path)
  .then(module => registerController(name, module, application))
  .catch(error => console.debug(`Failed to register controller: ${name} (${path})`, error))

This will only appear if the browser's JavaScript console is in Verbose or Debug mode. By default, this is off in Chrome. When I was using Stimulus with Webpacker, this kind of error was logged at a higher level. Would it be possible to make these critical errors more visible so they could be spotted easily in any environment?

Other areas in Stimulus use console.log for errors. And code in importmap-rails and turbo-rails use console.warn and console.error for displaying caught exceptions. I think console.error would make sense when a controller completely fails to load.

See this comment for more on how to recreate the problem.

stimulus:install task breaks asset compilation when using Webpacker

The changes in #65 included an update to the hello_controller created by the install script to reference @hotwired/stimulus instead of stimulus

With this change, Webpacker users will now see the following error when they attempt to compile:

ERROR in ./app/javascript/controllers/hello_controller.js
Module not found: Error: Can't resolve '@hotwired/stimulus' in '/code/new-app/app/javascript/controllers'
resolve '@hotwired/stimulus' in '/code/new-app/app/javascript/controllers'

Steps to reproduce:

  1. rails new my-app
  2. bundle add stimulus-rails
  3. rails stimulus:install
  4. bin/webpack-dev-server

A fix for this might be using gsub_file after the controllers directory is copied in the Webpacker install script to replace from "@hotwired/stimulus" with from "stimulus" which I'm happy to put in a PR for if that's an acceptable path to a resolution.

Generated controllers/index.js doesn't seem to work in production

With a basic Rails 7.0.0 app, running ./bin/rails stimulus:manifest:update will create a app/javascript/controllers/index.js file that doesn't seem to work in production, at least not for some people.

Changing this line:

import { application } from "./application"

to:

import { application } from "controllers/application"

And change each controller import from:

import NameController from "./name_controller.js"

to:

import NameController from "controllers/name_controller"

Seems to fix the problem in production.

Ref: https://stackoverflow.com/questions/69757938/404-errors-on-javascript-when-using-importmaps-in-rails-7-in-production

Generator Controller Comment Uses Incorrect Syntax

Steps to reproduce

rails g stimulus hello_world

Expected behavior

According to documentation, "when an identifier is composed of more than one word, write the words in kebab-case (i.e., by using dashes: date-picker, list-item)." Therefore, the generated help text in the controller should read:

// Connects to data-controller="hello-world"

Actual behavior

Auto-generated help text in the controller incorrectly reads:

// Connects to data-controller="hello_world"

Using this format data-controller="hello_world" in your view results in the following error

Warning connecting controller "hello_world"

Element references undefined controller "hello_world"

Generated comment doesn't compensate for underscored controller filenames, which are acceptable according to the documentation. "In filenames, separate multiple words using either underscores or dashes (snake_case or kebab-case: controllers/date_picker_controller.js, controllers/list-item-controller.js)."

System configuration

Rails version: Rails 7.0.0.alpha2

Ruby version: ruby 3.0.1p64

Stimulus-rails version: 0.5.4

Stimulus version: 3.0.0-beta.2

Release 0.5 busts the installer

Just tried installing on a new Rails 6.1 codebase. Running ./bin/rails stimulus:install didn't install the 'controllers' directory or change application.js. Rolling back to 0.4.2 got it all working fine.

Autoloading race conditions

I love the autoloader in conjunction with native ESM modules, but I'm running into a race condition that I suspect others will bump into.

In my project I have a canvas that is controlled by a stimulus controller - it allows you to draw on it when clicked.

When I run my capybara system tests, it's not difficult to have the tests fail because the stimulus controller attached to the canvas hasn't yet been loaded. Of course, this problem is pronounced in an automated testing scenario, but given different network conditions, it's not unreasonable to think it could pop up for regular usage situations as well.

I previously solved this problem by eager loading all my stimulus controllers, and suspect that's what I'll end up doing to work around this in the short-term. I'm curious what other thoughts there may be around how to handle this.

In this case, if there were some way to show a message/loading indicator on the canvas, that could help prevent issues of interacting before the stimulus controller had loaded, but I feel like that's not going to be generic enough to apply to every situation. My other thought is maybe to have the autoloader dispatch an event when a controller loads that you could listen to, but I don't love how manual that'd be.

For anyone else that's struggling with this, here's my code that allows you to eager load using native ESM:

  <%= javascript_include_tag("stimulus/libraries/es-module-shims", type: "module") %>
  <%= tag.script(type: "importmap-shim", src: asset_path("importmap.json")) %>
  <%= javascript_include_tag("turbo", type: "module-shim") %>
  <%= javascript_include_tag("stimulus/libraries/stimulus", type: "module-shim") %>
  <script type="module-shim" type=text/javascript>
    import { Application } from 'stimulus'

    const application = Application.start()
    <% controllers = Rails.root.join('app/assets/javascripts/controllers').children.map { |filename| filename.basename.to_s.remove(filename.extname).split('_controller').first } %>
    const controllerNames = <%= controllers.to_json.html_safe %>
    controllerNames.forEach((controllerName) => {
      let controllerFilename = `${controllerName}_controller`

      import(controllerFilename).then((controllerModule) => {
        application.register(controllerName.replace('_', '-'), controllerModule.default)
      }).catch(error => console.log(`Failed to autoload controller: ${controllerName}`, error))
    })
  </script>

Of course, you can always just use webpacker and bundle it all together for an easy eager-loading solution too.

rake stimulus:manifest import @hotwired/stimulus

Using the ./bin/rails stimulus:manifest:update command, the index.js file is automatically updated when all stimulus controllers. This include the application import at the top of the file:

import { application } from "./application"

import ModalController from "./modal_controller"
application.register("modal", ModalController)

When stimulus is installed through the @hotwired/stimulus gem, the auto-generated import lines should be:

import { Application } from "@hotwired/stimulus"
const application = Application.start()

import ModalController from "./modal_controller"
application.register("modal", ModalController)

Add manual installation instructions to readme

When running the installer with with rails stimulus:install on an existing project, it often fails to corretly update files to add the necessary lines of code, showing this error message: File unchanged! The supplied flag value not found!.

I think it would be helpful to add a section that explains the manual install to the readme, so when somebody installs it and sees that it failed to update a file correctly, they can just do it manually. In addition, we could add what it was supposed to do to the error message, something like:


File unchanged! The supplied flag value not found!  app/javascript/application.js

Wanted to add:
import "@hotwired/stimulus-loading"

to app/javascript/application.js, but couldn't parse the file correctly. Please add manually or retry.

Happy to look into this.

Controllers written in Coffeescript?

I'm fairly new to Rails, so I may just be missing something stupid. Using import maps, how can I write controllers in coffeescript?

I've installed the coffee-rails gem and set up a controller, but it seems that Stimulus just won't load the controller from this line eagerLoadControllersFrom("controllers", application).

I'm certain this is a problem with Coffeescript because manually compiling the Coffeescript and putting the output in a plain JS controller side-by-side with the Coffeescript controller works perfectly; the JS controller hooks up to my DOM element, the Coffeescript controller simply doesn't execute.

Here's my repo demonstrating this (ignore everything in /lib): https://github.com/Be-brand/game-of-life

0.4.0 (app/javascript/application.js does not appear to exist) for Rails without importmap

From version 0.4.0, stimulus:install tries to append import "./controllers" in app/javascript/application.js instead of app/javascript/packs/application.js when using the default install of Rails (with webpacker). This also affects hotwire-rails since it's using the latest version of Stimulus.

Rails version: 6.1.4.1

$ rails new myapp
$ย cd myapp
$ bundle add stimulus-rails
$ rails stimulus:install

Create controllers directory
      create  app/javascript/controllers
      create  app/javascript/controllers/index.js
      create  app/javascript/controllers/hello_controller.js
Import Stimulus controllers
rails aborted!
Thor::Error: The file /..../myapp/app/javascript/application.js does not appear to exist

Autoloader sometimes failing to load in Safari

tabs_controller.js not always auto-loading in Safari but works fine in Chrome.

NOTE: This could be a Safari issue and may have nothing todo with the auto loader.

Rails 6.0.3.4
gem 'hotwire-rails'
Safari Desktop Version 13.1.3 (15609.4.1) Fail but after hard refresh it Pass
Safari Mobile Version 14.0 Fail Still fails even after 'Clear History and Website data'
Chrome Desktop Version 87.0.4280.88 (Official Build) (x86_64) Pass

I am binding to 0.0.0.0 but did start rails in localhost mode and same error.

Desktop
Screen Shot 2020-12-29 at 7 11 06 pm

Mobile
Screen Shot 2020-12-29 at 7 50 36 pm

rails stimulus:manifest:update replacing our index.js configs

from the read me it told us too add this

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

the problem is whenever we use rails stimulus:manifest:update the settings is removed

TypeError: application.register is not a function

After upgrading from 0.4.1 to 0.4.2 I get a bunch of these in the console and, obviously, Stimulus is not working.

CleanShot 2021-09-06 at 11 02 49

The browser points me to then line in this function:

function registerControllerFromPath(path, under, application) {
  const name = path
    .replace(`${under}/`, "")
    .replace("_controller", "")
    .replace(/\//g, "--")
    .replace(/_/g, "-")

  import(path)
    .then(module => application.register(name, module.default))
    .catch(error => console.log(`Failed to register controller: ${name} (${path})`, error))
};

Probably an issue in stimulus itselfโ€ฆ

Don't limit stimulus:manifest:update to only .js files

rollup and webpack can perform transpiration of source in languages like coffeescript. The following monkeypatch will enable stimulus:manifest:update to not only pick up sources that strictly end in .js, but also controllers that end in .js.whatever. Perhaps it should be expanded further to support things like .ts?

module Stimulus::Manifest
  def import_and_register_controller(controllers_path, controller_path)
    controller_path = controller_path.relative_path_from(controllers_path).to_s
    module_path = controller_path.split('.').first
    controller_class_name = module_path.camelize.gsub(/::/, "__")
    tag_name = module_path.remove(/_controller/).gsub(/_/, "-").gsub(/\//, "--")

    <<~JS

      import #{controller_class_name} from "./#{controller_path}"
      application.register("#{tag_name}", #{controller_class_name})
    JS
  end

  def extract_controllers_from(directory)
    (directory.children.select { |e| e.to_s =~ /_controller\.js(\.\w+)?$/ } +
      directory.children.select(&:directory?).collect { |d| extract_controllers_from(d) }
    ).flatten.sort
  end
end

See https://www.ruby2js.com/examples/rails/stimulus_rollup and https://www.ruby2js.com/examples/stimulus/ for a complete walkthrough.

P.S. In any case, the regular expression /_controller.js/ should escape the period, thus: /_controller\.js/.

Fix output of bin/rails stimulus:install?

I believe the following output is perhaps a remnant of previous work?

Add app/javascripts to asset pipeline manifest
      append  app/assets/config/manifest.js

Everything I see indicates this should be Add app/assets/javascripts

Controllers are connected multiple times

When I have just one element with a controller, then everything works as expected and the controller gets connected just once. However, when I have n elements with the same controller, then each element is connected n times.

This can be reproduced with the following controller:

// app/assets/javascripts/controllers/hello_controller.js
import { Controller } from "stimulus"

export default class extends Controller {
  connect() {
    this.element.textContent = "Hello World!"
    console.log(this.element)
  }
}

It looks like this with two elements with data-controller="hello":

Screen.Recording.2020-12-25.at.00.31.54.mov

Getting Uncaught Error: Cannot find module 'controllers'

Hello there, I have just installed Hotwire gem and everything is working fine with turbo, but I could not manage to make stimulus work.

I am getting: Uncaught Error: Cannot find module 'controllers'

I am using:
Rails 6.0.3.4
Ruby 2.7.1
Stimulus-rails 0.2.2

This is my application.js

// This file is automatically compiled by Webpack, along with any other files
// present in this directory. You're encouraged to place your actual application logic in
// a relevant structure within app/javascript and only use these pack files to reference
// that code so it'll be compiled.

require("@rails/ujs").start()
require("@rails/activestorage").start()
require("channels")


// Uncomment to copy all static images under ../images to the output folder and reference
// them with the image_pack_tag helper in views (e.g <%= image_pack_tag 'rails.png' %>)
// or the `imagePath` JavaScript helper below.
//
// const images = require.context('../images', true)
// const imagePath = (name) => images(name, true)

import "controllers"

require("trix")
require("@rails/actiontext")

I have tried running rails stimulus:install

The error I get is:

Uncaught Error: Cannot find module 'controllers'
    at webpackMissingModule

Errors regarding es-module-shims & autoloading controllers thrown randomly

Hello, recently I've noticed that there are some errors thrown in the console from time to time when using Stimulus in a application that I am working on (using Rails 6 & no Webpacker).

I tried to reproduce this problem on a fresh app and it still happens.

Steps to reproduce this problem:

  1. create new rails project with --skip-webpack-install and --skip-javascript to get rid of Webpacker,
  2. add stimulus-rails gem,
  3. create a page on which you use hello controller generated by rails stimulus:install,
  4. if you refresh few times you should be able to see errors like this in the console
    image
    image

Browsers on which I managed to reproduce this problem: Firefox, Firefox Nightly, Firefox Developer Edition, Opera, MS Edge, Chrome for developers.

stimulus-rails installation rails - undefined method Stimulus::Rails.application

I have installed stimulus-rails gem on my rails 7 application. When I run the application, I get the following error.

The error is

Uncaught exception: undefined method `application' for Stimulus::Rails:Module

in lib/stimulus/engin.rb, Rails is somehow being interpreted as Stimulus::Rails instead of root class ::Rails.

Does anyone know how to handle this? Help would be greatly appreciated.

The following is the backtrace

=> Booting WEBrick
=> Rails 7.0.2.4 application starting in development http://0.0.0.0:3002
=> Run `bin/rails server --help` for more startup options
Exiting
Uncaught exception: undefined method `application' for Stimulus::Rails:Module
	/Users/jae/.rvm/gems/ruby-3.0.4/gems/stimulus-rails-1.0.0/lib/stimulus/engine.rb:4:in `block in <class:Engine>'
	/Users/jae/.rvm/gems/ruby-3.0.4/gems/railties-7.0.2.4/lib/rails/initializable.rb:32:in `instance_exec'
	/Users/jae/.rvm/gems/ruby-3.0.4/gems/railties-7.0.2.4/lib/rails/initializable.rb:32:in `run'
	/Users/jae/.rvm/gems/ruby-3.0.4/gems/railties-7.0.2.4/lib/rails/initializable.rb:61:in `block in run_initializers'
	/Users/jae/.rvm/rubies/ruby-3.0.4/lib/ruby/3.0.0/tsort.rb:228:in `block in tsort_each'
	/Users/jae/.rvm/rubies/ruby-3.0.4/lib/ruby/3.0.0/tsort.rb:350:in `block (2 levels) in each_strongly_connected_component'
	/Users/jae/.rvm/rubies/ruby-3.0.4/lib/ruby/3.0.0/tsort.rb:431:in `each_strongly_connected_component_from'
	/Users/jae/.rvm/rubies/ruby-3.0.4/lib/ruby/3.0.0/tsort.rb:349:in `block in each_strongly_connected_component'
	/Users/jae/.rvm/rubies/ruby-3.0.4/lib/ruby/3.0.0/tsort.rb:347:in `each'
	/Users/jae/.rvm/rubies/ruby-3.0.4/lib/ruby/3.0.0/tsort.rb:347:in `call'
	/Users/jae/.rvm/rubies/ruby-3.0.4/lib/ruby/3.0.0/tsort.rb:347:in `each_strongly_connected_component'
	/Users/jae/.rvm/rubies/ruby-3.0.4/lib/ruby/3.0.0/tsort.rb:226:in `tsort_each'
	/Users/jae/.rvm/rubies/ruby-3.0.4/lib/ruby/3.0.0/tsort.rb:205:in `tsort_each'
	/Users/jae/.rvm/gems/ruby-3.0.4/gems/railties-7.0.2.4/lib/rails/initializable.rb:60:in `run_initializers'
	/Users/jae/.rvm/gems/ruby-3.0.4/gems/railties-7.0.2.4/lib/rails/application.rb:372:in `initialize!'
	/Users/jae/src/reiance2/config/environment.rb:5:in `<main>'
	/Users/jae/.rvm/gems/ruby-3.0.4/gems/bootsnap-1.12.0/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:30:in `require'
	/Users/jae/.rvm/gems/ruby-3.0.4/gems/bootsnap-1.12.0/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:30:in `require'
	/Users/jae/.rvm/gems/ruby-3.0.4/gems/zeitwerk-2.6.0/lib/zeitwerk/kernel.rb:35:in `require'
	config.ru:3:in `block in <main>'
	/Users/jae/.rvm/gems/ruby-3.0.4/gems/rack-2.2.4/lib/rack/builder.rb:116:in `eval'
	/Users/jae/.rvm/gems/ruby-3.0.4/gems/rack-2.2.4/lib/rack/builder.rb:116:in `new_from_string'
	/Users/jae/.rvm/gems/ruby-3.0.4/gems/rack-2.2.4/lib/rack/builder.rb:105:in `load_file'
	/Users/jae/.rvm/gems/ruby-3.0.4/gems/rack-2.2.4/lib/rack/builder.rb:66:in `parse_file'
	/Users/jae/.rvm/gems/ruby-3.0.4/gems/rack-2.2.4/lib/rack/server.rb:349:in `build_app_and_options_from_config'
	/Users/jae/.rvm/gems/ruby-3.0.4/gems/rack-2.2.4/lib/rack/server.rb:249:in `app'
	/Users/jae/.rvm/gems/ruby-3.0.4/gems/rack-2.2.4/lib/rack/server.rb:422:in `wrapped_app'
	/Users/jae/.rvm/gems/ruby-3.0.4/gems/railties-7.0.2.4/lib/rails/commands/server/server_command.rb:76:in `log_to_stdout'
	/Users/jae/.rvm/gems/ruby-3.0.4/gems/railties-7.0.2.4/lib/rails/commands/server/server_command.rb:36:in `start'
	/Users/jae/.rvm/gems/ruby-3.0.4/gems/railties-7.0.2.4/lib/rails/commands/server/server_command.rb:143:in `block in perform'
	<internal:kernel>:90:in `tap'
	/Users/jae/.rvm/gems/ruby-3.0.4/gems/railties-7.0.2.4/lib/rails/commands/server/server_command.rb:134:in `perform'
	/Users/jae/.rvm/gems/ruby-3.0.4/gems/thor-1.2.1/lib/thor/command.rb:27:in `run'
	/Users/jae/.rvm/gems/ruby-3.0.4/gems/thor-1.2.1/lib/thor/invocation.rb:127:in `invoke_command'
	/Users/jae/.rvm/gems/ruby-3.0.4/gems/thor-1.2.1/lib/thor.rb:392:in `dispatch'
	/Users/jae/.rvm/gems/ruby-3.0.4/gems/railties-7.0.2.4/lib/rails/command/base.rb:87:in `perform'
	/Users/jae/.rvm/gems/ruby-3.0.4/gems/railties-7.0.2.4/lib/rails/command.rb:48:in `invoke'
	/Users/jae/.rvm/gems/ruby-3.0.4/gems/railties-7.0.2.4/lib/rails/commands.rb:18:in `<main>'
	/Users/jae/.rvm/gems/ruby-3.0.4/gems/bootsnap-1.12.0/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:30:in `require'
	/Users/jae/.rvm/gems/ruby-3.0.4/gems/bootsnap-1.12.0/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:30:in `require'
	/Users/jae/src/reiance2/bin/rails:9:in `<top (required)>'

Process finished with exit code 0

Release the latest version

@dhh can you release the latest version ๐Ÿ™๐Ÿผ ๐Ÿ™๐Ÿผ
Current release 0.2.2 doesn't work with webpacker since it is missing the definitions
(works from the the master, but... ๐Ÿ˜„ )

Add typescript support for rails stimulus generator

When using the stimulus generator, it always generates a .js regardless if you are using typescript for all your files. It would be great to be able to configure the generator to generate typescript or javascript files.

stimulus-loading Race Condition

In trying to use importmap with stimulus `stimulus-loading' and I've run into a challenge with race conditions.

For example, say I have the following code.

// send_controller.js
export default class extends Controller {
  connect() {
    this.dispatch('connected')
  }
}
// receive_controller.js
export default class extends Controller {
  catch() {
    console.log('catch');
  }
}
<div data-controller="receive send"
     data-action="send:connected->receive#catch"></div>
  1. send controller dispatches a connected event on connect()
  2. send controller event runs action receive#catch

In the case of importmap, sometimes the send controller loads and executes before receive controller loaded. The event is dispatched but nothing happens because the receive controller is not loaded.

I've explored other options:

Option 1: turbo:load@window->receive#catch

This works for visits, but not not if the content is loaded via turbo-frame or a FORM turbo-stream response with lazyLoadControllersFrom

Option 2: turbo:load@window->receive#catch turbo:frame-load@window->receive#catch

This works for visits and turbo-frame, but still not for FORM turbo-stream response with lazyLoadControllersFrom.

I thought perhaps I could tap into the solution outlined in #57, but the PR is closed.

Is there a solution I'm not aware of that works with visit, turbo-frame and turbo-stream scenarios? Perhaps an event which indicates that all the stimulus controllers have been loaded and ready to execute? Should stimulus hold off on executing javascript until controllers finish loading?

Mutation observer does not autoload controllers

This issue to to (re)report problems autoloading turbo-frame content (#14), and also to report problems with autoloading when the data-controller attribute of an element changes. This seems to indicate the mutation observer introduced in #11 isn't actually handling the changes it's supposed to.

Autoloading given turbo-frame content

When turbo-frame content is loaded, new nodes are added. For a mutation observer to pick up these changes, the observer needs to be able to handle changes to the child list (the option to watch attributes only applies to existing nodes, not added/removed ones). The problem is that the MutationRecord passed to a MutationObserver's callback has different properties depending on the type of mutation. That was the reason for this switch in #3 (there's also a similar example here). Currently though, only attribute mutation events are handled properly in the callback itselfโ€”not child list mutation events. You can replicate this quickly by adding puma temporarily to the Gemfile and running the dummy app, then adding a node with data-controller="hello".

Autoloading given data-controller attribute changes

The mutation observer callback currently calls this function, passing the element whose data-controller attribute has changed, but because Element.querySelectorAll() finds descendants of the element which match the selector, this doesn't return the element with the data-controller attribute, so nothing gets loaded. To replicate this, do the same as above, but instead change one of the existing nodes' data-controller attributes to hello.


There were tests in #3 to catch both of these cases (that was actually how I found out I needed to modify the callback for child list mutations). I know there's been disagreement about, e.g., the relative value of unit testing, and I know that there's a plan to effectively upstream the autoloader (and rewrite it in the process)โ€”I'm not dead set on any particular approach, but I do think we need to have some tests to make sure things actually work, even if these become redundant once stimulus gets the autoloader.

Action params not populating

I'm having an issue where my Stimulus controller is not picking up event params for a click action. I'm referencing https://stimulus.hotwired.dev/reference/actions#action-parameters to wire this up. Here's a simple setup I have:

View code:

<%= search_form_for @q, url: "#",  html: { data: { controller: "balance form" } } do |f| %>
...
  <tr data-action="click->balance#updateTargetRow" data-balance-id-param="2">
...

Stimulus controller code:

// app/javascript/controllers/balance_controller.js
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {

  updateTargetRow(event) {
    console.log(event.params) //always logs `{}`
    ...
  }
}

Accessing the param via event.currentTarget.dataset.balanceIdParam gives the expected value of "2" in this example. Is this a known issue or do I have something setup wrong?

Controller inheritance with importmap

Using stimulus 3.0.0 with importmap in rails 7-alpha2, I'm trying:

// application_controller.js
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  //...
}
// some_controller.js
import { ApplicationController } from "controllers/application_controller";

export default class extends ApplicationController {
  // ...
}

FF
Failed to register controller: some (controllers/some_controller) SyntaxError: import not found: ApplicationController stimulus-importmap-autoloader-7366d931317007a1e7e62c8dd8198dbc6d6b438207ff8d8539d06019597bf2f7.js:24:29

Chromium
Failed to register controller: some (controllers/some_controller) SyntaxError: The requested module 'controllers/application_controller' does not provide an export named 'ApplicationController'

Changing the parent controller to:

// application_controller.js
import { Controller } from "@hotwired/stimulus"

export class ApplicationController extends Controller {
  //...
}

Failed to register controller: application (controllers/application_controller) TypeError: controllerConstructor is undefined

Not sure is this is a bug on the importmap autoloader or I missing something?

Thanks in advance!

Turn of stimulus response in Rails console.

In Rails, Stimulus js response is visible in rails console, i.e. whole HTML page that it sends to client, I would like to turn it off as log is taking more space than it should, how can i achieve it, I couldn't find any solution
I have attached sample of console that is being showing in rails console.

stimulus

why not auto "rails stimulus:manifest:update"

like watching other js file changes, why not auto detected a new js file that created under the app/javascript/controllers, and then run command like rails stimulus:manifest:update?

Skip non JS files in the Importmap

Hi!

#3 Would you think about introduce a change in the ImportmapHelper to just import files ending in '.js' instead skip only folders and those starting with '.'?

Not sure if all JS libraries end with a .js but so far all stimulus controllers do. And this change will be really helpful when you load folders with a mix of files (not only .js) like in view_components with stimulus controllers.

This is what happen when you include the components folder:

{
	"imports": {
		"stimulus": "/assets/stimulus/libraries/stimulus-dcca1023dd079a97692e855a18618fc4827389a09a23b23c4ea8916f65d46f09.js",
		".keep": "/components/.keep",
		"card.html": "/components/card.html.slim",
		"card": "/components/card.rb",
		"card_controller": "/components/card_controller-6fb9f06f02cc9b20d81deda74ee422fbfe0ca5bba001ea35b3ecb599612caca6.js"
	}
}

Thanks!!!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.