hotwired / stimulus

A modest JavaScript framework for the HTML you already have
https://stimulus.hotwired.dev/
MIT License
12.73k stars 426 forks source link

Changed callbacks can fire before connect #726

Closed ianterrell closed 1 year ago

ianterrell commented 1 year ago

I have a situation where I want polymorphic outlets, and I've solved it by having "bridging" controllers so that I can reference a homogeneous collection of outlets, where each member of that collection can reference another specific controller. I do it by setting a controller value on the bridging controller from the non-homogenous bridged controller.

In some circumstances I notice that the controllerValueChanged callback occurs before the bridging controller's connect is executed.

Bridging controller:

import { Controller } from "@hotwired/stimulus"

let xid = 0
export default class extends Controller {
  static values = {
    controller: String,
  }

  connect() {
    this.id = xid++
    console.log("ExtensionBridgeController#connect", this.id)
  }

  controllerValueChanged() {
    console.log("ExtensionBridgeController#controllerValueChanged", this.id, this.controllerValue)
  }
}

Bridged controller base class:

import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  connect() {
    this.element.setAttribute('data-ui--admin--wysiwyg--extension-bridge-controller-value', this.identifier)
  }
}

Observed order of operations:

Screenshot 2023-10-11 at 3 14 41 PM

For context, ultimately I do something like this in the controllerValueChanged:

this._bridgedController = this.application.getControllerForElementAndIdentifier(this.element, this.controllerValue)

But it's now growing more complex managing the synchronization of everything. I think my approach can use some work generally, and I'd love to see #628 or other approach fleshed out, but I think this may be a bug regardless.

brunoprietog commented 1 year ago

Hi, as mentioned in #490, invoking that callback after initializing the controller and before the controller is connected is the expected behavior. Maybe you could stop the method execution if the element is not connected with something like:

if (!this.element.isConnected) return
ianterrell commented 1 year ago

Thank you, I misread and misinterpreted the commentary on #490. I had been erroneously conceptualizing connect() as a constructor and conflated it with initialization.