Stimulus: Inherit from ApplicationController without importing for each controller?

Created on 19 Jun 2020  Â·  6Comments  Â·  Source: hotwired/stimulus

I know you can make use of JavaScript’s class inheritance to set up an “Application Controller” that will serve as the foundation for all of your controllers to build upon. How can you do this without calling import at the top of each file? After installing Stimulus in a fresh Rails app, you're left with a app/javascript file.

Storing controllers in app/javascript/all/controllers and extending the Stimulus controllers from ApplicationController usually would require an import. Is there a way to put a base ApplicationController in app/javascript/ that could be globally accessible in the Stimulus controllers without having to declare import at the top of each controller?

Before

import ApplicationController from "./application_controller";

export default class extends ApplicationController {
  sayHi () {
    super();
    console.log("Hello from a Custom controller");
  }
}

After

export default class extends ApplicationController {
  sayHi () {
    super();
    console.log("Hello from a Custom controller");
  }
}

Most helpful comment

Here’s the ProvidePlugin config we use with Webpacker (ApplicationController is in app/javascript/controllers/application_controller.js):

const { environment } = require('@rails/webpacker')
const webpack = require('webpack')

environment.plugins.append('Provide', new webpack.ProvidePlugin({
  ApplicationController: ['application_controller', 'default']
}))

All 6 comments

If I try to use the webpack ProvidePlugin and set ApplicationController as the identifier I get an error: Uncaught TypeError: Super expression must either be null or a function at _inherits (controllers sync \.js$:56)

config/webpack/environment.js

const { environment } = require('@rails/webpacker')
const webpack = require('webpack')

environment.plugins.append(
  'Provide',
   new webpack.ProvidePlugin({
     ApplicationController: 'all/application_controller',
   }),
);

environment.splitChunks()
module.exports = environment

Here’s the ProvidePlugin config we use with Webpacker (ApplicationController is in app/javascript/controllers/application_controller.js):

const { environment } = require('@rails/webpacker')
const webpack = require('webpack')

environment.plugins.append('Provide', new webpack.ProvidePlugin({
  ApplicationController: ['application_controller', 'default']
}))

Here’s the ProvidePlugin config we use with Webpacker (ApplicationController is in app/javascript/controllers/application_controller.js):

const { environment } = require('@rails/webpacker')
const webpack = require('webpack')

environment.plugins.append('Provide', new webpack.ProvidePlugin({
  ApplicationController: ['application_controller', 'default']
}))

Thanks @georgeclaghorn ! I think the default path for ProvidePlugin is node_modules. Do you do anything else to specify that? I see there is another option to specify full path:

const path = require('path');

new webpack.ProvidePlugin({
  identifier: path.resolve(path.join(__dirname, 'src/module1'))
  // ...
});

Off the top of my head you could update config/webpacker.yml to lookup additional paths:

default: &default
  # Additional paths webpack should lookup modules
  # ['app/assets', 'engine/foo/app/assets']
  resolved_paths: ['app/javascript']

Minor correction: the file is in app/javascript:

// app/javascript/application_controller.js

import { Controller } from "stimulus"

export default class extends Controller {
  // …
}

Thanks @javan. Is resolved_path the recommended way to load that in?

app/javascript is a default load path so it should work out of the box. We're using fairly stock Webpacker setup.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

incompletude picture incompletude  Â·  3Comments

chocnut picture chocnut  Â·  4Comments

merwok picture merwok  Â·  4Comments

dwightwatson picture dwightwatson  Â·  3Comments

thooams picture thooams  Â·  4Comments