Samwise the coder. Defender of Frodo and defender of DRY!

Samwise the Coder: saving the Ring and preventing redundant code since the Third Age!”

Alright, in all seriousness I wanted to actually continue the Lord of the Rings theme throughout the blog post, but I figured it would diverge from the meat and “po-tay-toes” of the post. (last one I swear)

Essentially in Vue, you will learn about the concepts of reusable components. A simple Don’t Repeat Yourself philosophy for sure. They allow you to write code once, and then place that whole component within other areas in your app with a single custom formed tag. Usually this will contain three sections. template, script, and style.

What if we’re looking to just add in some additional logic that we can just reuse regardless of needing a template or not? Well in Vue 2 mixins were a thing sometimes used to do this, but in Vue 3 with composition API mixins have been more so phased out in favour for composables.

If you go to the Vue documentation website, you’ll see a very simple concept that leverages JavaScript to showcase how composables can be used, but of course since Vue promotes TypeScript, we’ll build our examples in TypeScript!

Here’s a very simple component App.vue file we’ll use for the example:

App.vue

<script setup lang="ts">
import ButtonOne from './ButtonOne.vue';
import ButtonTwo from './ButtonTwo.vue';
import { clickListener } from './clickCapture.ts';

const listClicks = clickListener();

</script>

<template>
  <div class="container" style="">
    <ButtonOne class="button" />
    <ButtonTwo class="button"/>
    <button id="ButtonThree" class="button">Button Three</button>
    <p>Click anywhere to begin!</p>
  </div>
  <div>
    <p v-if="listClicks.length > 0"></p>
  </div>
</template>

<style scoped>
  .container {
    width: 600px; 
    background-color: #ccc;
    display: flex;
    align-items: center;
    justify-content: center;
  }
  .button {
    margin: 10px;
  }
</style>

Our boring button component (you’ll have to copy and paste it to make the second button.)

<script setup></script>

<template>
  <div>
    <button id="buttonOne">Button One</button>
  </div>
</template>

And then our reusable composables clickCapture.ts

import { Ref, ref, onMounted, onUnmounted } from 'vue';

export function clickListener(): Ref<string[]> {
  const clickHistory = ref<string[]>([]);

  function clickCapture(event: MouseEvent): void {
    clickHistory.value.push(
      event.target instanceof Element
        ? event.target.id || event.target.tagName
        : ''
    );
  }

  onMounted(() => {
    window.addEventListener('click', clickCapture);
  });

  onUnmounted(() => {
    window.removeEventListener('click', clickCapture);
  });

  return clickHistory;
}

Once you get these files up and running you should see something like this:

To test our code we can begin by clicking on any parts of the example. You’ll see the element clicks get captured in a <p> tag below.

So in our clickCapture.ts file we have the following moving parts:

A Ref is a reactive generic type in Vue.js. Here, we use ref<string[]>([]) to create a reactive reference to an empty array that will store our click history.

The clickCapture function is called on every click event, identifying the clicked element and adding it to our click history.

we use onMounted and onUnmounted hooks. On component mount, we add an event listener to the window to capture clicks. On component unmount, we ensure the event listener is removed to avoid any potential chances of a crash due to memory leak.

I am sure you have a couple of questions. Like for instance. is there a reason why const listClicks = clickListener(); isn’t within the onBeforeMount?

const listClicks = clickListener(); is placed outside of the onBeforeMount hook to ensure that listClicks is created once when the component is initialized and is available throughout the component’s lifecycle. If we moved it inside the onBeforeMount hook, it would mean that listClicks is created every time before the component is mounted, and it will not persist between re-renders or updates.

Or perhaps you’re wondering is there a reason why const listClicks = clickListener(); doesn’t have to be within a ref()?

const listClicks = clickListener(); doesn’t need to be wrapped in a ref(). The clickListener function itself already returns a Ref<string[]>. In our clickListener function, we use ref() to create a reactive reference (clickHistory) and return it. Therefore, when we call clickListener(), we are directly getting a reference to the reactive data (clickHistory) without the need for an additional ref() wrapper.

I understand I’ve been doing a slew of explaining and certainly even more typing. So, what I have done is create a vue sandbox with the code involved above so you can test this code right away and see if piques your interest further!

Vue clickCapture Sandbox

As always, I am open to any feedback. If there is something I can do to better explain the code or other ways to refactor my logic to optimize further, by all means please reach out!

See you in the next one!

Gripes go up, s#!t rolls down!

Along the way since I have been learning Vue I have been dealing with the concepts of emits and props. These are definitely new to me and it's something that I have been trying to actively apply in my code to allow me to have two-way communication between components. My brain works in weird ways, but the correlation between how Vue operates and how this scene illistrates flow of hierarchy. At least you can feel good knowing this was wrote by a human with a very, very weird thought process.

Big O Notation

So a while back I was asked if I knew what "Big O notation" was, or pretty much any real sorting algorithms. I guess this is where my lack of degree in Computer Science kind of starts to reel it's head. I don't have a degree in computer science and I feel like this would have been one of those things that would have been discussed; alongside C++ which I kind of cringe at. Not cause of the language, more so for the fact that I would probably end up cooking a computer from memory mismanagement.

To-do ta-da!

I know it's been a couple of days since my last post. I have not given up on my challenge at all. In fact, (here is a link to my react to-do app)[https://github.com/jaalders/react-to-do]. It's definitely nothing crazy and I certainly have a bit to learn about states and whatnot, but understanding the little things like what the `...` operator does in advance is a big help. Feel free to fork my repo and give it a whirl!

All aboard the React train!

I finally got around to making a GitHub Pages blog. It's been an interesting time thus far because, I know things for me aren't always immediately intuitive and sometimes there is a bit of a grind to make things work. I often would joke with colleagues and say that for every day of work they put in, I would have to put in two. Just to have the retention they would overall.

Just let them win already!

I finally got around to making a GitHub Pages blog. It's been an interesting time thus far because, I know things for me aren't always immediately intuitive and sometimes there is a bit of a grind to make things work. I often would joke with colleagues and say that for every day of work they put in, I would have to put in two. Just to have the retention they would overall.

One step forward, and one step backward...

So Ionos decided to have issues yesterday right after I had gotten my CNAME and A records working as intended, but since their service decided to go sideways, it's made things really harder to deduce. Two phone calls later. Seems you could not assign a SSL certificate for a while and as a result the site's connection was not HTTPS based, so it errored out.

GitHub Pages, Ruby, chatGPT, and a challenge!

I finally got around to making a GitHub Pages blog. It's been an interesting time thus far because, I know things for me aren't always immediately intuitive and sometimes there is a bit of a grind to make things work. I often would joke with colleagues and say that for every day of work they put in, I would have to put in two. Just to have the retention they would overall.