9

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/[email protected]/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

1
  • 1
    Try to reduce the number of function calls in your template. The function results are not cached, meaning that pretty much every change in data causes a full re-render. Using a computed, by creating an array of pre-computed objects can greatly improve your apps performance. See @Boussadjra's updated (2nd) example Commented Nov 20, 2018 at 16:31

2 Answers 2

8

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/[email protected]/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/[email protected]/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>

Sign up to request clarification or add additional context in comments.

4 Comments

Seems that if having to loop through each name in names in a Computed property, it would be easier to set up a method.
i added a second solution using computed property
methods does not cache the result so it will be re-run every time the component or page render
@TomasLucena thank you for your comment, I changed it to computed property that returns a function with name as parameter. At those early days I didn't know about this tip
4

You can't use the 'computed' with a parameter. Most probably you want to use a method:

example

<span>{{ fullName('Hi') }}</span>

methods: {
  fullName(param) {
      return `${this.param} ${this.firstName} ${this.lastName}`
  }
}

Comments

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.