2

I'm somewhat new to Vue and I'm trying to figure out how to access computed values from components' computed values. Please check out this Fiddle: https://jsfiddle.net/85ma3rct/

 <script src="https://unpkg.com/vue"></script>
  <div id="app">
    <table>   
      <floor></floor> 
      <floor></floor>
      <floor></floor>
    </table>  
    Largest area: {{ largest_area}}
  </div>

Vue.component('floor', {
  data: function () {
    return {
      width: 20,
      height: 20
    }
  },
  computed: {
    area: function () {
      return this.width * this.height;
    }
  },  
  template: '<tr><td><input type="number" v-model="width"></td>' +
            '<td><input type="number" v-model="height"></td>' +
            '<td>{{ area }}</td>' +
            '</tr>'
})

new Vue({
  el: '#app',
  computed: {
    largest_area: function () {
      // How to get this from component computed values...
      return 0
    }
  }, 
})

How could I get the largest_area computed value by the computed value from within a number of components?

Tony
  • 1,414
  • 3
  • 9
  • 22
Gvdk
  • 35
  • 3
  • 1
    Possible duplicate of [VueJS access child component's data from parent](https://stackoverflow.com/questions/40410332/vuejs-access-child-components-data-from-parent) – hs-dev2 MR Jul 24 '19 at 02:39

3 Answers3

0

You can turn your <floor> components into custom elements that can use v-model. Each <floor> can then emit the computed area to its parent which can collect and compute the maximum.

For example

Vue.component('floor', {
  template: `<tr>
  <td><input type="number" v-model="width" @input="update"></td>
  <td><input type="number" v-model="height" @input="update"></td>
  <td>{{ area }}</td>
  </tr>`,
  data: () => ({ width: 20, height: 20 }),
  computed: {
    area() { return this.width * this.height }
  },
  methods: {
    update() { this.$emit('input', this.area) }
  },
  created() { this.update() } // emit the initial value
})

new Vue({
  el: '#app',
  data: { areas: [0, 0, 0] },
  computed: {
    largest_area () { return Math.max(...this.areas) }
  }
})
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.min.js"></script>
<div id="app">
  <table>
    <floor v-for="(_, n) in areas" v-model="areas[n]" :key="n"></floor>
  </table>
  Largest area: {{ largest_area }}
</div>

Typically, to support v-model, your component would have a value prop. However, since your components initialise their own data, I have omitted that. The only real requirement is that your component emit an input event.

tony19
  • 125,647
  • 18
  • 229
  • 307
Phil
  • 157,677
  • 23
  • 242
  • 245
0

One possible solution is watching for area value changes in child component and emit value to parent

  watch: {
    area: {
      handler() {
         this.$emit('input', this.area)
      },
      immediate: true
    }
  },

then in parent

<table>

  <floor @input="changeVal($event, 0)"></floor> 
  <floor @input="changeVal($event, 1)"></floor>
  <floor @input="changeVal($event, 2)"></floor>

  </table>

new Vue({
  el: '#app',
  data: {
    message: 'Hello Vue.js!',
    areas: [0, 0, 0]
  },
  computed: {
    largest_area: function () {
        return Math.max(...this.areas)
    }
  },
  methods: {
    changeVal(val, index) {
       this.$set(this.areas, index, val)
    }
  }
})

Demo here https://jsfiddle.net/ittus/kuo9crjm/1/

ittus
  • 21,730
  • 5
  • 57
  • 57
0

You can use $refs for this Refer - Refs

Add a ref to your component wherever you import it and use it -

<MyComponent ref="child" />

Then you can access all it's property from your consuming component.

In your consuming component you can access it in $refs property like below

this.$refs.child.{child-property}

Add ref to floor

<table>   
  <floor ref="floor"></floor> 
</table>  

And then refer it

new Vue({
  el: '#app',
  computed: {
    largest_area: function () {
        console.log(this.$refs.floor.area())
        return 0
    }
  }, 
})
tony19
  • 125,647
  • 18
  • 229
  • 307
Satyam Pathak
  • 6,612
  • 3
  • 25
  • 52
  • This violates [one-way data flow](https://vuejs.org/v2/guide/components-props.html#One-Way-Data-Flow) – Phil Jul 24 '19 at 05:12
  • @Phil, `refs` are not reactive anymore which stops `child` from mutating `parent` state accidentally. I am not sure if I missed a point here but this is the recommended way from official docs as per OP requirement - https://vuejs.org/v2/guide/components-edge-cases.html#Accessing-Child-Component-Instances-amp-Child-Elements – Satyam Pathak Jul 24 '19 at 05:27
  • Calling methods is very different to reaching into refs and accessing their data and computed properties. That is not recommended – Phil Jul 24 '19 at 05:31