New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposal: Add Stimulus event to listen to addition/removal of targets #200

Closed
ernsheong opened this Issue Oct 30, 2018 · 5 comments

Comments

Projects
None yet
4 participants
@ernsheong
Copy link

ernsheong commented Oct 30, 2018

I am looking for a way to listen to events for addition or removal of controller targets.

Use case: When new targets appear in the DOM, I want to trigger an action/DOM manipulation.

Why: My controller sits at the top level (document body). I suppose the recommended use case is to scope it to a specific DOM subtree, but in this case it is meant to manage DOM everywhere so it resides in the body

@skyksandr

This comment has been minimized.

Copy link

skyksandr commented Oct 30, 2018

My approach would be to create a controller for this specific targets and let them emits an event in connect.
Also, you can pass any required data in CustomEvent.

@ernsheong

This comment has been minimized.

Copy link
Author

ernsheong commented Oct 30, 2018

Yes, but event emitted in connect() is only the first time, what if more targets are added later on, just targets mind you, no new controllers.

@skyksandr

This comment has been minimized.

Copy link

skyksandr commented Oct 30, 2018

What I was saying in other words - let targets emit events.

@sstephenson

This comment has been minimized.

Copy link
Contributor

sstephenson commented Oct 31, 2018

Thanks for the suggestion! This is something that's on our radar.

For now, I would echo @skyksandr's recommendation to add a new data-controller annotation to each target element you want to observe. Those elements can still serve as targets for your outer controller. For example:

<body data-controller="outer">
  ...
  <!-- Some newly inserted targets: -->
  <div data-target="outer.thing" data-controller="inner">1...</div>
  <div data-target="outer.thing" data-controller="inner">2...</div>
</body>

Each target has its own inner controller, which emits an inner-connected event when connected:

// inner_controller.js
export default class extends Controller {
  connect() {
    const event = document.createEvent("CustomEvent")
    event.initCustomEvent("inner-connected", true, true, null)
    this.element.dispatchEvent(event)
  }
}

Add a data-action to the body element to call the thingConnected() method in response to those events:

<body data-controller="outer" data-action="inner-connected->outer#thingConnected">
   ...
// outer_controller.js
export default class extends Controller {
  static targets = [ "thing" ]

  thingConnected(event) {
    console.log(event.target) // the element that was just connected
  }
}

Closing this issue for now, but please feel free to continue discussing it over on the community forum.

@fiznool

This comment has been minimized.

Copy link

fiznool commented Jan 24, 2019

@sstephenson I really like the above approach, and feel that its a very valuable Stimulus pattern. Perhaps it could become part of the official documentation? There are many places where custom events make a lot of sense.

Or perhaps it could be considered for inclusion into the core library, on the base Controller class?

  protected emit(evtType: string, evtData?: object) {
    let evt;
    if (typeof CustomEvent === 'function') {
      // Modern browsers
      evt = new CustomEvent(evtType, {
        bubbles: true,
        detail: evtData,
      });
    } else {
      // IE
      evt = document.createEvent('CustomEvent');
      evt.initCustomEvent(evtType, true, false, evtData);
    }

    this.element.dispatchEvent(evt);
  }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment