Giter VIP home page Giter VIP logo

Comments (13)

chrisvfritz avatar chrisvfritz commented on May 13, 2024 6

For this effect, I'd recommend using layouts a little bit differently. Instead of defining them in your view components, you could define them as a meta property on your routes (potentially with a default to fall back on). Below is an example refactor that I believe should achieve what you want. 🙂

diff --git a/src/app.vue b/src/app.vue
index 6857411..b9702a8 100644
--- a/src/app.vue
+++ b/src/app.vue
@@ -9,6 +9,14 @@ export default {
       return title ? `${title} | ${appConfig.title}` : appConfig.title
     },
   },
+  computed: {
+    LayoutComponent() {
+      return (
+        (this.$route.meta && this.$route.meta.layout) ||
+        require('@layouts/main').default
+      )
+    },
+  },
 }
 </script>
 
@@ -18,7 +26,22 @@ export default {
     Even when routes use the same component, treat them
     as distinct and create the component again.
     -->
-    <router-view :key="$route.fullPath"/>
+    <transition
+      name="fade"
+      mode="out-in"
+    >
+      <component
+        :is="LayoutComponent"
+        :key="LayoutComponent.name || LayoutComponent.__file"
+      >
+        <transition
+          name="fade"
+          mode="out-in"
+        >
+          <router-view :key="fullPath"/>
+        </transition>
+      </component>
+    </transition>
   </div>
 </template>
 
@@ -88,4 +111,17 @@ h6 {
 #nprogress .bar {
   background: $color-link-text;
 }
+
+// ===
+// Transitions
+// ===
+
+.fade-enter-active,
+.fade-leave-active {
+  transition: opacity 0.5s;
+}
+.fade-enter,
+.fade-leave-to {
+  opacity: 0;
+}
 </style>
diff --git a/src/router/views/404.vue b/src/router/views/404.vue
index 4ce46e9..1921d60 100644
--- a/src/router/views/404.vue
+++ b/src/router/views/404.vue
@@ -1,12 +1,9 @@
 <script>
-import Layout from '@layouts/main'
-
 export default {
   page: {
     title: '404',
     meta: [{ name: 'description', content: '404' }],
   },
-  components: { Layout },
   props: {
     resource: {
       type: String,
@@ -17,15 +14,13 @@ export default {
 </script>
 
 <template>
-  <Layout>
-    <h1 :class="$style.title">
-      404
-      <span v-if="resource">
-        {{ resource }}
-      </span>
-      Not Found
-    </h1>
-  </Layout>
+  <h1 :class="$style.title">
+    404
+    <span v-if="resource">
+      {{ resource }}
+    </span>
+    Not Found
+  </h1>
 </template>
 
 <style lang="scss" module>
diff --git a/src/router/views/home.vue b/src/router/views/home.vue
index 540e538..dc71078 100644
--- a/src/router/views/home.vue
+++ b/src/router/views/home.vue
@@ -1,22 +1,20 @@
 <script>
 import appConfig from '@src/app.config'
-import Layout from '@layouts/main'
 
 export default {
   page: {
     title: 'Home',
     meta: [{ name: 'description', content: appConfig.description }],
   },
-  components: { Layout },
 }
 </script>
 
 <template>
-  <Layout>
+  <div>
     <h1>Home Page</h1>
     <img
       src="@assets/images/logo.png"
       alt="Logo"
     >
-  </Layout>
+  </div>
 </template>
diff --git a/src/router/views/loading.vue b/src/router/views/loading.vue
index df01943..eaf6e8f 100644
--- a/src/router/views/loading.vue
+++ b/src/router/views/loading.vue
@@ -1,25 +1,20 @@
 <script>
-import Layout from '@layouts/main'
-
 export default {
   page: {
     title: 'Loading page...',
     meta: [{ name: 'description', content: 'Loading page...' }],
   },
-  components: { Layout },
 }
 </script>
 
 <template>
-  <Layout>
-    <transition appear>
-      <BaseIcon
-        :class="$style.loadingIcon"
-        name="sync"
-        spin
-      />
-    </transition>
-  </Layout>
+  <transition appear>
+    <BaseIcon
+      :class="$style.loadingIcon"
+      name="sync"
+      spin
+    />
+  </transition>
 </template>
 
 <style lang="scss" module>
diff --git a/src/router/views/login.vue b/src/router/views/login.vue
index 7352feb..0f17226 100644
--- a/src/router/views/login.vue
+++ b/src/router/views/login.vue
@@ -1,5 +1,4 @@
 <script>
-import Layout from '@layouts/main'
 import { authMethods } from '@state/helpers'
 import appConfig from '@src/app.config'
 
@@ -8,7 +7,6 @@ export default {
     title: 'Log in',
     meta: [{ name: 'description', content: `Log in to ${appConfig.title}` }],
   },
-  components: { Layout },
   data() {
     return {
       username: '',
@@ -43,36 +41,34 @@ export default {
 </script>
 
 <template>
-  <Layout>
-    <form
-      :class="$style.form"
-      @submit.prevent="tryToLogIn"
+  <form
+    :class="$style.form"
+    @submit.prevent="tryToLogIn"
+  >
+    <BaseInput
+      v-model="username"
+      name="username"
+    />
+    <BaseInput
+      v-model="password"
+      name="password"
+      type="password"
+    />
+    <BaseButton
+      :disabled="tryingToLogIn"
+      type="submit"
     >
-      <BaseInput
-        v-model="username"
-        name="username"
+      <BaseIcon
+        v-if="tryingToLogIn"
+        name="sync"
+        spin
       />
-      <BaseInput
-        v-model="password"
-        name="password"
-        type="password"
-      />
-      <BaseButton
-        :disabled="tryingToLogIn"
-        type="submit"
-      >
-        <BaseIcon
-          v-if="tryingToLogIn"
-          name="sync"
-          spin
-        />
-        <span v-else>Log in</span>
-      </BaseButton>
-      <p v-if="authError">
-        There was an error logging in to your account.
-      </p>
-    </form>
-  </Layout>
+      <span v-else>Log in</span>
+    </BaseButton>
+    <p v-if="authError">
+      There was an error logging in to your account.
+    </p>
+  </form>
 </template>
 
 <style lang="scss" module>
diff --git a/src/router/views/profile.vue b/src/router/views/profile.vue
index 3a500f3..ea57ae4 100644
--- a/src/router/views/profile.vue
+++ b/src/router/views/profile.vue
@@ -1,6 +1,4 @@
 <script>
-import Layout from '@layouts/main'
-
 export default {
   page() {
     return {
@@ -13,7 +11,6 @@ export default {
       ],
     }
   },
-  components: { Layout },
   props: {
     user: {
       type: Object,
@@ -24,12 +21,12 @@ export default {
 </script>
 
 <template>
-  <Layout>
+  <div>
     <h1>
       <BaseIcon name="user"/>
       {{ user.name }}
       Profile
     </h1>
     <pre>{{ user }}</pre>
-  </Layout>
+  </div>
 </template>
diff --git a/src/router/views/timeout.vue b/src/router/views/timeout.vue
index 97a96d8..af443af 100644
--- a/src/router/views/timeout.vue
+++ b/src/router/views/timeout.vue
@@ -1,6 +1,4 @@
 <script>
-import Layout from '@layouts/main'
-
 export default {
   page: {
     title: 'Page timeout',
@@ -8,16 +6,13 @@ export default {
       { name: 'description', content: 'The page timed out while loading.' },
     ],
   },
-  components: { Layout },
 }
 </script>
 
 <template>
-  <Layout>
-    <h1 :class="$style.title">
-      The page timed out while loading
-    </h1>
-  </Layout>
+  <h1 :class="$style.title">
+    The page timed out while loading
+  </h1>
 </template>
 
 <style lang="scss" module>

Let me know if that solves the problem for you.

from vue-enterprise-boilerplate.

wrurik avatar wrurik commented on May 13, 2024 2

@dnewkerk Did you ever find a solution to the multiple named slots issue?
I'm facing the same problem now and the only ways I could think of fixing it are:

  1. include content for all named slots in route meta data.
  2. maybe use something like portal-vue

Both solutions seem don't feel quite right for me, so I'm hoping you are willing to share what you came up with?

from vue-enterprise-boilerplate.

ma53 avatar ma53 commented on May 13, 2024 1

It did indeed! Thanks, @chrisvfritz, you're the best.

from vue-enterprise-boilerplate.

chrisvfritz avatar chrisvfritz commented on May 13, 2024 1

@marceloavf The easiest way might be to add something like a new currentRouteStatus property to the router, e.g. in src/router/index.js:

router.currentRouteStatus = Vue.observable({
  isLoaded: false,
})

router.beforeEach((routeTo, routeFrom, next) => {
  router.currentRouteStatus.isLoaded = !routeTo.matched.some(
    (route) =>
      typeof route.components === 'function' ||
      typeof route.components.default === 'function'
  )
  // ...

Then update isLoaded inside the AsyncHandler:

function lazyLoadView(AsyncView) {
  const AsyncHandler = () => ({
    component: AsyncView.then((componentConfig) => {
      require('@router').default.currentRouteStatus.isLoaded = true
      return componentConfig
    }),
    // ...

And finally, inside the template of app.vue, you could check $router.currentRouteStatus.isLoaded to see if the current route is loaded and only render the layout if it is. I haven't tested this strategy, so there may be edge cases I'm not currently thinking of, but this should give you a starting point. 🙂

from vue-enterprise-boilerplate.

chrisvfritz avatar chrisvfritz commented on May 13, 2024 1

@marceloavf Great question. In order to prevent it from interfering, you'd have to render the <RouterView> without a wrapping layout when the current route is not loaded, rather than just rendering nothing.

from vue-enterprise-boilerplate.

chrisvfritz avatar chrisvfritz commented on May 13, 2024 1

@dnewkerk Hopefully you've solved the issue on your own by now, but you may want $route.path in that case rather than $route.fullPath (see the Vue Router docs for the difference).

Also, I'm reopening this as I'm thinking about updating the routing strategy to something along these lines, as I've had a number of projects that have needed this kind of strategy and am thinking there might be more advantages than disadvantages for most projects.

from vue-enterprise-boilerplate.

chrisvfritz avatar chrisvfritz commented on May 13, 2024

I'm going to assume this solves the problem, but happy to reopen if it doesn't. 🙂

from vue-enterprise-boilerplate.

marceloavf avatar marceloavf commented on May 13, 2024

Hey @chrisvfritz, you did an awesome refactor here!

I implemented this content in my project but I got a little problem,

On the first load, it always show a little of the default layout until it gets totally loaded and goes to the desired layout specified in the meta, is there a way to solve this?

I was thinking about making a "loading" kind of layout to be the default and set the main to everyone else, but I don't think it's the best solution :/

LayoutTransition

from vue-enterprise-boilerplate.

chrisvfritz avatar chrisvfritz commented on May 13, 2024

@marceloavf That idea with the loading layout sounds fine actually. 🙂 You'd just have to always define a layout. You could also simply not have a default layout, or only show the default layout after the component for the current route has finished downloading.

from vue-enterprise-boilerplate.

marceloavf avatar marceloavf commented on May 13, 2024

Nice @chrisvfritz, I was thinking about this last idea, but I couldn't find a way to implement this. 😞

from vue-enterprise-boilerplate.

marceloavf avatar marceloavf commented on May 13, 2024

Works like charm @chrisvfritz, just a question, this code will not block this one from lazyLoadView?

    // A component to use while the component is loading.
    loading: require('@views/_loading').default,
    // Delay before showing the loading component.
    // Default: 200 (milliseconds).
    delay: 400,

from vue-enterprise-boilerplate.

dnewkerk avatar dnewkerk commented on May 13, 2024

@chrisvfritz thanks for the alternate way of using layouts above. I ran into a similar issue with the layout being re-rendered on route changes. For reference, the app I'm working on (tagnifi.com) has a filterable data table, and the selected filter params get persisted in the URL so the filtered search can be saved/shared. However every filter change causes the layout to re-render, so the form loses the current tab index, some temporary state on the page is lost, etc.

I tried using the alternate approach you provided above, and while it resolves the above issue with route changes, I can't figure out now if/how I can use multiple named slots in my layout. The filter area appears in a specific spot of the layout using <template v-slot:before-content>...</template> (it stays sticky at the top of the page while only the results scroll) though I get compile errors now when trying to do this without the Layout wrapper in my component.

Anyhow if you might be able to point me in the right direction, it would be greatly appreciated. Thanks!

from vue-enterprise-boilerplate.

myleslee avatar myleslee commented on May 13, 2024

@chrisvfritz is there a branch that contains the code changes illustrated inside this comment? Thanks!

from vue-enterprise-boilerplate.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.