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 build breadcrumb? #299

Open
hongquan opened this issue Nov 8, 2024 · 7 comments
Open

How to build breadcrumb? #299

hongquan opened this issue Nov 8, 2024 · 7 comments

Comments

@hongquan
Copy link
Contributor

hongquan commented Nov 8, 2024

I am trying to build a breadcrumb for deep nested pages, to let user easily go to ancestor pages.

In vue-router, I use route.matched to get the URLs of the ancestor pages. But in @kitbag/router, there is no way. In @kitbag/router, the route.matches just return the data used for defining routes (in routes.ts), where name is of string | undefined type, and path is just a portion, not a full path.

@stackoverfloweth
Copy link
Contributor

@hongquan do your breadcrumbs use router-link? You should be able to use the name property but I can see how that's not a perfect solution. You'll need to use a type guard or casting to convince TS your name is valid. It also wouldn't work for any routes that have params.

<template v-for="match in route.matches" :key="match.id">
  <router-link :to="resolve => resolve(match.name as typeof route['name'])">{{  match.name }}</router-link>
</template>

Note if a match has an undefined name, it's not a navigable route.

We will see if we can find a better solution for you

@hongquan
Copy link
Contributor Author

hongquan commented Nov 8, 2024

@stackoverfloweth I don't believe I can use <RouterLink> because it requires:

  • Known "name", which is returned as string.
  • Concrete values of "params" (to pass to resolve), which cannot be retrieved from any where.

So I only hope to get the full path and the route name to use with <a>, like:

<a :href='fullPath'>{{ getTitle(name) }}</a>

More context:
The breadcrumb component will be a common one, to insert to many pages, it cannot be fixed with a route.

@stackoverfloweth
Copy link
Contributor

@hongquan you can indeed use router-link, we just need a type guard to satisfy Typescript for the known name piece and we can pass params as well. Here's a complete working example.

<template>
  <template v-for="match in matches" :key="match.id">
    <router-link :to="match">{{  match }}</router-link>
  </template>
  
  <router-view />
</template>
import { Url, isUrl, useRoute, useRouter } from '@kitbag/router';
import { computed } from 'vue';

const router = useRouter()
const route = useRoute()

function isRouteName(name: string | undefined): name is typeof route['name'] {
  return !!name && name !== 'NotFound'
}

const matches = computed(() => {
  return route.matches.reduce<Url[]>((matches, match) => {
    if(isRouteName(match.name)){
      const url = router.resolve(match.name, route.params)

      if(isUrl(url)){
        matches.push(url)
      }
    }

    return matches
  }, [])
})

Note, the check for name !== "NotFound" is checking that the current route is not a rejection route. Again, we have a type guard for that which we can include in the public API. For now checking the name will suffice since rejection routes use the reject type for a name.

@hongquan
Copy link
Contributor Author

hongquan commented Nov 14, 2024

@stackoverfloweth Thank you.

Will try again some day. I canceled the "vue-router to kitbag router migration" attempt due to this issue and another "recursive type resolve" when building navigation bar (which I didn't have time to report).

@pleek91
Copy link
Contributor

pleek91 commented Nov 14, 2024

@hongquan Thanks for contributing to this project by using it and submitting issues! Sorry to hear your migration didn't succeed. We're going to keep working on making this specific use case (breadcrumbs) a good devx.

I'm also very curious about the recursive types issue you had in your nav bar. If you have time to share that one I'd appreciate it.

@stackoverfloweth
Copy link
Contributor

I'm going to reopen this as an action item for us to finish improvements to types to make this easier and then either

A. Provide a concise snippet, possibly in docs for how to build such a component
B. Actually create a BreadCrumbs vue component exported by @kitbag/router

@hongquan
Copy link
Contributor Author

hongquan commented Nov 15, 2024

@pleek91 Unfortunately, I cannot reproduce the "recursive type resolve" issue with the "router-preview" example.

The summary is that:

I want to pass a name and params to <RouterLink>'s :to, but it only accepts Url | ToCallback, so I create an intermediate component, say RoomNavLink, where I define a ToCallback which gets params from props, kind of:

import type { RegisteredRoutesName } from '@kitbag/router'
import type { RouterResolve } from '@/routes'

interface Props {
   routerName: RegisteredRoutesName
   roomId: number
}
function getUrl(resolve: RouterResolve) {
    return resolve(props.name, { roomId: props.roomId })
}

The RouterResolve type is defined as:

export type RouterResolve = typeof router.resolve

It seems that, the import of RegisteredRoutesName and RouterResolve causes "recursive type resolve" and make one of my component become "{}" type, instead of Vue Component type. But I cannot reproduce with "router-preview" example.

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