0
votes

Let's say I have a SearchForm Component that has a Reset button, as well as a slot to include any desired SearchField Components. When I click SearchForm's Reset button, I'd like to call each SearchField's reset method, but I'm having a hard time understanding how to do this dynamically... I obviously don't want to add refs to each SearchField because these aren't static and can change when using the SearchForm in some other part of the application. Fiddle for example.

In Vue2, it seemed liked there was some sort of $children property, but that was taken out in Vue3. I was thinking I could potentially use querySelectorAll to access all "input" elements, but I didn't see how I could access the DOM element's component instance (similar to jQuery's $ selector). If I access the $slots.default() and loop over it, I get some weird object that isn't a component instance... or rather, it doesn't have the typical properties that the component instance has, and I have no clue how to access the actual instance from here.

It's also possible I'm not thinking in a Vue-centric way, as I'm new to the framework, so how can I solve this issue?

3

3 Answers

0
votes

I've come up with this solution, but I don't like it, as it adds some minor coupling. Basically, I listen for when the field is created, check its parent, and if it's a form, I push it onto the array of children. Then when the parent's reset is called, it loops through its children. This is a fragile approach because it requires the direct parent to be the form... if the field was nested inside of another component, it won't be added to the form's fields. I'm also pretty sure this breaks the best practices of the framework. It's a shame there doesn't appear to be a way of accessing child instances (without being forced to use ref)... that seems like it'd be desired by a lot of devs.

0
votes

I came up with yet another way, but once again, seems a little shady because I'm accessing the DOM element's private property __vueParentComponent. I like it better than the previous answer because it's not coupled, and I can use getElementsByTagName. Fiddle for reference. This is the relevant code that I added as a method in SearchForm:

getFields() {
  const fields = this.$el.getElementsByTagName("input");
  // getElementsByTagName returns an HTMLCollection, which doesn't have map,
  // so let's use spread to make an array and use map
  return [...fields].map((fieldEl) => {
    return fieldEl.__vueParentComponent.proxy;
  });
}
0
votes

Last solution... this one seems to be more stable and the proper Vue way. You use provide/inject; the parent provides the value, and the child injects it, so it can use it. Vuetify does something similar, but they have their own register and unregister methods, which I've created in the Fiddle but as a rudimentary implementation. The only caveat being if you're using TypeScript, the inject won't work properly, and you'll have to use one of these solutions.