vueschool.io         @vueschoo_io

Rolf Haug

Teacher @ Vue School 

Vue.js Community Partner

Vue.js Oslo meetups organizer

Full Stack developer & Consultant

Foodie 🌶

Oslo, Norway

Alex

Kyriakidis

Teacher @ Vue School
Author of The Majesty of Vue.js 1 & 2
Vue.js Contributor

Enterprise Consultant

Amsterdam

#1 Learning Resource for Vue.js

Our goal

300+ Lessons from 20 video courses

70 000 users have joined us already!

Alex Kyriakidis

Rolf Haug

Debbie O'Brien

Chris Fritz

Maria Lamardo

Roman Kuba

Sébastien Chopin

Alexandre Chopin

Alexander Lichter

Lydia Hallie

💪 🤓

Power User

I love...

...shortcuts

You probably do too?

Why don't we implement more keyboard shortcuts in our web apps?

Application Shortcuts

with a Renderless Event Component

Global Events

window.addEventListener()
<button @click="doSomething">My Button</button>

What is a

Renderless Component?

<template>
  ...
</template>

<script>
export default {
  props: {
    //...
  },
  data () {
    return { ... }
  }
  methods: {
    // ...
  }
}
</script>

Does not render it's own HTML

Only manages state and behavior

Render function

export default {

  render () {
    
  },
  
  data () {
    return {
      // ...
    }
  },
  
  methods: {
    //...
  }
}

Component API

<event-listener event="scroll" @fired="doSomething" />

EventListener.vue

export default {
  render () {},
  props: {
    event: {
      type: String,
      required: true
    }
  },
  methods: {
    handle (e) {
      this.$emit('fired', e)
    }
  },
  mounted () {
    window.addEventListener(this.event, this.handle)
  },
  destroyed () {
    window.removeEventListener(this.event, this.handle)
  }
}

Scroll Demo

<event-listener event="scroll" @fired="changeBackgroundColor" />

Network Demo

<event-listener event="offline" @fired="showOfflineMessage" />
<event-listener event="online" @fired="hideOfflineMessage" />

👆 Native events

Network Event

Notify the user or restrict certain actions

Shortcuts Demo

Play/Pause Demo

<event-listener event="keyup" @fired="toggleVideo"/>
// ...
methods: {
  toggleVideo (event) {
    if (event.code === 'Space') {
      const video = this.$refs.video

      video.paused ? video.play() : video.pause()
    }
  }
}
<video ref="video" src="/puppiness.mp4" />
<event-listener event="keyup" @fired="toggleVideo"/>
<event-listener @keydown.space="toggleVideo"/>

How can we use the v-on directive instead?

Doesn't feel like proper Vue

How can we get the listeners?

export default {
  render () {},
  props: {
    event: {
      type: String,
      required: true
    }
  },
  methods: {
    handle (e) {
      this.$emit('fired', e)
    }
  },
  mounted () {
    window.addEventListener(this.event, this.handle)
  },
  destroyed () {
    window.removeEventListener(this.event, this.handle)
  }
}

Register listeners

export default {
  render () {},
  props: {
    event: {
      type: String,
      required: true
    }
  },
  methods: {
    handle (e) {
      this.$emit('fired', e)
    }
  },
  mounted () {
    Object.keys(this.$listeners)
      .forEach(event => {
        const handler = this.$listeners[event]
        window.addEventListener(event, handler)
      })
  },
  destroyed () {
    window.removeEventListener(this.event, this.handle)
  }
}

Refactor

export default {
  render () {},
  props: {
    event: {
      type: String,
      required: true
    }
  },
  methods: {
    handle (e) {
      this.$emit('fired', e)
    }
  },
  mounted () {
    Object.keys(this.$listeners)
      .forEach(event => {
        const handler = this.$listeners[event]
        window.addEventListener(event, handler)
      })
  },
  destroyed () {
    window.removeEventListener(this.event, this.handle)
  }
}

Remove listeners

export default {
  render () {},
  
  mounted () {
    Object.keys(this.$listeners)
      .forEach(event => {
        const handler = this.$listeners[event]
        window.addEventListener(event, handler)
      })
  },
  
  destroyed () {
    Object.keys(this.$listeners)
      .forEach(event => {
        const handler = this.$listeners[event]
        window.removeEventListener(event, handler)
      })
  }
}
export default {
  render () {},
  
  mounted () {
    Object.keys(this.$listeners)
      .forEach(event => {
        const handler = this.$listeners[event]
        window.addEventListener(event, handler)
      })
  },
  
  destroyed () {
    Object.keys(this.$listeners)
      .forEach(event => {
        const handler = this.$listeners[event]
        window.removeEventListener(event, handler)
      })
  }
}

Event Listener 2.0

New Component API

<event-listener @keydown.space="toggleVideo"/>

Vue takes care of the .space key modifier for us automatically

🥳

Refactor handler

// ...
methods: {
  toggleVideo (event) {
    if (event.code === 'Space') {
      const video = this.$refs.video

      video.paused ? video.play() : video.pause()
    }
  }
}

Network Example

<event-listener event="offline" @fired="showOfflineMessage" />
<event-listener event="online" @fired="hideOfflineMessage" />
<event-listener @offline="showOfflineMessage" @online="hideOfflineMessage" />

Search Demo

cmd + f / ctrl + f

<event-listener 
  @keydown.meta.70="showSearch = true" 
  @keydown.ctrl.70="showSearch = true" 

  @keydown.esc="showSearch = false"
/>

<div v-if="showSearch">
  <!-- awesome search form goes here -->
</div>

Table Demo

Companion Repository

with resources

📸

vue-global-events

github.com/shentao/vue-global-events

Damian Dulisz

Eduardo San Martin Morote

@DamianDulisz

@posva

vueschool.io         @vueschoo_io

@rahaug

Vue.js 3 courses
coming soon

🕺

vueschool.io/love/vue-amsterdam

Grab your 1 month free-pass

Booth

Ciao

👋

Application Shortcuts (2020 Vue Amsterdam)

By Rolf Haug

Application Shortcuts (2020 Vue Amsterdam)

Application Shortcuts with a Renderless Event Component

  • 513