Accessing a child component’s HTML element(s) in Vue 3

In Vue 2 accessing using a template ref to access a components element was pretty intuitive, you could just access this.$refs.myComponent.$el but in Vue 3 the concept of Template Refs has been merged with any other Ref that has been exposed and the $el property been removed from the API.

What happened to $el

Vue 2 required that all templates have a single root element

<template>
<!-- This is $el -->
<div> 
  ...
</div>
</template>

Vue 3 removed the requirement that templates have a single root element, $el semantically no longer makes sense if you’re returning multiple root elements.

<template>
<!-- Which is $el ? -->
<div>...</div>
<div>...</div>
<div>...</div>
</template>

Of course they could have changed the semantics to $els and returned a list, but with with the merging of Template Refs and Ref it presented an opportunity to simplify the API at the cost of some confusion of those used to Vue 2.

A slight gotcha…

Knowing what that Template Refs are just regular Refs one might deduce that we can access the html of a component with the following

<script setup lang="ts">
import MyComponent from "./MyComponent.vue";
import { onMounted, ref } from "vue";

const childRef = ref<typeof MyComponent>();
const html = ref('');

onMounted(() => {
  html.value = childRef.value;
});
</script>
<template>
<my-component ref="childRef"></my-component>
<pre>{{ html }}</pre>
</template>

However, this won’t work because childRef is the Vue component instance, not its element and we can’t use $el for the reasons discussed above.

Exposing your internals

In order to access a Component instance’s internals one needs to expose them as they are closed by default.


Components using <script setup> are closed by default – i.e. the public instance of the component, which is retrieved via template refs or $parent chains, will not expose any of the bindings declared inside <script setup>.

https://vuejs.org/api/sfc-script-setup.html#defineexpose

Exposing the internals in <script setup> is pretty easy, like defineProps and defineEmits there is a compiler macro by the name defineExpose. In your child component you need to define Template Refs on each element you wish to expose.

<script setup lang="ts">
import { ref } from "vue";

const el = ref<HTMLDivElement>()

defineExpose({ el });
</script>
<template>
<div ref="el">
  ...
</div>
</template>

Note: If you’re using Composition API without <script setup> you can access the expose method through the context:

defineComponent(props, context) {
  const el = ref<HTMLDivElement>();
  context.expose({ el });
}

Now you can access this Template Ref in your parent component directly.

<script setup lang="ts">
import MyComponent from "./MyComponent.vue";
import { onMounted, ref } from "vue";

const myComponent = ref<typeof MyComponent>();
const html = ref('');

onMounted(() => {
  html.value = myComponent.value.el;
});
</script>
<template>
<my-component ref="myComponent"></my-component>
<pre>{{ html }}</pre>
</template>

Note: One might be tempted to write myComponent.value.el.value since it’s a Ref after all… right? Well Yes but actually no. Refs are unwrapped when exposed.


When a parent gets an instance of this component via template refs, the retrieved instance will be of the shape { a: number, b: number } (refs are automatically unwrapped just like on normal instances).

https://vuejs.org/api/sfc-script-setup.html#defineexpose

Accessing multiple elements

As I stated above all you need to do is add multiple Refs to your child component and expose them

<template>
<div ref="el1">...</div>
<!-- not exposed -->
<div>...</div>
<div ref="el2">...</div>
</template>
<script setup lang="ts">
import { ref } from "vue";

const el1 = ref<HTMLDivElement>()
const el2 = ref<HTMLDivElement>()

defineExpose({ el1, el2 });
</script>

Final words

While all of this behaviour is documented but it requires knowing what to look for or having read the relevant sections of the VueJS site and connecting the fact that Template Refs have been merged with standards Refs, components can have multiple root elements, and that Component instances require exposing their internals to parent components.

It took me a fair bit to piece it all together and I hope this saves you some time. Cheers.

YouTube’s Category chips suck


In the before times YouTube used to have Netflix style categories for videos, when they switched to Polymer they made a design choice to change these neatly divided categories into tabs (or chips as they call them) where above the videos you have all the categories listed out from “All” to “Recently Uploaded”, “Watched” and “New to you”. In between those categories are all the things the all mighty algorithm has decided you like.

One of my favorite categories is “Recently Uploaded” because it is a mix of videos from channels I subscribe to as well as videos similar to those I subscribe to and they are not usually more than a couple days old. I watch a lot of gaming videos which are time sensitive due to patches that change the “meta” (play style and mechanics) so watching a video from 6 years ago isn’t what I usually want. It’s not just games though, I have a lot of channels on bleeding edge tech and also programming channels which I want to stay up to date on.

For this reason it was very annoying to me that YouTube tucked this chip all the way at the back of the line, to the point I needed to scroll the chips to find it. They also have a styling bug that sometimes the scrolling doesn’t show last chip all the way. It got so annoying I found workaround like disabling the polymer view to get the old style. But, the thing is, other than the chips I really liked the new “infinite” vertical scroll and larger, more condensed, thumbnails.

I finally had enough and wrote a user script to solve my problem. A user script is kind of like a browser extension except it is somewhat more limited in scope and functionality and it doesn’t require publishing it to the Chrome/Firefox store, it can be shared, copied and edited easily. I did publish it to Greasy Fork and can be found here: Reposition YouTube Recently Uploaded Chip. I’ve also published it as a gist on Github and can be found here.

Cancelling Transunion Canada Credit Monitoring / ID Protection Program

I think it should be law in Canada that cancelling a membership to something should be as easy as signing up. Trying to find out how to cancel your Transunion Canada Credit Monitoring or Identity theft monitoring is practically impossible, they’ve hired a lot of SEO to make sure anything you search Google floods your results with irrelevant links.

After filtering out their domains the resources are still few and far between. I found one on Quora for the US site but not Canada. After adjusting my search terms to look for Credit Monitoring rather than ID protection program (which is what they call it in the emails) I came across a 2 year old Reddit post that gives you a direct link to the page in question. Apparently the cancel link is buried somewhere in the FAQ but heck if I could find it.

And it’s this easy (courtesy of an anonymous redditor /u/myusername121) :

  1. Log into your Transunion Canada Credit Monitoring account as you normally would to get the dashboard
  2. Paste & go to this URL into your address bar: https://members.transunion.ca/tucan_en/cancelMembership.page
  3. Click the “Cancel Product” button
  4. That’s it! You should now receive a confirmation email stating your monitoring has been cancel.

Significance of the underscore parameter in arrow functions

Reading code I noticed some devs have been using an underscore, _ => {...}, in their arrow function rather than empty parentheses, () => {...}. At first I thought this was further syntactic sugar of the ES2015+ specifications that I had missed, some convenience keyword.

But I just realized that people have just been using (abusing?) the single parameter with no parenthesis syntax. Basically passing a variable called _ to the function and never using it as a means of not having to type two characters thus “saving” time.

To be clear, these are all the same:

a => {...}
foo => {...}
ಠ_ಠ => {...}
_ => {...}

I’m not sure how I feel about it, the jury is out. On one hand it does save typing a character, on the other it feels… wrong.

My personal hell

I think it’s going to be driving a large truck towing a trailer in rush hour traffic making left hand turns at uncontrolled intersections going from one crowded mall to another where I have to cold call someone to sell them something they don’t need.

New design

I thought it was time to update the design of the website. I clearly don’t have the design chops to do it.

So I am now using WordPress and some stock photos from my old employer iStockphoto Istock by Getty Images. I found some beautiful photos of Calgary and Alberta, my home.

I struggled with whether I should enable my blog portion. As I really don’t think I have much to talk about and am not sure there are people that would be interested in hearing what I had to say. But I suppose I’ll give it a shot.

I recently picked up a new hobby, wood working, mostly power tools and whatnot for now. But perhaps I’ll use this as a platform for showing my works.

I may also talk about some programming and what we’re doing at Arterys.

More to come, probably.