Get information of a Stimulus Controller
faqndo97 opened this issue · comments
Description
Hi! I'm working on a project for stimulus, and given a controller instance (obtained using getControllerForElementAndIdentifier
), I'm needing to get all present targets, all values, and the classes configured. Is it possible to get that currently from the controller instance?
Thanks
Without digging into the internals of the controller instance's scope, your best (documented) way would be as follows.
Given the example code & controller
import { Controller } from '@hotwired/stimulus';
export default class extends Controller {
static classes = ['closed', 'open'];
static values = { closed: Boolean };
static targets = ['content', 'toggle'];
}
<section class="section">
<div class="container" id="my-container" data-controller="reveal" data-reveal-open-class="open">
<button data-action="click->reveal#close" type="button">Close</button>
<div data-reveal-target="content">...CONTENT</div>
</div>
</section>
1. Get the controller instance's constructor
We want to access the 'static' properties on the Controller class, this can be done by accessing the constructor
.
const controller = window.Stimulus.getControllerForElementAndIdentifier(document.getElementById('my-container'), 'reveal');
2. Get all classes
Using this.[logicalName]Classes
so we do not get an error if no class is declared. Returns an array of strings.
Object.fromEntries(controller.constructor.classes.map(key => [key, controller[`${key}Classes`]]));
// { "closed": [], "open": [ "open" ] }
3. Get all targets
Very similar to above, but using the this.[name]Targets
. Always returning an array of targets. Not throwing an error if no target is present.
Object.fromEntries(controller.constructor.targets.map(key => [key, controller[`${key}Targets`]]));
// { "content": [ <div...> ], "toggle": [] }
4. Getting all values
Here, we do not need to worry about the error case as Values all have defaults. Using this.[name]Value
. Remember that values are declared using objects.
Object.fromEntries(Object.keys(controller.constructor.values).map(key => [key, controller[`${key}Value`]]));
// { closed: false }
5. Putting it all together into a function
We would want to account for cases where we may not know the global in advance and also where the controller may not have values/targets/classes declared.
Here's a good starting point.
const getControllerData = (
element,
identifier,
getControllerForElementAndIdentifier = (..._) =>
window.Stimulus.getControllerForElementAndIdentifier(..._)
) => {
const controller = getControllerForElementAndIdentifier(element, identifier);
const { classes = [], targets = [], values = {} } = controller.constructor;
return {
classes: Object.fromEntries(
classes.map((key) => [key, controller[`${key}Classes`]])
),
targets: Object.fromEntries(
targets.map((key) => [key, controller[`${key}Targets`]])
),
values: Object.fromEntries(
Object.keys(values).map((key) => [key, controller[`${key}Value`]])
),
};
};
// calling it
getControllerData(document.getElementById('my-container'), 'reveal');