2

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!

Denkhis
  • 165
  • 1
  • 13
  • its better to enclose the component tag inside a div and place the v-else for that div – Amaarockz Jul 05 '21 at 12:12
  • @Amaarrockz, Thank you. Is it more correct for architecture, or does it solve some problem? – Denkhis Jul 05 '21 at 12:24
  • Before I confirm that, can you please share the full code including template tags etc – Amaarockz Jul 05 '21 at 12:37
  • `a dynamic component that looks different at different screen resolutions`, looks like a CSS solvable issue, with media queries. Not something to be done in JS. – kissu Jul 05 '21 at 13:00
  • @Amaarrockz, I edited the question to show more code. – Denkhis Jul 05 '21 at 14:09
  • You don't even need to import components in Nuxt. Maybe try a simpler example of importing a component depending of a button that you'll toggle from one to the other. Then look into the window size thing. Even if I still think you should be better server with CSS. – kissu Jul 05 '21 at 14:11
  • As Kissu said, you don't need to import components explicitly...nuxt does everything for you provided you need to give defined paths in the next.config.js – Amaarockz Jul 05 '21 at 14:20
  • @kissu, I like the solution with styles, but the question remains, how to make Nuxt work correctly with window object – Denkhis Jul 05 '21 at 14:36
  • @denkhis CSS media queries are exactly this. Have a different style depending of the viewport (aka width, height, device type etc...). Baked into the browser already. – kissu Jul 05 '21 at 14:42

1 Answers1

2

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.

kissu
  • 40,416
  • 14
  • 65
  • 133
  • Found an interesting feature while using Nuxt Components. When I try to overwrite the styles of the child component in the parent, the styles in the parent are not applied. Have to put a flag ```!important```. When I import components directly, this does not happen. Is the use of ```!important``` a "crutch" in this case? – Denkhis Jul 06 '21 at 10:35
  • `!important` is not the way to go here. Prefer using deep selectors in this situation: https://vue-loader.vuejs.org/guide/scoped-css.html#deep-selectors This answer may be helpful: https://stackoverflow.com/a/55368933/8816585 @denkhis – kissu Jul 06 '21 at 12:41