How to dynamically switch a component based on the existence of window in Nuxt.js?

Viewed 866

I have a dynamic component that looks different at different screen resolutions.

<template>
  <div>
    <headerComponent></headerComponent>
    <div v-if="!large" class="placeholder"></div>
    <component
        v-else
        :is="tariffBlock"
    >
    </component>
  </div>
</template>

<script>
  import smallComponent from '@/components/small-component'
  import largeComponent from '@/components/large-component'
  import headerComponent from '@/components/header-component'

  const components = {
    smallComponent,
    largeComponent
  }
  export default {
    components: {
      headerComponent
    },
    data () {
      return {
        large: false
      }
    },
    computed: {
      getComponent () {
        if (!this.large) return components.smallComponent
        return components.largeComponent
      }
    },
    created () {
      if (process.browser) {
        this.large = window.matchMedia('(min-width: 1200px)').matches
      }
    }
  }
</script>

By default, a smallComponent is shown, and then a largeComponent. To avoid "jumping" I decided to show the placeholder while large === false.

To avoid the error window in not defined I use the check for process.browser.

PROBLEM: placeholder is only shown in dev mode, but when I start generate the placeholder is not displayed.

The following solutions DIDN'T help:

1.

created () {
  this.$nextTick(() => {
    if (process.browser) {
      this.large = window.matchMedia('(min-width: 1200px)').matches
    }
  })
}
created () {
  this.$nextTick(() => {
    this.large = window.matchMedia('(min-width: 1200px)').matches
  })
}
mounted () {
  this.large = window.matchMedia('(min-width: 1200px)').matches
}

and with the addition process.browser and nextTick()

  1. Creating a mixin with ssr: false, mode: client

Thanks in advance!

1 Answers

This is how you toggle between components in Nuxt.js

<template>
  <div>
    <div @click="toggleComponents">toggle components</div>
    <hr />
    <first-component></first-component>
    <second-component></second-component>
    <hr />
    <component :is="firstOrSecond"></component>
  </div>
</template>

<script>
export default {
  data() {
    return {
      firstOrSecond: 'first-component',
    }
  },
  methods: {
    toggleComponents() {
      if (this.firstOrSecond === 'first-component') {
        this.firstOrSecond = 'second-component'
      } else {
        this.firstOrSecond = 'first-component'
      }
    },
  },
}
</script>

You don't need to import them, it's done automatically if you have the right configuration, as explained here: https://nuxtjs.org/blog/improve-your-developer-experience-with-nuxt-components

In this snippet of code, first-component and second-component are shown initially (between the two hr) just to be sure that you have them properly loaded already. You can of course remove them afterwards.


Not recommended

This is what you're looking for. Again, this is probably not how you should handle some visual changes. Prefer CSS for this use-case.

<template>
  <div>
    <component :is="firstOrSecond"></component>
  </div>
</template>

<script>
export default {
  data() {
    return {
      firstOrSecond: 'first-component',
    }
  },
  mounted() {
    window.addEventListener('resize', this.toggleComponentDependingOfWindowWidth)
  },
  beforeDestroy() {
    // important, otherwise you'll have the eventListener all over your SPA
    window.removeEventListener('resize', this.toggleComponentDependingOfWindowWidth)
  },
  methods: {
    toggleComponentDependingOfWindowWidth() {
      console.log('current size of the window', window.innerWidth)

      if (window.innerWidth > 1200) {
        this.firstOrSecond = 'second-component'
      } else {
        this.firstOrSecond = 'first-component'
      }
    },
  },
}
</script>

PS: if you really wish to use this solution, at least use a throttle because the window event will trigger a lot and it can cause your UI to be super sluggish pretty quickly.

Related