If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeThis search_preview
controller has been an awesome example for us to learn how to make great-looking, functional things in Stimulus. But I have a confession: we didn't really need to do all this work! Why? Because someone already created an open source autocomplete Stimulus controller!
Head to https://yarnpkg.com and search for "stimulus". You'll actually find a lot of Stimulus tools here that you can look through. What we're looking for is called stimulus-autocomplete. Here it is. Head to GitHub for find its documentation. Ooh! A GIF! Or.. GIF! This looks like exactly what we want!
Before we implement this package, this is just one of many pre-made Stimulus controllers that exists out there. Let's take a quick tour-de-tools!
First up is Stimulus Components: a collection of a bunch of controllers... each with a fancy demo! Let's pick a random one - how about this Lightbox controller - and click into its demo. Yup! If you need to display photos in a Lightbox, there's a pre-made controller for that!
If you use Tailwind CSS, check out the TailwindCSS Stimulus Components. It also has a demo with fun stuff inside, like a slide over, modals, tab functionality down here and more.
Want to add keyboard shortcuts to your Stimulus controller? There's a tool for that: stimulus-hotkeys.
What about a date picker? Try the stimulus-flatpickr controller, which integrates the flatpickr JavaScript library.
And... that's not even everything! You can check out the Awesome Stimulus GitHub resource for more libraries, reading, podcasts, etc about Stimulus.
One last interesting resource is betterstimulus.com, which holds a bunch of interesting patterns and best practices around Stimulus.
Anyways, let's get back to integrating this autocomplete controller. First, we need to install it! Copy the "yarn add" command, find your terminal and run:
yarn add "stimulus-autocomplete@2" --dev
So... once this finishes... how are we going to actually use this new controller? When we add a file to the assets/controllers/
directory, Stimulus automatically registers that as a controller, which means we can add a matching data-controller
element to the page and it will work. When we install a Symfony UX package, the controllers.json
does the same thing for those controllers.
{ | |
"controllers": { | |
"@symfony/ux-chartjs": { | |
"chart": { | |
"enabled": true, | |
"fetch": "lazy" | |
} | |
} | |
}, | |
"entrypoints": [] | |
} |
But what about the controller that we just installed? How do we register that with our Stimulus app? Back at the docs, under "Usage", you can see that they import the Autocomplete
package... then call some application.register()
thing to register this one controller.
But... which file should we add this code to? The answer is assets/bootstrap.js
. Notice that this looks pretty similar to the code example, though not exactly the same. The application
variable in the docs is the same as the app
variable that we have in our file.
import { startStimulusApp } from '@symfony/stimulus-bridge'; | |
// Registers Stimulus controllers from controllers.json and in the controllers/ directory | |
export const app = startStimulusApp(require.context( | |
'@symfony/stimulus-bridge/lazy-controller-loader!./controllers', | |
true, | |
/\.(j|t)sx?$/ | |
)); |
On the docs, copy the import
line and... pop that onto the top of our file. Then, to register the controller, down here say: app.register('autocomplete')
- that name could be anything... and will determine the data-controller
that will be used to connect to this controller in HTML - then Autocomplete
.
... line 1 | |
import { Autocomplete } from 'stimulus-autocomplete'; | |
... lines 3 - 9 | |
app.register('autocomplete', Autocomplete); | |
... lines 11 - 13 |
Congratulations! With 1 command and 2 lines of code, we now have a new controller called autocomplete
available in our app!
Next: let's use this controller instead of our search-preview
controller. It's going to be, well, refreshingly easy to drop in. Also: can we make this third-party controller load lazily... like we've done with some other controllers? Totally!
Hey Tomasz-I !
Hmm, that's an interesting question! You have a few options:
a) Simplest: Manually register them. With this solution, you're not loading an entire directory from a bundle, you just register the controllers you need one-by-one. I mention this first, because it's perfectly straightforward. In bootstrap.js
:
import SomeImportedController from '../vendor/myname/bundle/Resources/assets/controller/some_controller.js';
// all the normal stuff
app.register('some_controller_name', SomeImportedController);
b) Next simplest, and more of what you are after: importing an entire directory. The startStimulusApp()
function doesn't really have an "open door" to make this easy, so you just need to do it a bit more manually :).
import { startStimulusApp } from '@symfony/stimulus-bridge';
import { definitionsFromContext } from '@hotwired/stimulus-webpack-helpers';
// Registers Stimulus controllers from controllers.json and in the controllers/ directory
export const app = startStimulusApp(require.context(
'@symfony/stimulus-bridge/lazy-controller-loader!./controllers',
true,
/\.[jt]sx?$/
));
app.load(definitionsFromContext(require.context(
'@symfony/stimulus-bridge/lazy-controller-loader!../vendor/myname/bundle/Resources/assets/controller',
true,
/\.[jt]sx?$/
)));
I haven't tested that (so let me know!) but that should to the trick. The definitionsFromContext
is normally called FOR you inside of startStimulusApp
. So basically, this extra stuff at the bottom is replicating what happens inside of that function.
c) the fancy option. Cause, we like fancy things, right!
This option involves treating your application like a "ux package". This is two steps:
1) Add your "bundle" to package.json as a "package" under the "require"
"@vendor/your-bundle-name": "file: vendor/myname/bundle/Resources/assets"
Then run yarn install -f
or npm install --force
. You can actually use this part of the solution with solutions (a) and (b) if you want. It just means that, instead of referring to the paths with ../vednor/myname/bundle...
, you can reference as @vendor/your-bundle-name/controller
... which is kind of cool :).
Anyways, to register your controllers, you would update your controllers.json
file to point at them:
{
"controllers": {
"@vendor/your-bundle-name": {
"controller1": {
"enabled": true,
"fetch": "eager"
}
}
},
"entrypoints": []
}
This would create a controller called "@vendor/your-bundle-name/controller1". To tell the system exactly where this file lives, in your vendor/myname/bundle/Resources/assets
directory, you would have a package.json
file that would include, among the normal stuff, this:
"symfony": {
"controllers": {
"controller1": {
"main": "dist/controller1.js",
"webpackMode": "eager",
"fetch": "eager",
"enabled": true
}
}
},
The disadvantage of this is that... it's more work! And you can't load the entire directory automatically. The advantage would be if you are releasing a bundle that you would share with this world. By adding all of this config, when the user installs your bundle, their package.json
and controllers.json
files would automatically update, and they would get the new controller by doing zero other setup.
Phew! Probably a longer answer than you wanted, but I really liked the question :).
Cheers!
I'm in the same class as Jay - trying to stay current with Symfony just for "fun" since 2.3. In a project in Symfony 6.0.6 & yarn 3.2.0, I tried to add
stimulus-autocomplete@2. Compiling webpack failed with:
"./node_modules/stimulus-autocomplete/dist/stimulus-autocomplete.js" contains a reference to the file "stimulus". This file can not be found, please check it for typos or update it if the file got moved.
So I did "yarn remove stimulus-autocomplete", followed by "yarn add stimulus-autocomple@3". Webpack compiled nicely.
Hey geoB!
> I'm in the same class as Jay - trying to stay current with Symfony just for "fun" since 2.3
That's impressive!
> In a project in Symfony 6.0.6 & yarn 3.2.0, I tried to add
stimulus-autocomplete@2. Compiling webpack failed with:
Yup, if you're in a fresh Symfony project (which will be using Stimulus 3), then you want the latest stimulus-autocomplete version (version 3). We recommended installing version 2 in this tutorial only because the actual code I'm using (and the code in the course download) using Stimulus 2. To be it simply: Stimulus 2 ❤️ stimulus-autocomplete 2, and Stimulus 3 ❤️ stimulus-autocomplete 3.
Cheers!
I had to "yarn add stimulus-autocomplete@2" to get javascripts to build without error. Latest stimulus-autocomplete is 3, which has stimulus 3 as a dependency. But when upgraded stimulus then stimulus-bridge was not happy, and once again javascripts would not build.
As an aside, any ballpark timeframe on getting stimulus-bridge to work with stimulus 3, Ryan? OR, does stimulus 3 add anything worth being hurried about? Maybe stick with stimulus 2?
Hey @Jay Gee!
Ah, yay! stimulus-autocomplete has been updated for Stimulus 3 - that's good news :). So, some excellent questions here. Let me do my best to answer them:
> I had to "yarn add stimulus-autocomplete@2" to get javascripts to build without error.
Thanks - we'll update the script & video to show that :).
> As an aside, any ballpark timeframe on getting stimulus-bridge to work with stimulus 3, Ryan?
All the PR's to do this already exist - https://github.com/symfony/... and https://github.com/symfony/.... We were mostly waiting for the ecosystem to start releasing support - like stimulus-autocomplete, but also stimulus-use (which has not released support yet) https://github.com/stimulus...
> OR, does stimulus 3 add anything worth being hurried about? Maybe stick with stimulus 2?
Stimulus 3 is... no big deal! They released a new major version basically because thy changed the name of the library from "stimulus" to "@hotwired/stimulus". If it weren't for that, v3 would be v 2.1. They DID add a couple of nice features - https://world.hey.com/hotwi... - but it's all pretty minor stuff.
Anyways, once Symfony UX DOES give you Stimulus v3, we'll be adding some notes to this tutorial to mention some things... but Stimulus v2 is basically identical to Stimulus v3.
Cheers!
Awesome, thanks for the swift and thorough reply Ryan! I will add that Symfonycasts (I started when it was KNP U) is a Godsend for me. I'm so thankful for you, Leanna, and the entire Symfonycast crew! I've been following Symfony since 2.3. Completely self-taught and it's not my everyday job. I will also say that Stimulus integration has been the most exciting thing for me since learning Symfony!
Hey @Jay Gee!
Ah, you've been around since Symfony 2.3! It's been awhile - and things were pretty crazy way back then. Thanks for sticking with us - even more impressive if this isn't your everyday job! It should be! ;)
> I will also say that Stimulus integration has been the most exciting thing for me since learning Symfony!
I feel that same way - along with Turbo. It's completely changed the JavaScript paradigm for me, and allowed me to get that single-page-app experience with a "normal" app. We've been prepping the SymfonyCasts codebase for Turbo (mostly be refactoring legacy JavaScript into Stimulus, of course) - and I'm super excited.
Cheers!
// composer.json
{
"require": {
"php": ">=8.1",
"ext-ctype": "*",
"ext-iconv": "*",
"composer/package-versions-deprecated": "1.11.99.1", // 1.11.99.1
"doctrine/annotations": "^1.0", // 1.11.1
"doctrine/doctrine-bundle": "^2.2", // 2.2.3
"doctrine/doctrine-migrations-bundle": "^3.0", // 3.0.2
"doctrine/orm": "^2.8", // 2.8.1
"phpdocumentor/reflection-docblock": "^5.2", // 5.2.2
"sensio/framework-extra-bundle": "^5.6", // v5.6.1
"symfony/asset": "5.2.*", // v5.2.3
"symfony/console": "5.2.*", // v5.2.3
"symfony/dotenv": "5.2.*", // v5.2.3
"symfony/flex": "^1.3.1", // v1.18.5
"symfony/form": "5.2.*", // v5.2.3
"symfony/framework-bundle": "5.2.*", // v5.2.3
"symfony/property-access": "5.2.*", // v5.2.3
"symfony/property-info": "5.2.*", // v5.2.3
"symfony/proxy-manager-bridge": "5.2.*", // v5.2.3
"symfony/security-bundle": "5.2.*", // v5.2.3
"symfony/serializer": "5.2.*", // v5.2.3
"symfony/twig-bundle": "5.2.*", // v5.2.3
"symfony/ux-chartjs": "^1.1", // v1.2.0
"symfony/validator": "5.2.*", // v5.2.3
"symfony/webpack-encore-bundle": "^1.9", // v1.11.1
"symfony/yaml": "5.2.*", // v5.2.3
"twig/extra-bundle": "^2.12|^3.0", // v3.2.1
"twig/intl-extra": "^3.2", // v3.2.1
"twig/twig": "^2.12|^3.0" // v3.2.1
},
"require-dev": {
"doctrine/doctrine-fixtures-bundle": "^3.4", // 3.4.0
"symfony/debug-bundle": "^5.2", // v5.2.3
"symfony/maker-bundle": "^1.27", // v1.30.0
"symfony/monolog-bundle": "^3.0", // v3.6.0
"symfony/stopwatch": "^5.2", // v5.2.3
"symfony/var-dumper": "^5.2", // v5.2.3
"symfony/web-profiler-bundle": "^5.2" // v5.2.3
}
}
// package.json
{
"devDependencies": {
"@babel/preset-react": "^7.0.0", // 7.12.13
"@popperjs/core": "^2.9.1", // 2.9.1
"@symfony/stimulus-bridge": "^2.0.0", // 2.1.0
"@symfony/ux-chartjs": "file:vendor/symfony/ux-chartjs/Resources/assets", // 1.1.0
"@symfony/webpack-encore": "^1.0.0", // 1.0.4
"bootstrap": "^5.0.0-beta2", // 5.0.0-beta2
"core-js": "^3.0.0", // 3.8.3
"jquery": "^3.6.0", // 3.6.0
"react": "^17.0.1", // 17.0.1
"react-dom": "^17.0.1", // 17.0.1
"regenerator-runtime": "^0.13.2", // 0.13.7
"stimulus": "^2.0.0", // 2.0.0
"stimulus-autocomplete": "^2.0.1-phylor-6095f2a9", // 2.0.1-phylor-6095f2a9
"stimulus-use": "^0.24.0-1", // 0.24.0-1
"sweetalert2": "^10.13.0", // 10.14.0
"webpack-bundle-analyzer": "^4.4.0", // 4.4.0
"webpack-notifier": "^1.6.0" // 1.13.0
}
}
Hey @weaverryan do you have a solution you could share to somehow include controllers directory that is stored in a bundle? So except for including assets/controllers also to include vendor/myname/bundle/Resources/assets/controller? I cannot figure it out.