Skip to content
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

Fire callbacks when targets are added or removed #367

Closed
wants to merge 9 commits into from

Conversation

seanpdoyle
Copy link
Contributor

@seanpdoyle seanpdoyle commented Jan 20, 2021

Fire callbacks when targets are added or removed

Closes hotwired/stimulus#336


Implements the TargetObserver to monitor when elements declaring
[data-${identifier}-target] are added or removed from a Context
after being connected and before being disconnected.

In support of iterating through target tokens, export the
TokenListObserver module's parseTokenString function.

@seanpdoyle seanpdoyle force-pushed the target-callback branch 3 times, most recently from 2f8a20d to c89e2cc Compare January 20, 2021 17:32
docs/reference/targets.md Outdated Show resolved Hide resolved
@leastbad
Copy link
Contributor

I'm extremely happy to see this, @seanpdoyle! You rock.

@seanpdoyle
Copy link
Contributor Author

I've pushed up some changes to the test suite so that it finishes its run.

It's currently failing, but that's an improvement over the previous version which ran indefinitely due to some infinite recursion.

@seanpdoyle seanpdoyle force-pushed the target-callback branch 2 times, most recently from 339dee3 to 4d7f686 Compare March 27, 2021 01:28
@seanpdoyle
Copy link
Contributor Author

I'm not sure why, but CI is failing on Windows and Safari 9.1.2 (Mac OS 10.11.6). It passes for me locally in all browsers (including Safari Version 14.0.3 (16610.4.3.1.7)) on macOS.

Is it possible that part of the suite, or any other part of this implementation requires polyfills?

Copy link
Contributor

@sstephenson sstephenson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implementation-wise, this is looking better since the move to TokenListObserver. I'm not sure I understand the motivation for firing callbacks only on targets that appear after the controller connects, though. That really seems to limit the possible use cases.

My intuition is that target lifecycle callbacks would work similarly to value changed callbacks. So just like we call value changed callbacks for all values present when the controller connects, I'd expect we'd also call the connected callbacks for all targets present when the controller connects. Same goes for disconnected callbacks.

The specific timeline I'd expect is:

  • Controller#initialize()
  • Controller#{name}TargetConnected()
  • Controller#connect()
  • ...
  • Controller#{name}TargetDisconnected()
  • Controller#disconnect()

I've spiked this out in a separate branch, if you'd like to use it as a reference or a rebase target: https://github.com/hotwired/stimulus/compare/target-lifecycle-callbacks

docs/reference/targets.md Outdated Show resolved Hide resolved
packages/@stimulus/core/src/target_properties.ts Outdated Show resolved Hide resolved
packages/@stimulus/core/src/target_observer.ts Outdated Show resolved Hide resolved
docs/reference/targets.md Outdated Show resolved Hide resolved
packages/@stimulus/core/src/target_observer.ts Outdated Show resolved Hide resolved
Closes [hotwired#336][]

---

Implements the `TargetObserver` to monitor when elements declaring
`[data-${identifier}-target]` are added or removed from a `Context`
_after_ being connected and _before_ being disconnected.

In support of iterating through target tokens, export the
`TokenListObserver` module's `parseTokenString` function.

[hotwired#336]: hotwired#336
replace Array function with a for loop
Introduce the concept of a `TargetObserverDelegate`, then make `Context`
implement the interface.
}

inputTargetDisconnected(element) {
element.classList.add("removed-animation")
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sstephenson I now realize that the element will already be out of the DOM, so this contrived example doesn't really illustrate the point.

One use case I had in mind was sorting, would that be a better example? Can you think of a use case where having access to the Element instance that was removed would be helpful?

@seanpdoyle
Copy link
Contributor Author

@sstephenson I've incorporated your feedback and pushed up the changes. The suite passes for me locally on evergreen browsers, but is still failing in CI.

Are there polyfills we need to enable in our SauceLabs configuration?

@adrienpoly
Copy link
Member

@seanpdoyle for the CI I think the issue comes from isConnected that is not supported by all browsers. https://caniuse.com/?search=isConnected

This was my first result when searching for a Polyfill
https://gist.github.com/eligrey/f109a6d0bf4efe3461201c3d7b745e8f

you might want to try to add it here to see if it makes the CI happy
https://github.com/hotwired/stimulus/blob/main/packages/%40stimulus/polyfills/index.js

@seanpdoyle
Copy link
Contributor Author

Thanks for the tip @adrienpoly. The only other spot we're relying on isConnected is

if (element.isConnected != this.element.isConnected) {
.

Since the suite isn't failing prior to this change, I wonder if that line is properly covered by our suite. Maybe those tests include a polyfill of sorts?

The implementation was sourced from the [Mozilla Developer Network
documentation][poyfill] for [Node.isConnected][]

[poyfill]: https://developer.mozilla.org/en-US/docs/Web/API/Node/isConnected#polyfill
[Node.isConnected]: https://developer.mozilla.org/en-US/docs/Web/API/Node/isConnected
@adrienpoly
Copy link
Member

It seems to remove most of the prior issues. Not sure to understand the remaining CI error and yes I think this was a missing polyfill in previous versions.

```
IE 11.0 (Windows 8.1) ERROR
133
  Expected identifier, string or number
134
  at @stimulus/core/dist/tests/index.js:2281:1
135

136
  SyntaxError: Expected identifier, string or number
137
     at ./packages/@stimulus/polyfills/index.js (@stimulus/core/dist/tests/index.js:2281:1)
```

```
IE 11.0 (Windows 8.1)  target disconnected callback when element is removed FAILED
125
	Promise rejected during "target disconnected callback when element is removed": Object doesn't support property or method 'remove'
126
	TypeError: Object doesn't support property or method 'remove'
127
	   at Anonymous function (eval code:191:25)
```
@seanpdoyle
Copy link
Contributor Author

@adrienpoly thanks for the tips! The suite is now passing.

@javan
Copy link
Contributor

javan commented Apr 12, 2021

The only other spot we're relying on isConnected is … Since the suite isn't failing prior to this change, I wonder if that line is properly covered by our suite.

See #161 (comment) for context.

@seanpdoyle
Copy link
Contributor Author

Thanks for the context @javan. Since this polyfill is only used to pass our suite on CI in a legacy Windows environment, what do you think is the best path forward?

@javan
Copy link
Contributor

javan commented Apr 12, 2021

Totally fine to add a polyfill for isConnected.

seanpdoyle added a commit to seanpdoyle/stimulus that referenced this pull request Apr 27, 2021
Add attribute-level support for monitoring changes to attributes on the
marked element _and_ its descendants.

The `data-mutation` syntax draws direct inspiration from the
`[data-action]` syntax for routing browser events.

Similar to how the Action syntax supports [event listener options][],
the Mutation syntax would support [MutationObserverInit options][] like
`!subtree`.

The proposed hooks only cover _attribute_ mutations, since the proposal
made by [hotwired#367][] should cover `childList` type
mutations like the addition or removal of controller targets.

One alternative could involve combining `[data-mutation]` and
`[data-action]` into a single DOMTokenList, and using additional symbols
like `@...` or wrapping `[...]` as a differentiators (e.g.
`@aria-expanded->disclosure#toggle` or
`[aria-expanded]->disclosure#toggle`).

Another could push this responsibility application-side by introducing
more publicly available `MutationObserver` utilities like those used for
`DOMTokenList` parsing or deconstructing the `[data-action]` directives.

Once available, those utilities could be used by consumers to listen for
their own mutations and "route" them to actions by combining action
directive parsing and
`Application.getControllerForElementAndIdentifier(element, identifier)`
to invoke fuctions on a `Controller` instance.

[hotwired#367]: hotwired#367
[event listen options]: https://stimulus.hotwire.dev/reference/actions#options
[MutationObserverInit options]: https://developer.mozilla.org/en-US/docs/Web/API/MutationObserverInit#properties
@seanpdoyle seanpdoyle closed this Apr 30, 2021
seanpdoyle added a commit to seanpdoyle/stimulus that referenced this pull request Sep 12, 2021
Add attribute-level support for monitoring changes to attributes on the
marked element _and_ its descendants.

The `data-mutation` syntax draws direct inspiration from the
`[data-action]` syntax for routing browser events.

Similar to how the Action syntax supports [event listener options][],
the Mutation syntax would support [MutationObserverInit options][] like
`!subtree`.

The proposed hooks only cover _attribute_ mutations, since the proposal
made by [hotwired#367][] should cover `childList` type
mutations like the addition or removal of controller targets.

One alternative could involve combining `[data-mutation]` and
`[data-action]` into a single DOMTokenList, and using additional symbols
like `@...` or wrapping `[...]` as a differentiators (e.g.
`@aria-expanded->disclosure#toggle` or
`[aria-expanded]->disclosure#toggle`).

Another could push this responsibility application-side by introducing
more publicly available `MutationObserver` utilities like those used for
`DOMTokenList` parsing or deconstructing the `[data-action]` directives.

Once available, those utilities could be used by consumers to listen for
their own mutations and "route" them to actions by combining action
directive parsing and
`Application.getControllerForElementAndIdentifier(element, identifier)`
to invoke fuctions on a `Controller` instance.

[hotwired#367]: hotwired#367
[event listen options]: https://stimulus.hotwire.dev/reference/actions#options
[MutationObserverInit options]: https://developer.mozilla.org/en-US/docs/Web/API/MutationObserverInit#properties
seanpdoyle added a commit to seanpdoyle/stimulus that referenced this pull request Sep 12, 2021
Add attribute-level support for monitoring changes to attributes on the
marked element _and_ its descendants.

The `data-mutation` syntax draws direct inspiration from the
`[data-action]` syntax for routing browser events.

Similar to how the Action syntax supports [event listener options][],
the Mutation syntax would support [MutationObserverInit options][] like
`!subtree`.

The proposed hooks only cover _attribute_ mutations, since the proposal
made by [hotwired#367][] should cover `childList` type
mutations like the addition or removal of controller targets.

One alternative could involve combining `[data-mutation]` and
`[data-action]` into a single DOMTokenList, and using additional symbols
like `@...` or wrapping `[...]` as a differentiators (e.g.
`@aria-expanded->disclosure#toggle` or
`[aria-expanded]->disclosure#toggle`).

Another could push this responsibility application-side by introducing
more publicly available `MutationObserver` utilities like those used for
`DOMTokenList` parsing or deconstructing the `[data-action]` directives.

Once available, those utilities could be used by consumers to listen for
their own mutations and "route" them to actions by combining action
directive parsing and
`Application.getControllerForElementAndIdentifier(element, identifier)`
to invoke fuctions on a `Controller` instance.

[hotwired#367]: hotwired#367
[event listen options]: https://stimulus.hotwire.dev/reference/actions#options
[MutationObserverInit options]: https://developer.mozilla.org/en-US/docs/Web/API/MutationObserverInit#properties
@seanpdoyle seanpdoyle deleted the target-callback branch September 15, 2021 13:27
seanpdoyle added a commit to seanpdoyle/stimulus that referenced this pull request Oct 11, 2021
Closes hotwired#471

---

This commit re-arranges the documentation for Controller Lifecycle
Callbacks to reflect the order of execution and elaborates with more
details when possible.

The possibilities for the callback execution order was discussed and the
[current order was agreed upon][order].

[order]: hotwired#367
dhh pushed a commit that referenced this pull request Oct 11, 2021
Closes #471

---

This commit re-arranges the documentation for Controller Lifecycle
Callbacks to reflect the order of execution and elaborates with more
details when possible.

The possibilities for the callback execution order was discussed and the
[current order was agreed upon][order].

[order]: #367
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

v2 Proposed Enhancement: fooTargetsChanged callback
5 participants