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

Adding meta for routes without a component #33

Open
posva opened this issue Jul 12, 2022 · 15 comments
Open

Adding meta for routes without a component #33

posva opened this issue Jul 12, 2022 · 15 comments
Labels
⚡️ enhancement improvement over an existing feature

Comments

@posva
Copy link
Owner

posva commented Jul 12, 2022

Creating folders create routes and any file inside them becomes a nested route. This wasn't possible before v4.1 but now it makes sense to allow defining meta properties (mainly but also any other route property) at this level

This could maybe be achieved with a special file users/__route.js that exports some properties like name, meta, etc (the same as in the <route> block)

@Soviut
Copy link

Soviut commented Jul 21, 2023

This would be nice for applying meta: { requiresAuth: true } to a parent route. Currently, I'm having to use definePage on every child page.

@jods4
Copy link

jods4 commented Jan 8, 2024

Here we're building our navigation menus from the routes table (using metadata added in route blocks).
We'd like to add metadata to the non-navigable parent nodes, too.

We used the following hack to work-around this:

  1. Create an empty index.vue page anyway, with a <route> block and metadata.
  2. In the metadata, include a marker that the page must be removed, e.g. "remove": true.
  3. We use the extendRoute(route) callback during build to customize the process a bit:
if (route.node.value.overrides?.remove)
{
  // Move metadata to parent, depends on your use cases
  route.parent.addToMeta(route.node.value.overrides.meta);
  // Remove from routing table
  route.delete();
}

If anyone's interested, that approach works.
It would be nice to have this built-in though, maybe as the default export of __route.js as suggested initially; or __route.json and __route.yaml for syntax consistency with <route> blocks.

Random remark: route.node is described as a private variable in code, yet there is no access to overrides object from EditableTreeNode. I feel like one public API is missing?

@posva
Copy link
Owner Author

posva commented Jan 8, 2024

Thanks for sharing your approach!
I'm realizing this is quite old now and I haven't implemented any experimental APIs around it yet... I hope to get back to it after #246 and the Data Loaders RFC

Random remark: route.node is described as a private variable in code, yet there is no access to overrides object from EditableTreeNode. I feel like one public API is missing?

Yes, EditableTreeNode is definitely still missing some APIs but also docs and examples 😅

@settings settings bot removed the enhancement label Feb 21, 2024
@posva posva added the ⚡️ enhancement improvement over an existing feature label Feb 21, 2024
@an600027
Copy link

Here we're building our navigation menus from the routes table (using metadata added in route blocks). We'd like to add metadata to the non-navigable parent nodes, too.

We used the following hack to work-around this:

  1. Create an empty index.vue page anyway, with a <route> block and metadata.
  2. In the metadata, include a marker that the page must be removed, e.g. "remove": true.
  3. We use the extendRoute(route) callback during build to customize the process a bit:
if (route.node.value.overrides?.remove)
{
  // Move metadata to parent, depends on your use cases
  route.parent.addToMeta(route.node.value.overrides.meta);
  // Remove from routing table
  route.delete();
}

If anyone's interested, that approach works. It would be nice to have this built-in though, maybe as the default export of __route.js as suggested initially; or __route.json and __route.yaml for syntax consistency with <route> blocks.

Random remark: route.node is described as a private variable in code, yet there is no access to overrides object from EditableTreeNode. I feel like one public API is missing?

Called like this, vscode does not report an error
route["node"].value.overrides.remove

@a81n9
Copy link

a81n9 commented Aug 14, 2024

Creating folders create routes and any file inside them becomes a nested route. This wasn't possible before v4.1 but now it makes sense to allow defining meta properties (mainly but also any other route property) at this level

This could maybe be achieved with a special file users/__route.js that exports some properties like name, meta, etc (the same as in the <route> block)

@posva
I don't know how to configure the __route.js file
This kind of scene is too common
Can you provide a demo?

@jods4
Copy link

jods4 commented Aug 14, 2024

@AAABingBing This issue is open, there is no support for __route.js (or any other alternative) built into this library yet.
Look at my comment above for an alternative using extendRoute() to implement this feature in your code.

@a81n9
Copy link

a81n9 commented Aug 14, 2024

@AAABingBing This issue is open, there is no support for __route.js (or any other alternative) built into this library yet. Look at my comment above for an alternative using extendRoute() to implement this feature in your code.

Oh, I see
Thank you for your reply

@a81n9
Copy link

a81n9 commented Aug 15, 2024

Here we're building our navigation menus from the routes table (using metadata added in route blocks). We'd like to add metadata to the non-navigable parent nodes, too.

We used the following hack to work-around this:

  1. Create an empty index.vue page anyway, with a <route> block and metadata.
  2. In the metadata, include a marker that the page must be removed, e.g. "remove": true.
  3. We use the extendRoute(route) callback during build to customize the process a bit:
if (route.node.value.overrides?.remove)
{
  // Move metadata to parent, depends on your use cases
  route.parent.addToMeta(route.node.value.overrides.meta);
  // Remove from routing table
  route.delete();
}

If anyone's interested, that approach works. It would be nice to have this built-in though, maybe as the default export of __route.js as suggested initially; or __route.json and __route.yaml for syntax consistency with <route> blocks.

Random remark: route.node is described as a private variable in code, yet there is no access to overrides object from EditableTreeNode. I feel like one public API is missing?

This method solves my problem, but I have another question
Using definePage configuration, unable to use route.node.value.overrides to access route information
But in some cases I have to use the definePage configuration route information

Such as:

image

In this case, I cannot use the route block to configure

image

@posva
Copy link
Owner Author

posva commented Aug 15, 2024

@AAABingBing in your case, use definePage() and add a pass through template:

<RouterView />

That will add component: () => h(RouterView) which is like not having a component.

@a81n9
Copy link

a81n9 commented Aug 16, 2024

@AAABingBing in your case, use definePage() and add a pass through template:

<RouterView />

That will add component: () => h(RouterView) which is like not having a component.

Sorry, maybe I understand incorrectly

<script setup>
import IconLayout from '~icons/tabler/layout'

definePage({
  remove: true,
  meta: {
    title: 'Test',
    icon: IconLayout,
  },
})
</script>

<template>
  <RouterView />
</template>

This way still unable to use route.node.value.overrides to access route information
It is still necessary to use the route block

VueRouter({
  extendRoute(route) {
    // if (route.node.value.overrides?.remove) {
    //   route.parent.addToMeta(route.node.value.overrides.meta)
    //   route.delete()
    // }
  },
  dts: 'src/typed-router.d.ts',
}),

Copy link
Owner Author

posva commented Aug 16, 2024

Just open a discussion with all the information. I personally don't understand what you are trying to do

@a81n9
Copy link

a81n9 commented Aug 16, 2024

I use the above way to add metadata to the non-navigable parent nodes
But I found that this method must use route blocks instead of definePage() instead

Just like this, i don’t know how to use imported IconLayout icon in the route block:

image

@a81n9
Copy link

a81n9 commented Aug 16, 2024

Just open a discussion with all the information. I personally don't understand what you are trying to do

I see
Thank you for your reply

@rickiewars
Copy link

rickiewars commented Oct 21, 2024

For anyone interested, I’d like to share my temporary workaround for this issue. In this example, you can add a route.js file to any folder where you want to recursively apply certain metadata or settings.

Add the following to your vite.config.ts file under plugins.VueRouter:

async beforeWriteFiles(rootRoute) {
  const forEachRoute = (
    route: EditableTreeNode,
    callback: (route: EditableTreeNode) => Promise<void>
  ) => {
    callback(route)
    if (route.children) {
      route.children.forEach((childRoute: EditableTreeNode) => {
        forEachRoute(childRoute, callback)
      })
    }
  }

  type ExtendRouteData = {
    route: EditableTreeNode
    data: any
  }
  const promises: Array<Promise<ExtendRouteData | null>> = []

  const loadExtendedRoute = async (route: EditableTreeNode) => {
    const pagesDir = fileURLToPath(new URL('./resources/pages', import.meta.url))
    const routeFile = path.join(pagesDir, route.fullPath, 'route.js')
    promises.push(
      import(routeFile)
        .then((module) => {
          return { route: route, data: module.default }
        })
        .catch(() => null)
    )
  }

  forEachRoute(rootRoute, async (route: EditableTreeNode) => {
    if (route.isPassThrough) {
      loadExtendedRoute(route)
    }
  })

  await Promise.all(promises).then((results) => {
    results.forEach((result) => {
      if (result && result.data.meta) {
        result.route.addToMeta(result.data.meta)
      }
    })
  })
}

This approach is not without its drawbacks. Since unplugin-router doesn't monitors the route.js files to regenerate routes, changes made to the route.js files won’t be detected unless Vite is restarted. Additionally, it would be ideal to define these route overrides in a TypeScript file.

For now, this is a temporary fix, but hopefully, it will suffice until a more permanent solution becomes available.

PS: In my code-base, I use a "home" folder called resources to instead of src. If you use a src folder instead, remember to apply that instead.

    const loadExtendedRoute = async (route: EditableTreeNode) => {
-   const pagesDir = fileURLToPath(new URL('./resources/pages', import.meta.url))
+   const pagesDir = fileURLToPath(new URL('./src/pages', import.meta.url))
    const routeFile = path.join(pagesDir, route.fullPath, 'route.js')

Update: Found another caviat, children do not inherit meta from the root group for some reason (pages/route.js). I also found out that children do not inherit meta fields from their parents if they got their own meta (even if the meta-keys are different). This may make this solution a bit less usefull. Maybe I'll replace this solution with a better one if I find one.

@rickiewars
Copy link

My previous temporary solution had some inconsistencies and bugs, which I've now fixed. I’d like to share the final version of this temporary workaround.

In this approach, I recursively add metadata to the child routes to avoid any inconsistencies. However, changes to the route.js files are still not automatically detected, so the Vite dev server will need to be restarted for those changes to take effect.

Add the following to your vite.config.ts file under plugins.VueRouter:

async beforeWriteFiles(rootRoute) {
  const config = {
    src: './resources/pages',
    routeOverrideFile: 'route.js',
    forcedRecursive: true
  }

  const iterateRoutes = (
    route: EditableTreeNode,
    callback: (route: EditableTreeNode) => Promise<void> | void
  ) => {
    callback(route)
    if (route.children) {
      route.children.forEach((childRoute: EditableTreeNode) => {
        iterateRoutes(childRoute, callback)
      })
    }
  }

  type ExtendRouteData = {
    route: EditableTreeNode
    data: any
  }
  const promises: Array<Promise<ExtendRouteData | null>> = []

  const loadRouteGroupOverrides = async (route: EditableTreeNode) => {
    const pagesDir = fileURLToPath(new URL(config.src, import.meta.url))
    const routeFile = path.join(pagesDir, route.name, config.routeOverrideFile)
    promises.push(
      import(routeFile)
        .then((module) => Object({ route: route, data: module.default }))
        .catch(() => null)
    )
  }

  iterateRoutes(rootRoute, async (route: EditableTreeNode) => {
    if (route.isPassThrough) {
      loadRouteGroupOverrides(route)
    }
  })

  await Promise.all(promises).then((results: Array<ExtendRouteData | null>) => {
    results.forEach((result) => {
      if (!result) {
        return
      }

      if (result.data.meta) {
        if (config.forcedRecursive) {
          // Simple fix for root ('/') route not passing their meta to children
          iterateRoutes(result.route, (route: EditableTreeNode) => {
            route.addToMeta(result.data.meta)
          })
        } else {
          result.route.addToMeta(result.data.meta)
        }
      }

      // Override other properties if needed
    })
  })
}

I hope this proves helpful until a more permanent solution is available.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
⚡️ enhancement improvement over an existing feature
Projects
Status: 🔖 Ready
Development

No branches or pull requests

6 participants