Windvis / ember-breadcrumb-trail

Minimalistic but very flexible breadcrumb management solution for Ember applications.
MIT License
14 stars 3 forks source link

Rendering timing issue #18

Open Windvis opened 3 years ago

Windvis commented 3 years ago

Sometimes going from a child route to a parent route has some weird timing problems.

Situation: Overview / Details route with dynamic segment / sub page. The breadcrumbs bar is added to a top level route, so always visible.

Navigating between sub page and the details page is ok, but navigation to the parent page causes an issue. Ember tries to re-render the "Details" link with an undefined dynamic segment while it shouldn't need to render that link at all.

So it seems that the breadcrumb helper receives an update request before it's getting destroyed.

Windvis commented 2 years ago

The issue seems related to using LinkTo components when rendering the breadcrumbs. The internal computeds are rerun before the breadcrumb helper is destroyed when leaving the page, but after the router service has updated internally. This results in the implicit route params no longer being available to the routing system so the missing route param errors is shown.

More testing is needed to see if it also happens when using LinkTo alternatives like ember-link.

This is definitely a downside to the template based approach. Maybe keeping everything in the route would be better, even though the API won't be as nice.

jkeen commented 2 years ago

@Windvis Seems like the same thing happens with ember-link, fwiw.

I'm a huge fan of this template based approach—feels very declarative and right in line with how ember-page-title works so I'm definitely in favor of keeping this pattern.

For future travelers: I worked around this rendering issue by checking to see if the url is renderable before trying to render it in the template. It's not ideal, but it does the job.

@service router;

@action
shouldRenderUrl(data) {
  let valid = true;
  try {
    this.router.urlFor(...[data.route, data.model].filter((d) => !!d));
  } catch (e) {
    valid = false;
  }

  return valid;
}
<ol class='flex h-auto'>
  {{#each (breadcrumbs) as |breadcrumb index|}}
    {{#if (has-next breadcrumb (breadcrumbs))}}
      {{#if (compute (fn this.shouldRenderUrl breadcrumb.data))}}
        {{!-- LinkTo won't render this route when @model is passed in without a value }} --}}
        {{#let
          (if
            breadcrumb.data.model
            (component 'link-to' route=breadcrumb.data.route model=breadcrumb.data.model)
            (component 'link-to' route=breadcrumb.data.route)
          )
          as |LinkToComponent|
        }}
          <li class='flex flex-row items-center whitespace-nowrap'>
            <LinkToComponent
              aria-current={{if (not (has-next breadcrumb (breadcrumbs))) 'page'}}
              class='flex flex-row items-center text-base text-gray-500 hover:text-gray-700'
              data-test-breadcrumb={{index}}
            >
              {{#if breadcrumb.data.icon}}
                {{svg-jar
                  breadcrumb.data.icon
                  class=(or breadcrumb.data.iconStyle 'mr-2 w-4 h-4')
                }}
              {{/if}}
              <span>
                {{breadcrumb.title}}
              </span>
            </LinkToComponent>
            {{#if (and breadcrumb.title (has-next breadcrumb (breadcrumbs)))}}
              {{svg-jar 'right' class='fill-current ml-1' width='16'}}
            {{/if}}
          </li>
        {{/let}}
      {{/if}}
    {{/if}}
  {{/each}}
</ol>