How to tell if page is loading in theme component / widget?

I feel like I’m probably missing something obvious here, but I can’t figure it out. I have a theme component that loads up some widgets, and I’m using the following code to re-render them after the user navigates to a new page (borrowed from category banners)

api.decorateWidget("my-widget:after", (helper) => {
    helper.widget.appEvents.on("page:changed", () => {
      helper.widget.scheduleRerender();
    })
});

However, this code waits until after the new page has loaded to update the widget. What I’m hoping to do is hide my widget content as soon as someone clicks a link to another page, similar to how the native discourse functionality is to hide things and show a loading spinner immediately on click.

I see in app\assets\javascripts\discourse\app\templates\discovery.hbs that the div container observes a “loading” var, but I can’t figure out where that comes from and how to “tap in” to that or observe the loading state in my widget.

Appreciative of any answers or simply pointing me in the right general direction. :slight_smile:

Thanks,

Zach

1 Like

Thought I’d bump this one up – happy to pay for some premium support to get an answer if need be.

Can you briefly describe what behaviour is desired and on which exact pages?

You may try queueRerender() instead of scheduleRerender()

Thanks for the responses guys.

I have a number of widgets I’ve built for certain category pages, and I’d ideally like to hide them immediately when someone navigates away from a page. Here’s a 45 second loom video demo of what I’m going for: Loom | Free Screen & Video Recording Software

@hawm I’m not sure that the queue re-render is what I need here, as this isn’t really about re-rendering my widgets, but rather about giving my widgets awareness of whether or not the global Discourse app is loading a new page.

The short it is, the condition should be in the template. i.e.

{{#if xyz}}
your code
{{/If}}

The ember templates dynamic. If the value changes, the widget will be hidden.

Yes, of course. :slight_smile: I am not struggling with the if/then syntax; what I’m asking is if there’s a way that I can check if Discourse is currently loading a new page.

I think to hack into the discovery route by using the modifyClass API then trigger some custom event would work.

https://api.emberjs.com/ember/3.12/classes/Route

The loading variable comes from the discovery route that I mentioned above, the widget attached to plugin-outlet may not able to access it since it doesn’t pass as an argument, it depends on the definition of plugin-outlet.

Okay, thanks a bunch. I’ll do some digging and see what I find, and update this with a solution for posterity if I find one :slight_smile:

1 Like

I managed to finally find a solution. Spent sooooooooooo long on this and IMO it’s worth merging into core because honestly it feels like it should already exist anyway in core.

I added the code below to my theme component; what it does is adds a “loading” class to the body as soon as routing starts, and then removes it as soon as routing is complete. Maybe this would be simple/obvious for folks who know ember, but it took me an obscenely long time (and trying a million things + reading through all threads on here I could find) to figure out.

With this core code in place, I can add loading spinners and such to my different widgets that will have their css and visibility be driven by whether or not the body tag has the loading css class.

initialize() {
    withPluginApi("0.8.8", (api) => {
      const router = api._lookupContainer('router:main');
      router.reopen({
        addLoadingCSSClassToBody: function() {
          document.body.classList.add("loading");
        }.on('willTransition')
      });

      router.reopen({
        removeLoadingCSSClassFromBody: function() {
          document.body.classList.remove("loading");
        }.on('didTransition')
      });
    });
  },
};
1 Like

The code I am sharing isn’t from a widget, but it is from a component that we did release for download here on meta.

In it we use the router, along with @discourseComputed to check if the route has changed, and render based on that.

You can look deeper into the code if you are interested in how it works.

4 Likes