Manage your Modal window(s) effortlessly using EventBus

Manage your Modal window(s) effortlessly using EventBus

As a both Vue.js and React developer, I've been assigned to/leading ... Vue.js — Manage your Modal window(s) effortlessly using EventBus.

As a both Vue.js and React developer, I’ve been assigned to/leading various projects which were mostly about building UIs. One thing I was always battling was popup Dialogs — not in the sense that they didn’t work, but I was always left wondering whether there’s a better way to code them.

I’ve also seen other approaches, mostly storing modal window states in global app store, which of course required developers to store more and more data as the app grew — and so did the number of used modal windows. The store quickly became bloated with data that was relevant only for a short amount of time for a small part of the UI.

For those interested only in the final open source code, it’s at the end of the article as usual!

While building SPAs with Vue.js, my approach was mostly to create a <Modal /> wrapper component (with a slot for the content) which held an internal boolean open state and had methods such as open() and close(), which toggled its state. I accessed those methods through a $ref in the Component that displayed the modal. An example could look similar to this:

methods: {
  openModal () {
    this.$refs.modal.show()
  }
}

I liked this approach in a sense that each Component had its own modal and was responsible for it. Thanks to this the application wasn’t prone to monolithic structure. I don’t much like keeping the modal state inside the Component that’s displaying the modal (and passing it as a prop) because the code often becomes way too repetitive and robust if you have to change the modal state in the displaying components. That’s why I chose the approach where the modal took care of its own open state and exposed the toggle methods through $ref.

The downside to this was that there were simply too many <Modal /> components as my app grew. They were also inside the Component that was displaying them HTML wise, so there were sometimes overlay issues which z-index simply couldn’t solve. I was wondering whether there’s a better way.

One modal root to rule them all

I already mentioned the z-index issue. When you need to display something on the top of everything else HTML wise, it only makes sense that your tree should be structured accordingly, instead of using hacks to force your elements to appear on the top of your site when actually.. they’re not.

So we’re aiming for something like this

<!doctype html>
<html>
<body>
  <div id="app">
    <div class="content">Lorem Ipsum..</div>
    <div id="modal-root"></div>
  </div>
</body>
</html>

Where #modal-root will always be on the bottom (but top, visually 😅) of your structure and it will always display your current selected element.

That means if you’re aiming to have multiple modals in your app, this is not an ideal approach for you. I will go with cases where I need only one dialog open at a time.

We will accomplish this reactivity of the root element by using EventBus, so any child can trigger opening the dialog displaying any component from anywhere.

1. Creating the EventBus

import Vue from 'vue'

export const ModalBus = new Vue()

VueModalBus - eventBus.js

Let’s start by creating a simple ModalBus inside our eventBus.js file. I chose a named export because I aim to work with more event buses, but that’s really a personal preference. If you feel comfortable using default exports, feel free to do so.

2. Creating the Modal (presentational) Component

<template>
  <div class="modal-backdrop"
    v-show="isOpen"
    :class="{open: isOpen}"
    @click="$emit('onClose')">

    <div class="modal-dialog" :class="{open: isOpen}" @click.stop>
      <div class="modal-title" v-if="title">{{ title }}</div>
      <div class="modal-body">
        <slot />
      </div>
    </div>

  </div>
</template>

<script>
export default {
  props: {
    isOpen: Boolean,
    title: String
  }
}
</script>

<style scoped>
.modal-backdrop {
  background: rgba(250, 250, 250, 0.8);
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: row;
  justify-content: center;
  align-items: center
}
.modal-dialog {
  width: 30rem;
  background: rgb(255, 255, 255);
  padding: 1.5rem 2rem;
  box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
  border-radius: 0.3rem;
}
.modal-title {
  font-weight: bold;
  font-size: 1.3rem;
  margin-bottom: 1rem;
  color: rgb(100, 100, 100);
}
.modal-body {
  color: rgb(180, 180, 180);
}
</style>

VueModalBus - Modal.vue

As you can see, this is merely a presentational component. It gets the isOpen and title properties from a parent we don’t have yet.

We handle several things in here:

  • Emitting an onClose call when we click the backdrop outside the dialog
  • Displaying the whole Modal component conditional logic
  • Displaying title conditional logic
  • Using <slot /> to display children inside the Modal
  • Defining some basic styles

3. Creating the ModalRoot component

<template>
  <Modal :isOpen="!!component" :title="title" @onClose="handleClose">
    <component :is="component" @onClose="handleClose" v-bind="props" />
  </Modal>
</template>

<script>
import { ModalBus } from '../eventBus'
import Modal from './common/Modal'
export default {
  data () {
    return {
      component: null,
      title: '',
      props: null
    }
  },
  created () {
    ModalBus.$on('open', ({ component, title = '', props = null }) => {
      this.component = component
      this.title = title
      this.props = props
    })
    document.addEventListener('keyup', this.handleKeyup)
  },
  beforeDestroy () {
    document.removeEventListener('keyup', this.handleKeyup)
  },
  methods: {
    handleClose () {
      this.component = null
    },
    handleKeyup (e) {
      if (e.keyCode === 27) this.handleClose()
    }
  },
  components: { Modal },
}
</script>

VueModalBus - ModalRoot.vue

The ModalRoot component is the one that’s listening to the ModalBus events and handling all the logic — and the one that’s displaying the <Modal />. It’s also the one we want to place inside our <App /> on the bottom of our HTML structure.

Let’s debunk what’s happening under the hood:

  • Script:

We’re storing 3 things in our state (data):

data () {
  return {
    component: null,
    title: '',
    props: null
  }
},

Those properties will be received via the ModalBus and they’re carrying the information about the component we’re going to display, the dialog’s title and any needed props for the child component inside the <Modal />.

created () {
  ModalBus.$on('open', ({ component, title = '', props = null }) => {
    this.component = component
    this.title = title
    this.props = props
  })
  document.addEventListener('keyup', this.handleKeyup)
},

Right upon the <ModalRoot />'s creation, we’re connecting to the ModalBus and listening to the open event, which is what we’ll call when we want to open the Modal from any component, passing our values in. You can see we’re accepting and setting those parameters that I mentioned above — component, title and props. We’re also adding a listener for the (Escape) key.

beforeDestroy () {
  document.removeEventListener('keyup', this.handleKeyup)
},

We have to be careful and destroy the listener before the <ModalRoot /> itself is destroyed.

methods: {
  handleClose () {
    this.component = null
  },
  handleKeyup (e) {
    if (e.keyCode === 27) this.handleClose()
  }
},

Then there’s our simple handleClose() method which is only setting component to null (which changes our isOpen prop for the <Modal /> component, more in the template section) and handleKeyup() method, which is checking whether we’re pressing the Escape key and then calling handleClose(), should the condition be met.

(Note that you can also listen to the _close_ event the same way you listen to _open_ and call handleClose() inside, so the modal can also be closed from anywhere)

  • Template:
  • We’re using our <Modal /> presentational component to wrap our dynamic <component />
  • We’re setting the isOpen and title props to <Modal /> based on our incoming data (isOpen is true whenever component’s not empty)
  • We’re also passing an @onClose listener to react to <Modal />'s backdrop click, which is when an $emit('onClose') happens in the <Modal />
<component :is="component" @onClose="handleClose" v-bind="props" />
  • We’re displaying the <component /> inside the <Modal />, therefore making use of the <slot /> functionality inside it
  • We’re also listening to the @onClose emit (the same way like in <Modal /> )
  • We’re binding whatever props we set in the open() ModalBus call to the <component />, so they find their way to the destined component.

4. Making use of the

Everything should be connected now — we just need to make use of it! I’ve created a few examples which are using Tailwind to show how the <ModalRoot /> can actually be used.

<template>
  <div id="app">
    <div class="content">
      <Button @click="openSuccessAlert" class="mr-2">
        Success alert
      </Button>
      <Button @click="openDangerAlert" class="mr-2">
        Danger alert
      </Button>
      <Button @click="openClosableInside" class="mr-2">
        Close from inside
      </Button>
      <Button @click="openSignIn">
        Sign in form
      </Button>
    </div>
    <ModalRoot />
  </div>
</template>

<script>
import { ModalBus } from './eventBus';
import ModalRoot from './components/ModalRoot'
import Button from './components/common/Button'
import SignInForm from './components/examples/SignInForm'
import Alert from './components/examples/Alert';
import ClosableInside from './components/examples/ClosableInside';
export default {
  name: 'app',
  components: {
    Button,
    ModalRoot
  },
  methods: {
    openSuccessAlert () {
      ModalBus.$emit('open', {
        component: Alert,
        props: { text: 'Everything is working great!', type: 'success' }
      })
    },
    openDangerAlert () {
      const props = {
        type: 'error',
        text: 'The server returned 500 again! omg!'
      }
      ModalBus.$emit('open', { component: Alert, title: 'An error has occured', props })
    },
    openClosableInside () {
      ModalBus.$emit('open', { component: ClosableInside, title: 'Close dialog from component' })
    },
    openSignIn () {
      ModalBus.$emit('open', { component: SignInForm, title: 'New group' })
    }
  }
}
</script>

<style scoped>
.content {
  width: 100vw;
  height: 100vh;
  display: flex;
  flex-direction: row;
  justify-content: center;
  align-items: center
}
</style>

VueModalBus - App.vue

1. Success alert

The first case is very simple. We’re calling a generic <Alert /> component and passing some basic config in:

openSuccessAlert () {
  ModalBus.$emit('open', {
    component: Alert,
    props: { text: 'Everything is working great!', type: 'success' }
  })
},

The <Alert /> component is expecting a text and a type. We’re not passing any title for our dialog, so it won’t be displayed.

<template>
  <div :class="classes">
    <span class="block sm:inline">{{ text }}</span>
  </div>
</template>

<script>
export default {
  props: {
    type: {
      type: String,
      value: 'error' | 'info' | 'success' | 'warning'
    },
    text: String
  },
  computed: {
    color () {
      switch (this.type) {
        case 'error':
          return 'red'
        case 'success':
          return 'teal'
        case 'warning':
          return 'orange'
        case 'info':
        default:
          return 'gray'
      }
    },
    classes () {
      const { color } = this
      return [`bg-${color}-400 border border-${color}-200 text-white px-4 py-3 rounded-lg relative`]
    }
  },
}
</script>

VueModalBus - Alert.vue

2. Danger alert

openDangerAlert () {
  const props = {
    type: 'error',
    text: 'The server returned 500 again! omg!'
  }
  ModalBus.$emit('open', { component: Alert, title: 'An error has occured', props: props })
},

We’re using the <Alert /> component again, this time passing the "error" type and also providing a title for the dialog.

3. A Component that’s closable from inside

openClosableInside () {
  ModalBus.$emit('open', { component: ClosableInside, title: 'Close dialog from component' })
},

We’re not passing anything special to the open() call, instead the magic happens inside the <ClosableInside /> example component:

<template>
  <div>
    <p class="mb-4">
      This dialog is closable from the inside of the component!
    </p>
    <div class="text-right">
      <Button @click="$emit('onClose')" color="gray">Close</Button>
    </div>
  </div>
</template>

<script>
import Button from '../common/Button';
export default {
  components: { Button }
}
</script>

VueModalBus - ClosableInside.vue

<Button @click="$emit('onClose')" color="gray">Close</Button>

Because the <ModalRoot /> is displaying the component, in our case the <ClosableInside /> component, and it’s listening to the @onClose event, we can $emit it inside the component and the modal window will close. The “React way” would be passing this close handler via props, which is also possible, of course.

4. A Sign In form

This form is almost completely copied from the Tailwind documentation and it’s included for presentational purposes. It doesn’t do much, but you can pass in whatever props you like and work with them.

openSignIn () {
  ModalBus.$emit('open', { component: SignInForm, title: 'New user' })
}
<template>
  <form class="mb-4">
    <div class="mb-4">
      <label class="block text-gray-700 text-sm font-bold mb-2">
        Username
      </label>
      <input
        class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
        type="text" placeholder="Username">
    </div>
    <div class="mb-6">
      <label class="block text-gray-700 text-sm font-bold mb-2">
        Password
      </label>
      <input
        class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
        type="password" placeholder="******************">
    </div>
    <div class="flex items-center justify-between">
      <button
        class="bg-teal-500 hover:bg-teal-600 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline"
        type="button">
        Sign In
      </button>
      <a class="inline-block align-baseline font-bold text-sm text-teal-500 hover:text-teal-800" href="#">
        Forgot Password?
      </a>
    </div>
  </form>
</template>

VueModalBus - SignInForm.vue

The beautiful thing about this is you can extend it whatever way you want. Let’s say we want to add a modal animation!

<template>
  <transition name="fade">
    <div class="modal-backdrop"
      v-show="isOpen"
      :class="{open: isOpen}"
      @click="$emit('onClose')">

      <div class="modal-dialog" :class="{open: isOpen}" @click.stop>
        <div class="modal-title" v-if="title">{{ title }}</div>
        <div class="modal-body">
          <slot />
        </div>
      </div>

    </div>
  </transition>
</template>

<script>
export default {
  props: {
    isOpen: Boolean,
    title: String
  }
}
</script>

<style scoped>
.fade-enter-active, .fade-leave-active {
  transition: 0.5s;
}
.fade-enter, .fade-leave-to {
  opacity: 0;
}
.fade-enter .modal-dialog, .fade-leave-to .modal-dialog {
  transform: translateY(-20%);
}
.modal-backdrop {
  background: rgba(250, 250, 250, 0.8);
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: row;
  justify-content: center;
  align-items: center
}
.modal-dialog {
  width: 30rem;
  background: rgb(255, 255, 255);
  padding: 1.5rem 2rem;
  box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
  border-radius: 0.3rem;
  transition: 0.5s;
}
.modal-title {
  font-weight: bold;
  font-size: 1.3rem;
  margin-bottom: 1rem;
  color: rgb(100, 100, 100);
}
.modal-body {
  color: rgb(180, 180, 180);
}
</style>

VueModalBus - ModalAnimated.vue

We pretty much just added a <**transition name=”fade”**> and some CSS rules in the <style /> section:

.fade-enter-active, .fade-leave-active {
  transition: 0.5s;
}

.fade-enter, .fade-leave-to {
  opacity: 0;
}

.fade-enter .modal-dialog, .fade-leave-to .modal-dialog {
  transform: translateY(-20%);
}

A pretty common case is where you want to persist the modal’s (open) state on backdrop click, for example when you’re filling a form and you want to protect the user from accidentally closing the window.

<template>
  <Modal :isOpen="!!component" :title="title" @onClose="handleOutsideClick">
    <component :is="component" @onClose="handleClose" v-bind="props" />
  </Modal>
</template>

<script>
import { ModalBus } from '../eventBus'
import Modal from './common/Modal'
export default {
  data () {
    return {
      component: null,
      title: '',
      props: null,
      closeOnClick: true
    }
  },
  created () {
    ModalBus.$on('open', ({ component, title = '', props = null, closeOnClick = true }) => {
      this.component = component
      this.title = title
      this.props = props
      this.closeOnClick = closeOnClick
    })
    document.addEventListener('keyup', this.handleKeyup)
  },
  beforeDestroy () {
    document.removeEventListener('keyup', this.handleKeyup)
  },
  methods: {
    handleOutsideClick () {
      if (!this.closeOnClick) return
      this.handleClose()
    },
    handleClose () {
      this.component = null
    },
    handleKeyup (e) {
      if (e.keyCode === 27) this.handleClose()
    }
  },
  components: { Modal }
}
</script>

VueModalBus - ModalRootConfirmClose.vue

  • The <ModalRoot /> is storing another property in the data() now — **closeOnClick**: **true**. We altered the ModalBus.$on(**‘open’, ...**) function to accept the new param.
ModalBus.$on('open', ({ component, title = '', props = null, closeOnClick = true }) => {
  this.component = component
  this.title = title
  this.props = props
  this.closeOnClick = closeOnClick
})

Let’s create a different handler for the <Modal />'s @onClose event, which gets emitted when a user clicks the backdrop of the <Modal />.

@onClose="handleOutsideClick"

The body of the function now determines whether to call handleClose() or not based on the **closeOnClick**property

handleOutsideClick () {
  if (!this.closeOnClick) return
  this.handleClose()
},

And that’s it! Let’s try it with the <SignInForm /> component to see if it works by passing the closeOnClick parameter (now you can close it using only the Escape key):

openSignIn () {
  ModalBus.$emit('open', { component: SignInForm, title: 'New user', closeOnClick: false })
}

Programming a Javascript Simon Game Tutorial

Programming a Javascript Simon Game Tutorial

In this javascript tutorial, I recorded myself live programming an html5 javascript simon game.

In this javascript tutorial, I recorded myself live programming an html5 javascript simon game.

For those who don't know, I'm a full stack web developer who has been in the industry for over 5 years now. There is a lot of things I have learned along the way and I'd like to share that knowledge with anyone wanting to learn!

like this video if you found it useful and would like to see more videos of the same content.

subscribe to my channel if you are trying to improve your abilities as a web developer, software engineer, or even if you are just learning to code.

Don't forget to turn on those bell notifications!

Understanding Memoization And Dynamic Programming in Javascript

Understanding Memoization And Dynamic Programming in Javascript

In this Javascript tutorial I will explain what memoization is, how to use it, when you should use memoization, how to use memoization, what dynamic programming is, how to use memoization in dynamic programming. Memoization is a big complicated word that you may have never even heard before, but you may be surprised to know that you are most likely already using memoization without even realizing it.

Memoization is a big complicated word that you may have never even heard before, but you may be surprised to know that you are most likely already using memoization without even realizing it. Memoization is just the act of caching values so that they can be calculated quicker in the future. Memoization is really useful in all parts of programming, but where it is most useful is in dynamic programming. In this video I will explain what memoization is, how to use it, and why it is so useful especially in dynamic programming.

🧠 Concepts Covered:

  • What memoization is
  • When you should use memoization
  • How to use memoization
  • What dynamic programming is
  • How to use memoization in dynamic programming

JavaScript Tutorial: if-else Statement in JavaScript

JavaScript Tutorial: if-else Statement in JavaScript

This JavaScript tutorial is a step by step guide on JavaScript If Else Statements. Learn how to use If Else in javascript and also JavaScript If Else Statements. if-else Statement in JavaScript. JavaScript's conditional statements: if; if-else; nested-if; if-else-if. These statements allow you to control the flow of your program's execution based upon conditions known only during run time.

Decision Making in programming is similar to decision making in real life. In programming also we face some situations where we want a certain block of code to be executed when some condition is fulfilled.
A programming language uses control statements to control the flow of execution of the program based on certain conditions. These are used to cause the flow of execution to advance and branch based on changes to the state of a program.

JavaScript’s conditional statements:

  • if
  • if-else
  • nested-if
  • if-else-if

These statements allow you to control the flow of your program’s execution based upon conditions known only during run time.

  • if: if statement is the most simple decision making statement. It is used to decide whether a certain statement or block of statements will be executed or not i.e if a certain condition is true then a block of statement is executed otherwise not.
    Syntax:
if(condition) 
{
   // Statements to execute if
   // condition is true
}

Here, condition after evaluation will be either true or false. if statement accepts boolean values – if the value is true then it will execute the block of statements under it.
If we do not provide the curly braces ‘{‘ and ‘}’ after if( condition ) then by default if statement will consider the immediate one statement to be inside its block. For example,

if(condition)
   statement1;
   statement2;

// Here if the condition is true, if block 
// will consider only statement1 to be inside 
// its block.

Flow chart:

Example:

<script type = "text/javaScript"> 

// JavaScript program to illustrate If statement 

var i = 10; 

if (i > 15) 
document.write("10 is less than 15"); 

// This statement will be executed 
// as if considers one statement by default 
document.write("I am Not in if"); 

< /script> 

Output:

I am Not in if
  • if-else: The if statement alone tells us that if a condition is true it will execute a block of statements and if the condition is false it won’t. But what if we want to do something else if the condition is false. Here comes the else statement. We can use the else statement with if statement to execute a block of code when the condition is false.
    Syntax:
if (condition)
{
    // Executes this block if
    // condition is true
}
else
{
    // Executes this block if
    // condition is false
}


Example:

<script type = "text/javaScript"> 

// JavaScript program to illustrate If-else statement 

var i = 10; 

if (i < 15) 
document.write("10 is less than 15"); 
else
document.write("I am Not in if"); 

< /script> 

Output:

i is smaller than 15
  • nested-if A nested if is an if statement that is the target of another if or else. Nested if statements means an if statement inside an if statement. Yes, JavaScript allows us to nest if statements within if statements. i.e, we can place an if statement inside another if statement.
    Syntax:
if (condition1) 
{
   // Executes when condition1 is true
   if (condition2) 
   {
      // Executes when condition2 is true
   }
}

Example:

<script type = "text/javaScript"> 

// JavaScript program to illustrate nested-if statement 

var i = 10; 

if (i == 10) { 

// First if statement 
if (i < 15) 
	document.write("i is smaller than 15"); 

// Nested - if statement 
// Will only be executed if statement above 
// it is true 
if (i < 12) 
	document.write("i is smaller than 12 too"); 
else
	document.write("i is greater than 15"); 
} 
< /script> 

Output:

i is smaller than 15
i is smaller than 12 too
  • if-else-if ladder Here, a user can decide among multiple options.The if statements are executed from the top down. As soon as one of the conditions controlling the if is true, the statement associated with that if is executed, and the rest of the ladder is bypassed. If none of the conditions is true, then the final else statement will be executed.
if (condition)
    statement;
else if (condition)
    statement;
.
.
else
    statement;


Example:

<script type = "text/javaScript"> 
// JavaScript program to illustrate nested-if statement 

var i = 20; 

if (i == 10) 
document.wrte("i is 10"); 
else if (i == 15) 
document.wrte("i is 15"); 
else if (i == 20) 
document.wrte("i is 20"); 
else
document.wrte("i is not present"); 
< /script> 

Output:

i is 20