6
votes

Basically I'm trying to either dynamically select a component for a matched route or have an internal rewrite so I can select a different route without changing url.

My solution right now: I have url's from an external resource I want to match, I'm catching these with a wildcard page in Nuxt using a _.js. In a middleware on this page I will determine the actual type of the page it should be (cmspage, productdetail, etc.) and place the result in the store. Then I would check if the data is found in the validate function so I can return a 404 if needed. If successful, would then use the render function to render components/pages/cms.vue or whatever type of page.

So this should work (still need most of the implementation) but it has the problem that I can't use a lot of Nuxt features (middleware, asyncData, fetch, validate, more?) that are available for pages which is exactly what I'm trying to achieve.

This nuxt config extension doesn't work but would be perfect:

{
  router: {
    extendRoutes(routes, resolve) {
      routes.push({
        path: '*',
        component: async () => {
          const pageType = 'pdp' // await getPageType()
          switch (pageType) {
            case 'cms':
              return resolve(__dirname, 'pages/cmsPage.vue')
            case 'pdp':
              return resolve(__dirname, 'pages/productDetailPage.vue')
            case 'plp':
              return resolve(__dirname, 'pages/productListPage.vue')
          }
        }
      })
    }
  }
}
3
How do you determine which page should be displayed? What determines the pageType?. Does the URL determine what kind of page to display ?ajobi
@ajobi, yes, the url is sent to magento and drupal to check if they can resolve it which determines if it is a cms, product or productlistpage.red-X
OK good, how exactly do they determine that ? they probably parse the url looking for slugs etc right ?ajobi
I believe they have a db table in which they can look up the complete slug so I can't match the pagetype on a specific url structure.red-X
Are you sure? Because that would be the obvious and a proper way to solve it. I don't see a reason why one product should be under product/product-name and other under a completely different slugajobi

3 Answers

3
votes

I am not fully sure if I get the question right, but I assume that:

  • You want one single path (route) for displaying different (dynamic) views
  • Before the route loads, you want to get the page type (probably from backend)
  • You don't want those components to be loaded to your application if they don't match the route

So if that's what you want, follow below:


  • Inside routes you need something like this:

let pageType = null;

export default new VueRouter({
  mode: "history",
  routes: [
    //... other routes
    {
      path: "/dynamic-page",
      component: () => import("./components/DynamicPage"),
      props: router => ({ pageType }),
      beforeEnter(to, from, next) {
        // getPageType() is supposed as a async method getting the page type
        getPageType().then(type => {
          pageType = type;
          next();
        });
      }
    }
  ]
});

So with the code above, we fetched the pageType and passed it to the component as a prop.

  • Next, create the dynamic component which should load different components
<template>
  <div class="dynamic-page">
    <component :is="pageType"/>
  </div>
</template>

<script>
export default {
  props: {
    pageType: String
  },

  components: {
    ProductList: () => import("../dynamic-pages/ProductListPage"),
    Cms: () => import("../dynamic-pages/CmsPage"),
    ProductDetail: () => import("../dynamic-pages/ProductDetailPage")
  }
};
</script>

So that component using lazy loading, will load only 1 component. And that is defined by pageType prop.

I made a CodeSandbox example

0
votes

I am not fully sure if I get the question right, but I assume that:

You want to load the page dynamically bases on some conditions.

I have this solution.

import Vue from "vue";
import VueRouter from "vue-router";
import HomePage from "./components/HomePage";

Vue.use(VueRouter);

function getRandomPage() {
  const pageTypes = ["ProductList", "Cms", "ProductDetail"];
  let min = 0;
  let max = 2;
  min = Math.ceil(min);
  max = Math.floor(max);
  const random = Math.floor(Math.random() * (max - min + 1)) + min;
  return pageTypes[random];
}

let pageType = null;

function getPageType() {
  return new Promise(res => setTimeout(res(getRandomPage()), 1500));
}

export default new VueRouter({
  mode: "history",
  routes: [
    {
      path: "/",
      component: HomePage
    },

    {
      path: "/dynamic-page",
      component: () => {
        let page = "ProductListPage.vue"
        getPageType().then(type => {
          pageType = type;
          console.log(pageType)
          if (pageType === "ProductList") {
            page = "ProductListPage.vue";
          } else if (pageType === "Cms") {
            page = "CmsPage.vue";
          } else {
            page = "ProductDetailPage.vue";
          }
        })

        return import("./dynamic-pages/" + page)
      },
    }
  ]
});
0
votes

I faced the same issue and I found this PR on github : https://github.com/nuxt/nuxt.js/pull/7754

The solution would be something like this :

{
  router: {
    async extendRoutes(routes, resolve) {
      const pageType = await getPageType()
      let component
      switch (pageType) {
            case 'cms':
              component = resolve(__dirname, 'pages/cmsPage.vue')
            case 'pdp':
              component = resolve(__dirname, 'pages/productDetailPage.vue')
            case 'plp':
              component = resolve(__dirname, 'pages/productListPage.vue')
      }
      routes.push({
        path: '*',
        component
      })
    }
  }
}

It has been merged in v2.14.0, and work well on my project !