Access to child element in tagless component

When using tagless components (tagName = ""), the element property is (understandably) null. However I’d like to access some child node, which is usually done through this.element.querySelector(...). Is there a similar API available for tagless components?

import Component from '@ember/component';
import { action } from "@ember/object";
export default class extends Component {
    tagName = "";
    @action actionName() {
        this.element.querySelector(".class")

        // this.element is null, how to get child node here?
    }
}

You have a couple options here.

The first is to set an id explicitly on the root element of your component, using guidFor:

import Component from '@ember/component';
import  { guidFor } from '@ember/object';
import  { action } from '@ember/object/internals';

export default class MyComponent extends Component {
  id = guidFor(this);

  @action actionName() {
    const el = document.querySelector(`#${this.id} .class`);
  }
}
<div ...attributes id={{this.id}}>
  {{! the rest of the template... }}
</div>

Depending on what you’re doing, though, the second option may be better: write a custom modifier using ember-modifier or possibly @ember/render-modifiers—though it’s easy to reach too quickly for the render modifiers because they feel familiar and a custom modifier is often a better solution.

My rule of thumb is that if I’m doing something which requires access to the element in Octane, I should use a modifier; if what I’m doing does not require access to the element, I should not use a modifier. It’s also often possible to refactor to just using one-way data flow and a series of getters to derive the state required to set something on the DOM, rather than doing it directly via the Element APIs, so the fact that you were doing it via DOM APIs doesn’t mean that’s how you should do it going forward.

With more details on what you’re actually doing with the element, I might be able to suggest how I would solve the specific use case.

3 Likes

Thank you for your reply. What I’m trying to do is create a tag input component. For example something like the tag input on StackOverflow:

What you see here is something that looks like an input field, but in fact there’s an input field surrounded by two labels:

[label: ember.js] [input: ember-cli|] [label:ember-data]

The input field needs to be resized based on the content. The technique I’m using here is a hidden label that mirrors the input’s value and the hidden label’s width is set on the input:

  1. onInput on the input updates the hidden label’s text
  2. determine the hidden label’s width
  3. set the width of the input field

In step 2 I need access to the hidden label’s element. I’m currently doing this:

this.element.querySelector("label.hidden").innerWidth

This is a perfect use case for a modifier. (In fact, a colleague and I were implementing something very much like this over the past week, as a custom modifier!)

Thanks, that looks exactly like what I need! Case in point, the RFC discussing modifiers is explicitly listing this use-case as an example: https://emberjs.github.io/rfcs/0415-render-element-modifiers.html#tagless-components