diff --git a/config/help/MakeStimulusController.txt b/config/help/MakeStimulusController.txt
index 0785f9733..33e021376 100644
--- a/config/help/MakeStimulusController.txt
+++ b/config/help/MakeStimulusController.txt
@@ -1,5 +1,15 @@
-The %command.name% command generates new Stimulus Controller.
+The %command.name% command generates a new Stimulus controller.
php %command.full_name% hello
-If the argument is missing, the command will ask for the controller name interactively.
\ No newline at end of file
+If the argument is missing, the command will ask for the controller name interactively.
+
+To generate a TypeScript file (instead of a JavaScript file) use the --typescript
+(or --ts) option:
+
+php %command.full_name% hello --typescript
+
+It will also interactively ask for values, targets, classes to add to the Stimulus
+controller (optional).
+
+php %command.full_name%
diff --git a/src/Maker/MakeStimulusController.php b/src/Maker/MakeStimulusController.php
index 0610c7d3b..103ffaa60 100644
--- a/src/Maker/MakeStimulusController.php
+++ b/src/Maker/MakeStimulusController.php
@@ -19,6 +19,7 @@
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Question\Question;
use Symfony\UX\StimulusBundle\StimulusBundle;
use Symfony\WebpackEncoreBundle\WebpackEncoreBundle;
@@ -44,8 +45,11 @@ public function configureCommand(Command $command, InputConfiguration $inputConf
{
$command
->addArgument('name', InputArgument::REQUIRED, 'The name of the Stimulus controller (e.g. hello>)')
+ ->addOption('typescript', 'ts', InputOption::VALUE_NONE, 'Create a TypeScript controller (default is JavaScript)')
->setHelp($this->getHelpFileContents('MakeStimulusController.txt'))
;
+
+ $inputConfig->setArgumentAsNonInteractive('typescript');
}
public function interact(InputInterface $input, ConsoleStyle $io, Command $command): void
@@ -53,16 +57,22 @@ public function interact(InputInterface $input, ConsoleStyle $io, Command $comma
$command->addArgument('extension', InputArgument::OPTIONAL);
$command->addArgument('targets', InputArgument::OPTIONAL);
$command->addArgument('values', InputArgument::OPTIONAL);
+ $command->addArgument('classes', InputArgument::OPTIONAL);
+
+ if ($input->getOption('typescript')) {
+ $input->setArgument('extension', 'ts');
+ } else {
+ $chosenExtension = $io->choice(
+ 'Language (JavaScript> or TypeScript>)',
+ [
+ 'js' => 'JavaScript',
+ 'ts' => 'TypeScript',
+ ],
+ 'js',
+ );
- $chosenExtension = $io->choice(
- 'Language (JavaScript> or TypeScript>)',
- [
- 'js' => 'JavaScript',
- 'ts' => 'TypeScript',
- ]
- );
-
- $input->setArgument('extension', $chosenExtension);
+ $input->setArgument('extension', $chosenExtension);
+ }
if ($io->confirm('Do you want to include targets?')) {
$targets = [];
@@ -98,16 +108,35 @@ public function interact(InputInterface $input, ConsoleStyle $io, Command $comma
$input->setArgument('values', $values);
}
+
+ if ($io->confirm('Do you want to add classes?', false)) {
+ $classes = [];
+ $isFirstClass = true;
+
+ while (true) {
+ $newClass = $this->askForNextClass($io, $classes, $isFirstClass);
+ if (null === $newClass) {
+ break;
+ }
+
+ $isFirstClass = false;
+ $classes[] = $newClass;
+ }
+
+ $input->setArgument('classes', $classes);
+ }
}
public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator): void
{
$controllerName = Str::asSnakeCase($input->getArgument('name'));
$chosenExtension = $input->getArgument('extension');
- $targets = $input->getArgument('targets');
- $values = $input->getArgument('values');
+ $targets = $targetArgs = $input->getArgument('targets') ?? [];
+ $values = $valuesArg = $input->getArgument('values') ?? [];
+ $classes = $classesArgs = $input->getArgument('classes') ?? [];
$targets = empty($targets) ? $targets : \sprintf("['%s']", implode("', '", $targets));
+ $classes = $classes ? \sprintf("['%s']", implode("', '", $classes)) : null;
$fileName = \sprintf('%s_controller.%s', $controllerName, $chosenExtension);
$filePath = \sprintf('assets/controllers/%s', $fileName);
@@ -118,6 +147,7 @@ public function generate(InputInterface $input, ConsoleStyle $io, Generator $gen
[
'targets' => $targets,
'values' => $values,
+ 'classes' => $classes,
]
);
@@ -128,7 +158,12 @@ public function generate(InputInterface $input, ConsoleStyle $io, Generator $gen
$io->text([
'Next:',
\sprintf('- Open %s and add the code you need', $filePath),
- 'Find the documentation at https://github.com/symfony/stimulus-bridge>',
+ '- Use the controller in your templates:',
+ ...array_map(
+ fn (string $line): string => " $line",
+ explode("\n", $this->generateUsageExample($controllerName, $targetArgs, $valuesArg, $classesArgs)),
+ ),
+ 'Find the documentation at https://symfony.com/bundles/StimulusBundle>',
]);
}
@@ -215,6 +250,29 @@ private function askForNextValue(ConsoleStyle $io, array $values, bool $isFirstV
return ['name' => $valueName, 'type' => $type];
}
+ /** @param string[] $classes */
+ private function askForNextClass(ConsoleStyle $io, array $classes, bool $isFirstClass): ?string
+ {
+ $questionText = 'New class name (press to stop adding classes)';
+
+ if (!$isFirstClass) {
+ $questionText = 'Add another class? Enter the class name (or press to stop adding classes)';
+ }
+
+ $className = $io->ask($questionText, validator: function (?string $name) use ($classes) {
+ if (str_contains($name, ' ')) {
+ throw new \InvalidArgumentException('Class name cannot contain spaces.');
+ }
+ if (\in_array($name, $classes, true)) {
+ throw new \InvalidArgumentException(\sprintf('The "%s" class already exists.', $name));
+ }
+
+ return $name;
+ });
+
+ return $className ?: null;
+ }
+
private function printAvailableTypes(ConsoleStyle $io): void
{
foreach ($this->getValuesTypes() as $type) {
@@ -234,6 +292,51 @@ private function getValuesTypes(): array
];
}
+ /**
+ * @param array $targets
+ * @param array $values
+ * @param array $classes
+ */
+ private function generateUsageExample(string $name, array $targets, array $values, array $classes): string
+ {
+ $slugify = fn (string $name) => str_replace('_', '-', Str::asSnakeCase($name));
+ $controller = $slugify($name);
+
+ $htmlTargets = [];
+ foreach ($targets as $target) {
+ $htmlTargets[] = \sprintf('', $controller, $target);
+ }
+
+ $htmlValues = [];
+ foreach ($values as ['name' => $name, 'type' => $type]) {
+ $value = match ($type) {
+ 'Array' => '[]',
+ 'Boolean' => 'false',
+ 'Number' => '123',
+ 'Object' => '{}',
+ 'String' => 'abc',
+ default => '',
+ };
+ $htmlValues[] = \sprintf('data-%s-%s-value="%s"', $controller, $slugify($name), $value);
+ }
+
+ $htmlClasses = [];
+ foreach ($classes as $class) {
+ $value = Str::asLowerCamelCase($class);
+ $htmlClasses[] = \sprintf('data-%s-%s-class="%s"', $controller, $slugify($class), $value);
+ }
+
+ return \sprintf(
+ '%s%s
',
+ $controller,
+ $htmlValues ? ("\n ".implode("\n ", $htmlValues)) : '',
+ $htmlClasses ? ("\n ".implode("\n ", $htmlClasses)) : '',
+ ($htmlValues || $htmlClasses) ? "\n" : '',
+ $htmlTargets ? ("\n ".implode("\n ", $htmlTargets)) : '',
+ "\n \n",
+ );
+ }
+
public function configureDependencies(DependencyBuilder $dependencies): void
{
// lower than 8.1, allow WebpackEncoreBundle
diff --git a/templates/stimulus/Controller.tpl.php b/templates/stimulus/Controller.tpl.php
index 555e38da9..a506ee445 100644
--- a/templates/stimulus/Controller.tpl.php
+++ b/templates/stimulus/Controller.tpl.php
@@ -2,8 +2,9 @@
/*
* The following line makes this controller "lazy": it won't be downloaded until needed
-* See https://github.com/symfony/stimulus-bridge#lazy-controllers
+* See https://symfony.com/bundles/StimulusBundle/current/index.html#lazy-stimulus-controllers
*/
+
/* stimulusFetch: 'lazy' */
export default class extends Controller {
= $targets ? " static targets = $targets\n" : "" ?>
@@ -14,5 +15,33 @@
}
- // ...
+= $classes ? " static classes = $classes\n" : '' ?>
+
+ initialize() {
+ // Called once when the controller is first instantiated (per element)
+
+ // Here you can initialize variables, create scoped callables for event
+ // listeners, instantiate external libraries, etc.
+ // this._fooBar = this.fooBar.bind(this)
+ }
+
+ connect() {
+ // Called every time the controller is connected to the DOM
+ // (on page load, when it's added to the DOM, moved in the DOM, etc.)
+
+ // Here you can add event listeners on the element or target elements,
+ // add or remove classes, attributes, dispatch custom events, etc.
+ // this.fooTarget.addEventListener('click', this._fooBar)
+ }
+
+ // Add custom controller actions here
+ // fooBar() { this.fooTarget.classList.toggle(this.bazClass) }
+
+ disconnect() {
+ // Called anytime its element is disconnected from the DOM
+ // (on page change, when it's removed from or moved in the DOM, etc.)
+
+ // Here you should remove all event listeners added in "connect()"
+ // this.fooTarget.removeEventListener('click', this._fooBar)
+ }
}
diff --git a/tests/Maker/MakeStimulusControllerTest.php b/tests/Maker/MakeStimulusControllerTest.php
index 69183d695..647864849 100644
--- a/tests/Maker/MakeStimulusControllerTest.php
+++ b/tests/Maker/MakeStimulusControllerTest.php
@@ -24,6 +24,19 @@ protected function getMakerClass(): string
public function getTestDetails(): \Generator
{
+ yield 'it_generates_stimulus_controller' => [$this->createMakerTest()
+ ->run(function (MakerTestRunner $runner) {
+ $runner->runMaker(
+ [
+ 'default', // controller name
+ ],
+ );
+
+ $generatedFilePath = $runner->getPath('assets/controllers/default_controller.js');
+ $this->assertFileExists($generatedFilePath);
+ }),
+ ];
+
yield 'it_generates_stimulus_controller_with_targets' => [$this->createMakerTest()
->run(function (MakerTestRunner $runner) {
$runner->runMaker(
@@ -74,16 +87,192 @@ public function getTestDetails(): \Generator
}),
];
- yield 'it_generates_typescript_stimulus_controller' => [$this->createMakerTest()
+ yield 'it_generates_stimulus_controller_with_values' => [$this->createMakerTest()
+ ->run(function (MakerTestRunner $runner) {
+ $runner->runMaker(
+ [
+ 'with_values', // controller name
+ 'js', // controller language
+ 'no', // no targets
+ 'yes', // values
+ 'min', // first value
+ 'Number', // first value type
+ 'email', // second values
+ 'String', // second value type
+ '', // empty input to stop adding values
+ ]);
+
+ $generatedFilePath = $runner->getPath('assets/controllers/with_values_controller.js');
+
+ $this->assertFileExists($generatedFilePath);
+
+ $generatedFileContents = file_get_contents($generatedFilePath);
+ $expectedContents = file_get_contents(__DIR__.'/../fixtures/make-stimulus-controller/with_values.js');
+
+ $this->assertSame(
+ $expectedContents,
+ $generatedFileContents
+ );
+ }),
+ ];
+
+ yield 'it_generates_stimulus_controller_with_classes' => [$this->createMakerTest()
+ ->run(function (MakerTestRunner $runner) {
+ $runner->runMaker(
+ [
+ 'with_classes', // controller name
+ 'js', // use default extension (js)
+ 'no', // do not add targets
+ 'no', // do not add values
+ 'yes', // add classes
+ 'foo', // first class
+ 'bar', // second class
+ '', // empty input to stop adding classes
+ ]);
+
+ $generatedFilePath = $runner->getPath('assets/controllers/with_classes_controller.js');
+
+ $this->assertFileExists($generatedFilePath);
+
+ $generatedFileContents = file_get_contents($generatedFilePath);
+ $expectedContents = file_get_contents(__DIR__.'/../fixtures/make-stimulus-controller/with_classes.js');
+
+ $this->assertSame(
+ $expectedContents,
+ $generatedFileContents
+ );
+ }),
+ ];
+
+ yield 'it_generates_stimulus_controller_with_targets_values_and_classes' => [$this->createMakerTest()
+ ->run(function (MakerTestRunner $runner) {
+ $runner->runMaker(
+ [
+ 'with_targets_values_classes',
+ 'js',
+ 'yes', // add targets
+ 'aaa',
+ 'bbb',
+ '', // end
+ 'yes', // add values
+ 'ccc',
+ 'Number',
+ 'ddd',
+ 'String',
+ '', // end
+ 'yes', // add classes
+ 'eee',
+ 'fff',
+ '', // end
+ ]);
+
+ $generatedFilePath = $runner->getPath('assets/controllers/with_targets_values_classes_controller.js');
+
+ $this->assertFileExists($generatedFilePath);
+
+ $generatedFileContents = file_get_contents($generatedFilePath);
+ $expectedContents = file_get_contents(__DIR__.'/../fixtures/make-stimulus-controller/with_targets_values_classes.js');
+
+ $this->assertSame(
+ $expectedContents,
+ $generatedFileContents
+ );
+ }),
+ ];
+
+ yield 'it_generates_typescript_stimulus_controller_interactively' => [$this->createMakerTest()
->run(function (MakerTestRunner $runner) {
$runner->runMaker(
[
'typescript', // controller name
'ts', // controller language
'no', // do not add targets
- ]);
+ ],
+ );
+
+ $this->assertFileExists($runner->getPath('assets/controllers/typescript_controller.ts'));
+ $this->assertFileDoesNotExist($runner->getPath('assets/controllers/typescript_controller.js'));
+ }),
+ ];
+
+ yield 'it_generates_typescript_stimulus_controller_when_option_is_set' => [$this->createMakerTest()
+ ->run(function (MakerTestRunner $runner) {
+ $runner->runMaker(
+ [
+ 'typescript', // controller name
+ // '', // language is not asked interactively
+ 'no', // do not add targets
+ ],
+ ' --typescript'
+ );
$this->assertFileExists($runner->getPath('assets/controllers/typescript_controller.ts'));
+ $this->assertFileDoesNotExist($runner->getPath('assets/controllers/typescript_controller.js'));
+ }),
+ ];
+
+ yield 'it_displays_controller_basic_usage_example' => [$this->createMakerTest()
+ ->run(function (MakerTestRunner $runner) {
+ $output = $runner->runMaker(
+ [
+ 'fooBar',
+ 'js',
+ ],
+ );
+
+ $usageExample = <<
+
+
+ HTML;
+
+ $this->assertStringContainsString('- Use the controller in your templates:', $output);
+ foreach (explode("\n", $usageExample) as $line) {
+ $this->assertStringContainsString($line, $output);
+ }
+ }),
+ ];
+
+ yield 'it_displays_controller_complete_usage_example' => [$this->createMakerTest()
+ ->run(function (MakerTestRunner $runner) {
+ $output = $runner->runMaker(
+ [
+ 'fooBar',
+ 'js',
+ 'yes', // add targets
+ 'firstOne',
+ 'secondOne',
+ '',
+ 'yes', // add values
+ 'minItems',
+ 'Number',
+ 'email',
+ 'String',
+ '',
+ 'yes', // add classes
+ 'isVisible',
+ 'hidden',
+ '',
+ ],
+ );
+
+ $usageExample = <<
+
+
+
+
+ HTML;
+
+ $this->assertStringContainsString('- Use the controller in your templates:', $output);
+ foreach (explode("\n", $usageExample) as $line) {
+ $this->assertStringContainsString($line, $output);
+ }
}),
];
}
diff --git a/tests/fixtures/make-stimulus-controller/with_classes.js b/tests/fixtures/make-stimulus-controller/with_classes.js
new file mode 100644
index 000000000..7fd71e0bc
--- /dev/null
+++ b/tests/fixtures/make-stimulus-controller/with_classes.js
@@ -0,0 +1,39 @@
+import { Controller } from '@hotwired/stimulus';
+
+/*
+* The following line makes this controller "lazy": it won't be downloaded until needed
+* See https://symfony.com/bundles/StimulusBundle/current/index.html#lazy-stimulus-controllers
+*/
+
+/* stimulusFetch: 'lazy' */
+export default class extends Controller {
+ static classes = ['foo', 'bar']
+
+ initialize() {
+ // Called once when the controller is first instantiated (per element)
+
+ // Here you can initialize variables, create scoped callables for event
+ // listeners, instantiate external libraries, etc.
+ // this._fooBar = this.fooBar.bind(this)
+ }
+
+ connect() {
+ // Called every time the controller is connected to the DOM
+ // (on page load, when it's added to the DOM, moved in the DOM, etc.)
+
+ // Here you can add event listeners on the element or target elements,
+ // add or remove classes, attributes, dispatch custom events, etc.
+ // this.fooTarget.addEventListener('click', this._fooBar)
+ }
+
+ // Add custom controller actions here
+ // fooBar() { this.fooTarget.classList.toggle(this.bazClass) }
+
+ disconnect() {
+ // Called anytime its element is disconnected from the DOM
+ // (on page change, when it's removed from or moved in the DOM, etc.)
+
+ // Here you should remove all event listeners added in "connect()"
+ // this.fooTarget.removeEventListener('click', this._fooBar)
+ }
+}
diff --git a/tests/fixtures/make-stimulus-controller/with_targets.js b/tests/fixtures/make-stimulus-controller/with_targets.js
index d78f58f25..6c80bb339 100644
--- a/tests/fixtures/make-stimulus-controller/with_targets.js
+++ b/tests/fixtures/make-stimulus-controller/with_targets.js
@@ -2,10 +2,38 @@ import { Controller } from '@hotwired/stimulus';
/*
* The following line makes this controller "lazy": it won't be downloaded until needed
-* See https://github.com/symfony/stimulus-bridge#lazy-controllers
+* See https://symfony.com/bundles/StimulusBundle/current/index.html#lazy-stimulus-controllers
*/
+
/* stimulusFetch: 'lazy' */
export default class extends Controller {
static targets = ['results', 'messages', 'errors']
- // ...
+
+ initialize() {
+ // Called once when the controller is first instantiated (per element)
+
+ // Here you can initialize variables, create scoped callables for event
+ // listeners, instantiate external libraries, etc.
+ // this._fooBar = this.fooBar.bind(this)
+ }
+
+ connect() {
+ // Called every time the controller is connected to the DOM
+ // (on page load, when it's added to the DOM, moved in the DOM, etc.)
+
+ // Here you can add event listeners on the element or target elements,
+ // add or remove classes, attributes, dispatch custom events, etc.
+ // this.fooTarget.addEventListener('click', this._fooBar)
+ }
+
+ // Add custom controller actions here
+ // fooBar() { this.fooTarget.classList.toggle(this.bazClass) }
+
+ disconnect() {
+ // Called anytime its element is disconnected from the DOM
+ // (on page change, when it's removed from or moved in the DOM, etc.)
+
+ // Here you should remove all event listeners added in "connect()"
+ // this.fooTarget.removeEventListener('click', this._fooBar)
+ }
}
diff --git a/tests/fixtures/make-stimulus-controller/with_targets_values_classes.js b/tests/fixtures/make-stimulus-controller/with_targets_values_classes.js
new file mode 100644
index 000000000..5e2d9170c
--- /dev/null
+++ b/tests/fixtures/make-stimulus-controller/with_targets_values_classes.js
@@ -0,0 +1,44 @@
+import { Controller } from '@hotwired/stimulus';
+
+/*
+* The following line makes this controller "lazy": it won't be downloaded until needed
+* See https://symfony.com/bundles/StimulusBundle/current/index.html#lazy-stimulus-controllers
+*/
+
+/* stimulusFetch: 'lazy' */
+export default class extends Controller {
+ static targets = ['aaa', 'bbb']
+ static values = {
+ ccc: Number,
+ ddd: String,
+ }
+ static classes = ['eee', 'fff']
+
+ initialize() {
+ // Called once when the controller is first instantiated (per element)
+
+ // Here you can initialize variables, create scoped callables for event
+ // listeners, instantiate external libraries, etc.
+ // this._fooBar = this.fooBar.bind(this)
+ }
+
+ connect() {
+ // Called every time the controller is connected to the DOM
+ // (on page load, when it's added to the DOM, moved in the DOM, etc.)
+
+ // Here you can add event listeners on the element or target elements,
+ // add or remove classes, attributes, dispatch custom events, etc.
+ // this.fooTarget.addEventListener('click', this._fooBar)
+ }
+
+ // Add custom controller actions here
+ // fooBar() { this.fooTarget.classList.toggle(this.bazClass) }
+
+ disconnect() {
+ // Called anytime its element is disconnected from the DOM
+ // (on page change, when it's removed from or moved in the DOM, etc.)
+
+ // Here you should remove all event listeners added in "connect()"
+ // this.fooTarget.removeEventListener('click', this._fooBar)
+ }
+}
diff --git a/tests/fixtures/make-stimulus-controller/with_values.js b/tests/fixtures/make-stimulus-controller/with_values.js
new file mode 100644
index 000000000..a5c1c2561
--- /dev/null
+++ b/tests/fixtures/make-stimulus-controller/with_values.js
@@ -0,0 +1,42 @@
+import { Controller } from '@hotwired/stimulus';
+
+/*
+* The following line makes this controller "lazy": it won't be downloaded until needed
+* See https://symfony.com/bundles/StimulusBundle/current/index.html#lazy-stimulus-controllers
+*/
+
+/* stimulusFetch: 'lazy' */
+export default class extends Controller {
+ static values = {
+ min: Number,
+ email: String,
+ }
+
+ initialize() {
+ // Called once when the controller is first instantiated (per element)
+
+ // Here you can initialize variables, create scoped callables for event
+ // listeners, instantiate external libraries, etc.
+ // this._fooBar = this.fooBar.bind(this)
+ }
+
+ connect() {
+ // Called every time the controller is connected to the DOM
+ // (on page load, when it's added to the DOM, moved in the DOM, etc.)
+
+ // Here you can add event listeners on the element or target elements,
+ // add or remove classes, attributes, dispatch custom events, etc.
+ // this.fooTarget.addEventListener('click', this._fooBar)
+ }
+
+ // Add custom controller actions here
+ // fooBar() { this.fooTarget.classList.toggle(this.bazClass) }
+
+ disconnect() {
+ // Called anytime its element is disconnected from the DOM
+ // (on page change, when it's removed from or moved in the DOM, etc.)
+
+ // Here you should remove all event listeners added in "connect()"
+ // this.fooTarget.removeEventListener('click', this._fooBar)
+ }
+}
diff --git a/tests/fixtures/make-stimulus-controller/without_targets.js b/tests/fixtures/make-stimulus-controller/without_targets.js
index c07f69be3..2a74ae7d7 100644
--- a/tests/fixtures/make-stimulus-controller/without_targets.js
+++ b/tests/fixtures/make-stimulus-controller/without_targets.js
@@ -2,9 +2,37 @@ import { Controller } from '@hotwired/stimulus';
/*
* The following line makes this controller "lazy": it won't be downloaded until needed
-* See https://github.com/symfony/stimulus-bridge#lazy-controllers
+* See https://symfony.com/bundles/StimulusBundle/current/index.html#lazy-stimulus-controllers
*/
+
/* stimulusFetch: 'lazy' */
export default class extends Controller {
- // ...
+
+ initialize() {
+ // Called once when the controller is first instantiated (per element)
+
+ // Here you can initialize variables, create scoped callables for event
+ // listeners, instantiate external libraries, etc.
+ // this._fooBar = this.fooBar.bind(this)
+ }
+
+ connect() {
+ // Called every time the controller is connected to the DOM
+ // (on page load, when it's added to the DOM, moved in the DOM, etc.)
+
+ // Here you can add event listeners on the element or target elements,
+ // add or remove classes, attributes, dispatch custom events, etc.
+ // this.fooTarget.addEventListener('click', this._fooBar)
+ }
+
+ // Add custom controller actions here
+ // fooBar() { this.fooTarget.classList.toggle(this.bazClass) }
+
+ disconnect() {
+ // Called anytime its element is disconnected from the DOM
+ // (on page change, when it's removed from or moved in the DOM, etc.)
+
+ // Here you should remove all event listeners added in "connect()"
+ // this.fooTarget.removeEventListener('click', this._fooBar)
+ }
}