4
votes

There is probably a really simple fix to this problem but I cannot seem to get it to work correctly. I am using a Vuetify data table to loop through and show all my properties but it does not seem to work correctly when I use a computed property. I am trying to combine a few props (street, city, state, zip) into a computed address prop. So when I use each prop individually it works fine like this:

<td class="text-xs-center">{{ props.item.street }}</td>
<td class="text-xs-center">{{ props.item.city }}</td>
<td class="text-xs-center">{{ props.item.state }}</td>
<td class="text-xs-center">{{ props.item.zip }}</td>

However, if in the script section of my Vue template, I create a computed property "fullAddress" like:

computed: {
  fullAddress () {
    return this.street & '\n' & this.city & ', ' & this.state & ' ' & this.zip
  }
}

then use that in the template above like:

<td class="text-xs-center">{{ props.item.fullAddress() }}</td>

it does not work at all. I have also tried numerous other ways to write it out but nothing has worked. I am new to Vue and how it uses computed properties so I am sure that I am just not understanding how it works properly.

EDIT: I am using a Vuetify data table to loop through my data. I am using their documentation to display the table. Like I said, I am new to Vue so I am trying to figure all this out. I believe the :items="items" is a v-bind for all the props (similar to a v-for loop?). Here is a more complete example of what Vuetify has for a very simple data table:

<template>
  <v-data-table
    :headers="headers"
    :items="items"
    hide-actions
    class="elevation-1"
  >
    <template slot="items" slot-scope="props">
      <td>{{ props.item.name }}</td>
      <td class="text-xs-right">{{ props.item.street }}</td>
      <td class="text-xs-right">{{ props.item.city }}</td>
      <td class="text-xs-right">{{ props.item.state }}</td>
      <td class="text-xs-right">{{ props.item.zip }}</td>
    </template>
  </v-data-table>
</template>

<script>
  export default {
    data () {
      return {
        headers: [
          {
            text: 'Name',
            sortable: false,
            value: 'name'
          },
          { text: 'Street', value: 'street' },
          { text: 'City', value: 'city' },
          { text: 'State', value: 'state' },
          { text: 'Zip Code', value: 'zip' }
        ],
        items: [
          {
            name: 'Braums',
            street: '159 W Elm St',
            city: 'St. Louis',
            state: 'MO',
            zip: 83607
          },
          {
            name: 'McDonalds',
            street: '237 N Cup Way',
            city: 'Dallas',
            state: 'TX',
            zip: 47621
          }
        ]
      }
    }
  }
</script>
1
A property is not a function. {{ props.item.fullAddress() }} is a function, notice the (). However, you're in a loop, you can't get a single property to work for each of the items you have. Why don't you just use {{ props.item.street }} {{ props.item.city }} {{ props.item.state}}? - N.B.
What is props.item? - Roy J
@RoyJ Sorry, I guess that is relevant information. I have an array of values named items. In the table, I loop through the items in props, hence props.item.value. - Matt Campbell
@N.B. I actually did try that and that does work. I'd simply like to learn how to get the computed properties to work. Rather than having to combine those values each time I need to output the address, I'd like to simply call that computed property. - Matt Campbell
There are a couple of approaches you could take, depending on the data structure and HTML. I'm inferring a v-for in there, but the props.item notation doesn't work with it, so I'm confused. Can you edit the question to give a more complete picture of what you're working with? - Roy J

1 Answers

6
votes

Thank you for expanding your question.

What you can't do: you can't have an array of computeds, because computeds aren't associated with data items, they are associated with the component. The way you proposed writing it, the computed would have to have a different this for each item. But its this is the component.

You can create a computed based on items that returns each value in items augmented with the fullAddress value you compute for that item. Note that you should be careful not to modify your original item; I use Object.assign to create a new copy.

Then you pass the computed array to your v-table instead of passing items.

I just plugged the new variable in in place of street to show that it works.

Vue.use(Vuetify);

new Vue({
  el: '#app',
  data() {
    return {
      headers: [{
          text: 'Name',
          sortable: false,
          value: 'name'
        },
        {
          text: 'Street',
          value: 'street'
        },
        {
          text: 'City',
          value: 'city'
        },
        {
          text: 'State',
          value: 'state'
        },
        {
          text: 'Zip Code',
          value: 'zip'
        }
      ],
      items: [{
          name: 'Braums',
          street: '159 W Elm St',
          city: 'St. Louis',
          state: 'MO',
          zip: 83607
        },
        {
          name: 'McDonalds',
          street: '237 N Cup Way',
          city: 'Dallas',
          state: 'TX',
          zip: 47621
        }
      ]
    }
  },
  computed: {
    itemsWithFullAddress() {
      // This creates a new empty object, copies the item into it,
      // then calculates `fullAddress` and copies that entry into it
      return this.items.map((obj) => Object.assign({}, obj, {
        fullAddress: `${obj.street}\n${obj.city}, ${obj.state} ${obj.zip}`
      }));
    }
  }
});
<link href="//unpkg.com/vuetify/dist/vuetify.min.css" rel="stylesheet" />
<script src="//unpkg.com/vue@latest/dist/vue.js"></script>
<script src="//unpkg.com/vuetify/dist/vuetify.min.js"></script>
<div id="app">
  <v-data-table :headers="headers" :items="itemsWithFullAddress" hide-actions class="elevation-1">
    <template slot="items" slot-scope="props">
      <td>{{ props.item.name }}</td>
      <td class="text-xs-right">{{ props.item.fullAddress }}</td>
      <td class="text-xs-right">{{ props.item.city }}</td>
      <td class="text-xs-right">{{ props.item.state }}</td>
      <td class="text-xs-right">{{ props.item.zip }}</td>
    </template>
  </v-data-table>
</div>