1

I am in the process of converting a Vue2 project into Vue3 and Typscript. There are plenty of bizzare errors, but I am not sure how to handle this one on $el.

I am just looking to grab every <g> tag in the template above and I can, but this error persists.

Any ideas on how to fix this?

Code sample with error

Here is the template of the component, but the entire component is too big to put on stackoverflow. I need to target the <g> tag of.

<template>
  <div class="pep-header-decoration-light">
    <svg
      xmlns="http://www.w3.org/2000/svg"
      width="1158.942"
      height="143.376"
      viewBox="0 0 1158.942 143.376"
    >
      <g :style="`color: ${colors.green}`">
        <!-- ... -->
      </g>
      <!-- many more g elements to select here too -->
    </svg>
  </div>
</template>
Jeff Bowman
  • 90,959
  • 16
  • 217
  • 251
LovelyAndy
  • 841
  • 8
  • 22
  • 1
    Suggesting as a dupe of [this](https://stackoverflow.com/q/61600078/1426891), which talks about the deliberate lack of `$el`. Be careful about your use of `this`, too: Arrow functions don't get their own `this`, and the `this` in `setup` is `null` as listed [in the tip here](https://vuejs.org/api/composition-api-setup.html#basic-usage). – Jeff Bowman Mar 06 '22 at 16:28
  • 1
    Does this answer your question? [Vue 3 Composition API - How to get the component element ($el) on which component is mounted](https://stackoverflow.com/questions/61600078/vue-3-composition-api-how-to-get-the-component-element-el-on-which-componen) – Jeff Bowman Mar 06 '22 at 16:29
  • @JeffBowman Thank you for the answer. I did see the use of `ref` instead, but my template needs all tags, which there are a lot of. Do I really need to go on every tag and add a template ref? That seems a bit excessive – LovelyAndy Mar 06 '22 at 16:36
  • 1
    You didn't show us the template, if there's an SVG element you can set `ref` on that and do a `querySelectorAll` or other DOM traversal from there. You just can't rely on either `this` or `$el` specifically in your `setup`. – Jeff Bowman Mar 06 '22 at 16:49
  • @JeffBowman I've added the template, but it's quite huge and I didn't think people needed to see it. So you mean putting the ref on the svg and then targetting from there? so something like this ?`$refs.$el.querySelectorAll('g')` Thanks again for your help so far! – LovelyAndy Mar 06 '22 at 17:00
  • 1
    I've converted most of my feedback to an answer. I'll edit the less-relevant parts out of your question; in the future, please don't upload a screenshot of your code, but rather copy-paste the text so it's easier to read and modify. Cheers! – Jeff Bowman Mar 06 '22 at 17:32

1 Answers1

4

As in the question Vue 3 Composition API - How to get the component element ($el) on which component is mounted, Vue 3 discourages the use of $el, since components can have more than one top-level element. Furthermore, your arrow function won't redefine this, and this is not available in setup:

setup() itself does not have access to the component instance - this will have a value of null inside setup(). You can access Composition-API-exposed values from Options API, but not the other way around.

Given that, you should probably define a ref for either your enclosing div or your enclosing svg, and use that to get to your element:

<template>
  <div class="pep-header-decoration-light" ref="divEl">
    <svg [...]>
      <!-- ... -->
    </svg>
  </div>
</template>

Note that you'll need to return the new ref in the object that setup returns, and that it needs the same name that it has in the template. You can access this later using this.$refs.divEl, but in the setup function you don't have access to this to get to this.$refs.

setup(props) {
  const divEl = ref<HTMLDivElement>(); // create the ref here
  onMounted(() => {
    // no `this` and no `$el`, so use `divEl.value`
    const shapes = divEl.value.querySelectorAll("svg > g");
    // ...
  });
  // ...
  return {divEl}; // exposes the ref named `divEl`, and since it matches
                  // the template, vue will populate it during mount
}
Jeff Bowman
  • 90,959
  • 16
  • 217
  • 251
  • 1
    I don't find DivElement, the correct name should be Element or HTMLDivElement – Jianwu Chen Jul 08 '22 at 19:42
  • That's right, was going from memory. Fixed. – Jeff Bowman Jul 08 '22 at 23:25
  • Generally I completely agree. However, in my case [refs don't play nicely within loops](https://stackoverflow.com/a/71417860/9831274) (or at least not yet). Any suggestions for folks who need to type a Vue component to access $el? – Ryan Aug 26 '22 at 16:39
  • @Ryan I'm not sure I understand your question. You can't get to `$el` from `setup` or the composition API directly, since the render function won't have been called yet. Outside of that [the first link in my answer](https://stackoverflow.com/q/71372032/1426891) has some alternatives, and if there are constraints beyond that you may need to specify them in your own question. – Jeff Bowman Aug 26 '22 at 16:45
  • 2
    Apologies, that was confusing. Totally agree: no `$el`s in the composition API. However, `$el`s can be accessed via computed (or methods) defined in the composition API. All I'm really asking is how to type a Vue component so `someVueComponent.$el` doesn't throw a typescript error. Maybe it is worth writing up a separate question – Ryan Aug 26 '22 at 17:00
  • @Ryan Ah, that helps, but has an unsatisfying answer: `$el` is only typed as a [Node in the docs](https://vuejs.org/api/component-instance.html#el) and an [Element in the declarations](https://github.com/vuejs/vue/blob/ee57d9fd1d51abe245c6c37e6f8f2d45977b929e/types/vue.d.ts#L65), so there are no clever generics that will let you avoid normal means of [casting via `as` ("type assertions")](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#type-assertions) or [narrowing (e.g. type predicates or `typeof` checks)](https://www.typescriptlang.org/docs/handbook/2/narrowing.html). – Jeff Bowman Aug 26 '22 at 18:04
  • @Jeff, thank you so much for your help! That brought a lot of clarity! I ended up [writing up a question](https://stackoverflow.com/questions/73532500/how-to-type-an-arrow-function-parameter-in-a-vue-3-attribute-and-how-to-type-a-v) for a related issue and quoted you. – Ryan Aug 29 '22 at 17:27