Quick navigation results in multiple components(forms) being added to the top-level page

I have my app setup with a top-level page where I load components in using an on-click event handler for my navigation buttons. This is somewhat of an edge case but I noticed that if navigation is performed quickly the slot where the component is added does not always get cleared. This is because the first navigation button that was clicked hasn’t fully added the form yet. For example, if I click my home navigation link and then immediately click my articles navigation link both forms will be added to the top-level page.

At first I thought it was due to how I handled navigation but I refactored to match most of the best-practices I’ve seen in the forum and the docs.

I noticed this same behavior on the default app templates as well. Has anyone found a way to work around this?

It seems like no matter how I clear the slot on the top level form that I’m placing the component forms into, the nav change doesn’t process quick enough if a user selects them in quick succession.

I recently looked into this HashRouting: Routing, navigation with URL Hash and noticed that I wasn’t able to replicate the issue with the HashRouting example but that could be because of the load speed.

Has anyone experienced this and found a workaround?

Looks like you may be running into the same situation as this post: Wierd "duplicated" site on custom domain

Personally, I always use hash routing from Anvil Extras. But if you wanted to stick with the navigation you have, the first line of code in your click handler could disable the navigation link/button, and the last line could re-enable it. That might cut down on double clicks causing the issue.

1 Like

Thanks for sending that post over! I’ll take a look at it. I’ll probably end up switching to the hash routing since I like the idea of that functionality anyways.
I also considered disabling the nav bars but that seemed like a poor UX and wanted to avoid it if I could.

Thanks for the reply though. I plan on refactoring my navigation this week and I’ll see if that takes care of it.

When developing hash routing I worked quite hard to get rid of the behaviour you describe.

I think I spent hours creating slow loading forms and hitting navigation buttons and then quickly clicking another navigation button or immediately hitting the browser back forward buttons and making sure there was always a single correct form in the content panel.

What’s happening is that while clicking a navigation button the previous form has not yet been added to the content panel while you invoke the next navigation. If you add print statements between the lines of code it’ll go something like.

A - clear panel
B - instantiate form SlowForm
A - clear panel
B - instantiate form FastForm
C - FastForm instantiated
D - add FastForm to panel 
C - SlowForm instantiated 
D - add SlowForm to panel 

Buttons are free to be clicked and invoke their click handler even if the previous click handler has not yet finished.

The way hash routing handles this is three fold.

  1. Forms loaded into the content panel are cached making slow loading forms less frequent.
  2. Navigating to the exact same url hash does nothing.
  3. Keeps a track of the current form using a global variable. Before adding the form to the content panel ensure that the current form matches what we think it is. If it’s something else don’t add this form to the content panel. Also re-clear the content panel after the form is instantiated just in case.

So a click handler that does this might look like.

def nav_link_click(self, sender, **event_args):
    form_name = sender.tag
    if self.current == form_name:
        return

    self.content_panel.clear()
    self.current = form_name
    form_cls = self.name_to_cls[form_name]
    form = form_cls() # this might be slow
    if self.current != form_name:
        return
    self.content_panel.clear() # just in case
    self.content_panel.add_component(form)

:sweat_smile:

Another thing you might consider is exiting immediately if the click handler is still being processed.

def nav_link_click(self, **event_args):
    if self.navigating:
        return
    self.navigating = True
    self.content_panel.clear()
    try:
        self.content_panel.add_component(Form())
    finally:
        self.navigating = False
2 Likes

Well you definitely confirmed my suspicions here. I had attempted something similar to the first option you gave but I see where my mistake was.

I’m going to refactor the navigation and go with the hash routing. Appreciate the insight!

1 Like

In one of my apps, I try to prevent this behavior, by disabling the interference-causing widgets, for the duration of the update.

1 Like