Skip to content

Use the full power of Stimulus #5

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

Closed
guillaumebriday opened this issue Dec 3, 2020 · 3 comments
Closed

Use the full power of Stimulus #5

guillaumebriday opened this issue Dec 3, 2020 · 3 comments

Comments

@guillaumebriday
Copy link

First thing first, I'm so happy to see this project! It's a huge deal for Stimulus and Symfony!! 👍

Under this title, I mean multiple things.

  1. Using disconnect callback

I think all controllers should use the disconnect callback to destroy instances. Like the Chart one.

If I dynamically remove the node from the DOM, the chart instance will remain loaded.

connect() {
  this.chart = new Chart(this.element.getContext('2d'), payload);
}

disconnect () {
  this.chart.destroy()
  this.chart = undefined
}
  1. Using data maps

Stimulus has a pretty handy API to deal with data: https://stimulusjs.org/reference/data-maps

It's cool because it's name-spaced by default, so it "forces" developer to use it in a more generic way. And it has methods to access datas.

I see lots of this.element.getAttribute('key') but they are not using data-maps

  1. Improve inheritence

All controllers dispatch a custom event "extends" controllers in a some way.

I think it would be more consistent to allow developers to extends these controllers like here with instance variables and so on instead of using custom event. Because sometime, it's useful to override the behavior.

Thanks a lot for this project!

@tgalopin
Copy link
Contributor

tgalopin commented Dec 4, 2020

Hi Guillaume! Thanks for your feedback and thanks a lot for your ideas!

Answering your message:

1/ Using disconnect callback

You are absolutely right: I added this in the READMEs of the packages but it's a very good idea to add it on the Chart component. If you wish to create a PR for https://github.com/symfony/ux/blob/main/src/Chartjs/Resources/assets/src/controller.js, that would be awesome

2/ Using data maps

My reasoning behind not using the data API but native JS getAttribute calls is the following:

  • my main problem is that it does impose a scoped name, which would be very verbose in the case of Symfony UX controllers (data-symfony-ux-chartjs-chart-view) as they come with already scoped package names
  • my second thought is that the Data API is likely to change a lot in Stimulus 2: it wouldn't make much sense to base Symfony UX on it right now if it will change a lot in the coming weeks

In opposition to these two drawbacks, I don't see a huge value into using the data APIs in package controllers as they don't store a complex state for the moment. So the balance of drawbacks vs advantages went to not using the data API for now, but I can change my opinion if I missed some arguments in favour of it!

3/ Inheritance

My current reasoning regarding inheritance is based on the experience of Symfony as a framework, in which providing features using vertical inheritance has major drawbacks:

  • If an application controller extends a Controller that requires an external service of something complex, one cannot create an automated test without actually calling the extended Controller, as the reference to the Controller is embedded in the app controller. Using events allow automated tests to easily simulate a behavior, in order to test the application controller more easily.
  • When you provide and incentivize people to develop their application code extending package controllers, it automatically exposes your full controller yo the child controllers, meaning all the methods of the controller can be called. This means that developing a Backward Compatible controller becomes much harder, as you can't remove or update methods from your base controller without breaking the application using it.
  • Finally, using events is more composable IMO: let's imagine that I develop a debug package for Symfony UX as a whole. I can listen to all events relative to the current element quite easily and chose whether to react to them of not. It's much harder to develop such a package with inheritance, as I would need to implement a controller per base controller.

Generally speaking, I feel the inheritance debate is a bit like the Inheritance vs Composition debate, and my mind favours Composition in this situation. I may be missing something of course, feel free to point it to me if so!

@tgalopin
Copy link
Contributor

tgalopin commented Dec 4, 2020

Oh and also! I think it would be awesome if your stimulus-components library would be compatible with Symfony UX, would you mind if I open a PR to see how it could work :) ?

@nicolas-grekas
Copy link
Member

I think this has been discussed and addressed. Thanks for chimming in!

dunglas referenced this issue in dunglas/ux Mar 2, 2021
* /Users/dunglas/workspace/hotwire-bundle:
  prepare merge in symfony/ux
  tweaking how the cloned form is used in the docs (symfony#12)
  updating stream media type to match changes in Turbo (symfony#11)
  ci: setup GitHub Actions (symfony#8)
  fix: regressions introduced in #5 (symfony#7)
  Relax minimum php version (#5)
  fix: various quality improvements (symfony#6)
  test: add UI tests (#4)
  ci: add PHPStan
  ci: add PHP-CS-Fixer
  refactor: broadcast template conventions and config
  refactor: decouple Broadcast from Twig and Mercure
  feat: initial implementation
weaverryan added a commit that referenced this issue Sep 27, 2022
…ued" changes (weaverryan, kbond)

This PR was merged into the 2.x branch.

Discussion
----------

[WIP] [Live] Make Ajax calls happen in serial, with "queued" changes

| Q             | A
| ------------- | ---
| Bug fix?      | yes
| New feature?  | yes
| Tickets       | None
| License       | MIT

Hi!

Consider the following situation:

A) Model update is made: Ajax call starts
B) An action is triggered BEFORE the Ajax call from (A) finishes.

Previously, we would start a 2nd Ajax call for (B) before the Ajax call from (A) finished... meaning two Ajax calls were happening at the same time. There are a few problems with this: (i) Ajax call (B) is missing any potential data changes from Ajax call (A) and (i) complexity of multiple things loading at once, and potentially finishing in a different order.

This PR simplifies things, which matches Livewire's behavior anyways. Now, the action from (B) will WAIT until the Ajax call from (A) finishes and THEN start. In fact, while an Ajax call is being made, all changes (potentially multiple model updates or actions) will be "queued" and then all set at once on the next request.

TODO:

* [X] Update the backend: for POST requests, the "data" was moved under a `data` key in JSON. Previously the entire body was the "data".
* [X] Update the backend: for POST/action requests, action "arguments" were moved from query parameters to an `args` key in the JSON.
* [x] Frontend: add tests for the `batch` action Ajax calls
* [x] Update the backend: a new fake `/batch` action needs to be added that can handle multiple actions at once
* [ ] A new `updatedModels` is sent on the ajax requests. If the signature fails, use this to give a better error message about what readonly properties were just modified. (in fact, this is the only purpose of sending this new field to the backend at this time).
* [X] ~~Pass a `includeUpdatedModels` value to the Stimulus controller ONLY when in `dev` mode.~~ For consistency, we will always pass the `updatedModels` in our Ajax requests, though this is intended to be "internal" and we won't use it other than to throw better errors.
* [X] ~~(Optional) If the backend has an unexpected exception (i.e. 5xx or maybe some 4xx where we mark that this is a problem we should show the developer), then render the entire HTML exception page (in dev mode only) so the user can see the error.~~ Done in #467

Cheers!

Commits
-------

4fbcebe cs fix and small tweaks
a0c4d3f use `Request::toArray()`
13d7110 mildly reworking to follow the logic of what is happening more clearly (#5)
b47ece1 [Live] add batch action controller
8b1e879 Refactoring towards single request & pending updates
weaverryan added a commit that referenced this issue Nov 1, 2022
# This is the 1st commit message:

WIP heavy refactoring to Component

Initial "hook" system used to reset model field after re-render

Adding a 2nd hook to handle window unloaded

reinit polling after re-render

Adding Component proxy

# This is the commit message #2:

fixing some tests

# This is the commit message #3:

Refactoring loading to a hook

# This is the commit message #4:

fixing tests

# This is the commit message #5:

rearranging

# This is the commit message #6:

Refactoring polling to a separate class
daFish pushed a commit to daFish/ux that referenced this issue Jan 26, 2023
# This is the 1st commit message:

WIP heavy refactoring to Component

Initial "hook" system used to reset model field after re-render

Adding a 2nd hook to handle window unloaded

reinit polling after re-render

Adding Component proxy

# This is the commit message symfony#2:

fixing some tests

# This is the commit message symfony#3:

Refactoring loading to a hook

# This is the commit message symfony#4:

fixing tests

# This is the commit message symfony#5:

rearranging

# This is the commit message symfony#6:

Refactoring polling to a separate class
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants