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

How to know when every stimulus controllers are connected ? #698

Closed
lecrapal opened this issue Jul 3, 2023 · 2 comments
Closed

How to know when every stimulus controllers are connected ? #698

lecrapal opened this issue Jul 3, 2023 · 2 comments

Comments

@lecrapal
Copy link

lecrapal commented Jul 3, 2023

Hello,

I can't find any documentation or API reference on how to retrieve global states on the application.

I am using Stimulus with Symfony and my goal is to display a loading screen while all my Stimulus controllers finish their "connect()" action, knowing that many make AJAX calls.

Has anyone ever encountered this problem?
How to solve it?

Thank you

@lecrapal lecrapal changed the title How to know when all the stimulus controller are connected ? How to know when every stimulus controllers are connected ? Jul 3, 2023
@lb-
Copy link
Contributor

lb- commented Jul 3, 2023

That's a tricky problem to solve, connect only gets called once a controller connects to the DOM. If you have five registered controllers and only four have DOM elements on the initial page load... what do you expect to happen?

It's also usually not a good idea to block the entire UI with a loading screen. The point of using something like Stimulus is to avoid the downsides of a JS single page application where nothing can show on screen until the JS has been parsed.

Nonetheless, here is an approach for a per-controller loading styling.

  1. Give any controllers you want to have this loading behaviour a value e.g. data-my-table-ready-value="false".
  2. Then when your controller loads, change this value on connection. this.loadingValue = true;.
  3. Add some global CSS to hide/blur or do whatever you need based on any values that match the pattern.

You could do this generically with a consistent data attribute and/or class. You could even add a controller that removes itself when it receives a specific event.

The idea is you go for a CSS approach to change the visual state, driven by data attributes that get removed once the controller is ready.

Another approach, loading targets. For the controllers that need to show a loading state, use a target. Once your Promise has resolved, remove all loading elements. Be sure to remove them even if you get an error.

<div data-controller="my-table">
  <span data-my-table-target="loader">Loading...</span>
</div>
export class extends Controller {
  static targets = ['loader'];
  connect() {
    loadStuff.finaly(() => {
      this.loaderTargets.forEach(element => { element.remove(); } );
    });
  }
}

@marcoroth
Copy link
Member

marcoroth commented Jul 4, 2023

I think the approaches @lb- pointed out here are the way to go. Usually you don't really want to have a loading for a framework like Stimulus. Especially since Stimulus controllers all load "async" and can connect/disconnect at any time in the future.

Though, you can check if all controllers of your application are connected with a snippet like:

Stimulus.controllers.every(controller => {
  return Array.from(controller.context.module.connectedContexts).find(context => context === controller.context)
})

And if you are using useApplication from stimulus-use in your controllers you could make use of the the isConnected property and simplify the above snippet to:

Stimulus.controllers.every(controller => controller.isConnected)

But since this won't wait for your Promises in the connect() to resolve this might not be really useful for this particular problem. Also you won't get any callback/event which would say "all your controllers are connected now".

The only other approach I could think of is using a controller with outlets for all the "dependent" controller. You could also do it the other way around, something like: https://codepen.io/marcoroth/pen/XWygwpX. But I guess this depends on your controller and the indirections/complexity you want to introduce.

I hope this helps!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

3 participants