8

I need a reliable way to call the a parent component of n parents in vuejs. Looking for something more like closest('element') in jQuery where you don't have to specify how many parents or children there are, you only give it the name of the component you need and it will look it up for you recursivly untill it finds the right one.

I just wonder if there is something like: this.$find(component-name); or this.$closest(component-name);

I have read somewhere people are using $ref, but I don't think that's the right one for this use-case or is it ?

Instead of this.$parent.$parent which is obviously not very reliable, when you don't know how many parents there are.

Thanks,

2
  • 2
    This'll not be a very satisfying answer, but you probably ought to use either an event bus or a shared data store instead. Commented Oct 8, 2018 at 18:52
  • @DanielBeck thanks for the resources! I will take a look :) Commented Oct 8, 2018 at 19:01

1 Answer 1

16

You could use a while loop, iterating over all the parent components.

getComponent(componentName) {
  let component = null
  let parent = this.$parent
  while (parent && !component) {
    if (parent.$options.name === componentName) {
      component = parent
    }
    parent = parent.$parent
  }
  return component
},

Example:

mounted() {
  console.log(this.getComponent('App'))
},

Utility function (TS):

import Vue from 'vue'

/**
 * @name findParentComponentByName
 * @summary Find the Vue instance of the first parent component that matches the provided component name.
 *
 * @description The `findParentComponentByName()` method returns the Vue instance of the first parent component
 * that has a `name` component option that matches the provided component name.
 *
 * @param {Vue} vm - The children component Vue instance that is looking for the parent component Vue instance
 * @param {string} componentName - The parent component name
 * @returns {Vue|undefined} The parent component instance that matches the provided component name,
 * otherwise, undefined is returned
 *
 * @example
 * // Find `<App/>` component from `<Child/>`:
 * <App>
 *   <GrandParent>
 *     <Parent>
 *       <Child/>
 *     </Parent>
 *   </GrandParent>
 * </App>
 *
 * // Descendant component Vue instance
 * new Vue({
 *   name: 'Child',
 *
 *   created() {
 *     const app = findParentComponentByName(this, 'App')
 *     // => VueComponent {_uid: 1, _isVue: true, $options: {…}, _renderProxy: Proxy, _self: VueComponent, …}
 *  },
 * })
 */
export function findParentComponentByName(
  vm: Vue,
  componentName: string
): Vue | undefined {
  //
  //  Components tree:
  //  +---------------------+  \ return undefined, , <Child/> is not a descendant of <App/> \
  //  | <App>               |---> Return if component name option matches, otherwise
  //  |---------------------|    \ continue traversing the tree upwards \
  //  |   <GrandParent>     |-----> Return if component name option matches, otherwise
  //  |---------------------|      \ continue traversing the tree upwards \
  //  |     <Parent>        |-------> Return if component name option matches, otherwise
  //  |---------------------|        \ traverse the tree upwards \
  //  |       <Child/>      |---------> STARTING POINT, start looking for App component from here
  //  |---------------------|
  //  |     </Parent>       |
  //  |---------------------|
  //  |   </GrandParent>    |
  //  |---------------------|
  //  | </App>              |
  //  +---------------------+
  //

  let component: Vue | undefined
  let parent = vm.$parent

  while (parent && !component) {
    if (parent.$options.name === componentName) {
      component = parent
    }
    parent = parent.$parent
  }

  return component
}

Example:

// In consuming component
import { findParentComponentByName } from '@/utils'

// ...

mounted() {
  console.log(findParentComponentByName(this, 'App'))
},
Sign up to request clarification or add additional context in comments.

5 Comments

thanks!! you are awesome I was just about implementing it on the prototype of vue and you saved me time :) thanks again!
You're welcome! What's your use case though? Maybe this is not an ideal solution.
I'm publishing an NPM package for datatables and it was important for me that the structure of the components will remain has expected.
Alright, this works then! Good luck with that package 😃.
how about adding a counter to condition and ++ it so this loop ends somewhere.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.