Search code examples
javascriptvue.jsvuejs2vue-componentcomputed-properties

Vue.js - How to implement Computed properties on objects of array?


I have an array of objects inside my Vue instance, and for each item I'd like to write a Computed property.

Each object has only two properties: firstName and lastName. I would like to write a Computed property for each named 'fullName', which is just a concatenation of firstName and lastName.

I'm familiar with implementing Computed properties of data object properties of a Vue instances, but when it comes to doing so with elements of an array, I get confused.

Currently, my code is this:

var app = new Vue({
  el: '#app',
  data: {
    names: [{
        firstName: 'Mike',
        lastName: 'McDonald',
        done: false
      },
      {
        firstName: 'Alex',
        lastName: 'Nemeth',
        done: false
      },
      {
        firstName: 'Nate',
        lastName: 'Kostansek',
        done: true
      },
      {
        firstName: 'Ivan',
        lastName: 'Wyrsta',
        done: true
      }
    ]
  },
  computed: {
    fullName: function(name) {
      return name.lastName + ', ' + name.firstName;
    }      
  }
  methods: {
    toggle: function(name) {
      name.done = !name.done;
    }
  }
});
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
<div id='app'>
  <ol>
    <li v-for='name in names'>
      <input type='checkbox' v-bind:checked='name.done' v-on:change='toggle(name)' />
      <span v-if='!name.done'>{{ fullName(name) }}</span>
      <del v-else>{{ fullName(name) }}</del>
    </li>
  </ol>
</div>

And here is the respective jsFiddle


Solution

  • It's recommended to return a function with name as argument from the computed property :

    var app = new Vue({
      el: '#app',
      data: {
        names: [{
            firstName: 'Mike',
            lastName: 'McDonald',
            done: false
          },
          {
            firstName: 'Alex',
            lastName: 'Nemeth',
            done: false
          },
          {
            firstName: 'Nate',
            lastName: 'Kostansek',
            done: true
          },
          {
            firstName: 'Ivan',
            lastName: 'Wyrsta',
            done: true
          }
        ]
      },
      computed: {
         fullName(){
          return (name)=>name.lastName + ', ' + name.firstName;
        },
         toggle() {
           return (name)=>name.done = !name.done;
        }
      },
    });
    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
    <div id='app'>
      <ol>
        <li v-for='name in names'>
          <input type='checkbox' v-bind:checked='name.done' v-on:change='toggle(name)' />
          <span v-if='!name.done'>{{ fullName(name) }}</span>
          <del v-else>{{ fullName(name) }}</del>
        </li>
      </ol>
    </div>

    Another solution is to loop through names array inside a computed property by concatenating firstname and lastname, after that return this array and loop through it in your template

    var app = new Vue({
      el: '#app',
      data: {
        names: [{
            firstName: 'Mike',
            lastName: 'McDonald',
            done: false
          },
          {
            firstName: 'Alex',
            lastName: 'Nemeth',
            done: false
          },
          {
            firstName: 'Nate',
            lastName: 'Kostansek',
            done: true
          },
          {
            firstName: 'Ivan',
            lastName: 'Wyrsta',
            done: true
          }
        ]
      },
      computed: {
        fullNames() {
          return this.names.map(name => {
            let fl = {};
            fl.fname = name.firstName + ", " + name.lastName;
            fl.done = name.done;
            return fl;
          })
        }
      },
      methods: {
        fullName: function(name) {
          return name.lastName + ', ' + name.firstName;
        },
        toggle: function(name) {
          name.done = !name.done;
        }
      }
    });
    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
    <div id='app'>
      <ol>
        <li v-for='name in fullNames'>
          <input type='checkbox' v-bind:checked='name.done' v-on:change='toggle(name)' />
          <span v-if='!name.done'>{{ name.fname }}</span>
          <del v-else>{{  name.fname  }}</del>
        </li>
      </ol>
    </div>