WoJ
WoJ

Reputation: 29987

How to get the component a function is called from?

Consider the following fragment in a <template>:

<div v-for="entry in entries">
  <my-component :color="doSomething(entry)">hello</my-component>
</div>

The related method:

doSomething(entry) {
  return entry.color
}

Is there a way to replace the setup above with a computed property without the need to pass (entry) each time? Something like:

<div v-for="entry in entries">
  <my-component :color="doSomething">hello</my-component>
</div>

The related method:

doSomething() {
  // I need to know here that the call is coming from <my-component> 
  // in the context of "entry"
  return ???.color
}

The reason for the question is that I would like to reduce the boilerplate for components that rely on the state of an iterated property:

      <q-editor
          v-for="note in notesByDay[when]"
          @click="noteClicked(note)"
          @blur="noteBlurred(note)"
          :toolbar="SetToolbar(note)"
          (...)

Upvotes: 2

Views: 444

Answers (2)

Majed Badawi
Majed Badawi

Reputation: 28414

I don't think computed-properties are designed to be used as custom functions that you can call with different inputs.

However, to make your code cleaner, I would suggest making a computed-property for your entries list and fill in all the properties you want to add to each item.

Here is a simple demo:

const mycomponent = Vue.component('mycomponent', {
  template: '#mycomponent',
  props: ['color']
});

new Vue({
  el: "#app",
  components: { mycomponent },
  data: () => ({ entries: [ { id:1 }, { id:2 } ] }),
  computed: {
    customEntries() {
      return this.entries.map(e => ({
        ...e,
        color: e.id === 1 ? 'red' : 'blue', // or call a function to compute this
        score: 10 // ...
      }));
    }
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

<template id="mycomponent">
  <p :style="{ color: color }">
    <slot></slot>
  </p>
</template>

<div id="app">
  <div v-for="({color}, index) in customEntries" :key="index">
    <mycomponent :color="color">hello</my-component>
  </div>
</div>

Upvotes: 2

tony19
tony19

Reputation: 138286

Computed props can't receive arguments unless they were computed methods themselves, but that's no better than using a component method as in the original question.

You could instead compute the array with the color field added, using Array.prototype.map on entries[], where you'll have access to the entry field:

export default {
  computed: {
    computedEntries() {
      return this.entries.map(entry => ({
        ...entry,
        color: this.getColor(entry),
      }))
    }
  },
  methods: {
    getColor(entry) {
      return /* determine color based on state, etc. */
    }
  },
}

Then in the template, loop over the computed prop instead of entries:

<template>
  <div v-for="entry in computedEntries">
    <my-component :color="entry.color">hello</my-component>
  </div>
</template>

Upvotes: 3

Related Questions