hotwired / stimulus

A modest JavaScript framework for the HTML you already have

Home Page:https://stimulus.hotwired.dev/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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');