2
\$\begingroup\$

I have the following vue component which all works fine

<template>
  <div class="sales-agreements">
    <nav class="sales-agreements__navigation">
      <ul class="sales-agreements__navigation-list">
        <li class="sales-agreements__navigation-item">
          <router-link :to="{ path: '/' }" class="sales-agreements__navigation-link">All</router-link>
        </li>
        <li class="sales-agreements__navigation-item">
          <router-link :to="{ path: '/', query: { salesType: 'mortars' } }" class="sales-agreements__navigation-link">Mortars</router-link>
        </li>
        <li class="sales-agreements__navigation-item">
          <router-link :to="{ path: '/', query: { salesType: 'bricks' } }" class="sales-agreements__navigation-link">Bricks</router-link>
        </li>
      </ul>
    </nav>
    <div class="sales-agreements__header">
      <div class="sales-agreements__header-box sales-agreements__header-box--welcome">
        Welcome
      </div>
      <h1 class="sales-agreements__header-box sales-agreements__header-box--title">
        {{ title }}
      </h1>
    </div>
    <div class="sales-agreements__results">
      <h2 class="sales-agreements__results-title">{{ subTitle }} sales agreements</h2>
      <div v-if="salesAgreementItems.length">
        <div v-for="salesAgreementItem in filteredSalesAgreementItems" :key="salesAgreementItem.id" class="sales-agreements__item">
          <div class="sales-agreements__item-details">
            <div class="sales-agreements__item-id"><strong>{{ salesAgreementItem.id }}</strong></div>
            <div class="sales-agreements__item-icon-holder">
              <bricks-icon v-if="salesAgreementItem.department === 'bricks'" :icon-class="'sales-agreements__item-icon'"></bricks-icon>
              <mortars-icon v-if="salesAgreementItem.department === 'mortars'" :icon-class="'sales-agreements__item-icon'"></mortars-icon>
            </div>
            <div class="sales-agreements__item-address">
              <span v-if="salesAgreementItem.deliveryStreet">{{ salesAgreementItem.deliveryStreet }}<br></span>
              <span v-if="salesAgreementItem.deliveryCity">{{ salesAgreementItem.deliveryCity }}<br></span>
              <span v-if="salesAgreementItem.deliveryPostcode">{{ salesAgreementItem.deliveryPostcode }}</span>
            </div>
          </div>
        </div>
      </div>
      <div v-else>
        <p><strong>Sorry, there are currently no sales agreements to view.</strong></p>
      </div>
    </div>
  </div>
</template>

<script>
  import BricksIcon from '../icons/bricks-icon.vue';
  import MortarsIcon from '../icons/mortars-icon.vue';
  import './sales-agreements.scss';

  export default {
    name: 'SalesAgreements',
    components: {
      BricksIcon,
      MortarsIcon,
    },
    props: {
      salesAgreementItems: {
        default() {
          return [];
        },
        type: Array,
      },
    },
    computed: {
      filteredSalesAgreementItems() {
        switch (this.$route.query.salesType) {
          case 'bricks':
            return this.salesAgreementItems.filter(obj => obj.department === 'bricks');

          case 'mortars':
            return this.salesAgreementItems.filter(obj => obj.department === 'mortars');

          default:
            return this.salesAgreementItems;
        }
      },
      subTitle() {
        switch (this.$route.query.salesType) {
          case 'bricks':
            return 'Bricks';

          case 'mortars':
            return 'Mortars';

          default:
            return 'All';
        }
      },
      title() {
        switch (this.$route.query.salesType) {
          case 'bricks':
            return 'Bricks';

          case 'mortars':
            return 'Mortars';

          default:
            return 'Bricks & Mortars';
        }
      },
    },
  };
</script>

But as you can see, the computed properties repeats the same switch - is there a way to change all three properties in one switch statement so if I add further properties or switch cases, I do not have to do it multiple times?

\$\endgroup\$
2
\$\begingroup\$

Computed properties can return objects, which seems useful for you.

computed: {
  filteredItems() {
    switch (this.$route.query.salesType) {
      case 'bricks':
        return {
            filteredSalesAgreementItems: this.salesAgreementItems.filter(obj => obj.department === 'bricks'),
            subTitle: 'Bricks',
            title: 'Bricks'
        }
      case 'mortars':
        return {
          filteredSalesAgreementItems: this.salesAgreementItems.filter(obj => obj.department === 'mortars'),
          subTitle: 'Mortars',
          title: 'Mortars'
        }
      default:
        return {
          filteredSalesAgreementItems: this.salesAgreementItems,
          subTitle: 'All',
          title: 'Bricks & Mortars'
        }
    }
  },

You can then refer to the values using filteredItems.filteredSalesAgreementItems, filteredItems.subTitle and filteredItems.title

\$\endgroup\$
1
  • \$\begingroup\$ Can't believe I didn't think of returning an object! Much better than using the @click Thanks \$\endgroup\$
    – Pete
    Dec 1 '21 at 15:55
0
\$\begingroup\$

One option is to ensure that the sales type is in (or not in) a white list of types using Array.prototype.includes() - for example:

filteredSalesAgreementItems() {
  if (!['bricks', 'mortars'].includes(this.$route.query.salesType)) {
    return this.salesAgreementItems;
  }
  return this.salesAgreementItems.filter(obj => obj.department === this.$route.query.salesType);
}
subTitle() {
  if (!['bricks', 'mortars'].includes(this.$route.query.salesType)) {
    return 'All';
  }
  const type = this.$route.query.salesType;
  return type.charAt(0).toUpperCase() + type.subString(1);
},
title() {
  if (!['bricks', 'mortars'].includes(this.$route.query.salesType)) {
    return 'Bricks & Mortars';
  }
  const type = this.$route.query.salesType;
  return type.charAt(0).toUpperCase() + type.subString(1);
},

The last return values of the latter two computed properties could be stored in a method - e.g. toTitleCase().

if (!String.prototype.toTitleCase) {
  String.prototype.toTitleCase = function() {
    return this.charAt(0).toUpperCase() + this.subString(1);
  }
}

So then that can be used to simplify those last two computed properties - e.g.:

return this.$route.query.salesType.toTitleCase();

And the list of sales types could be declared before the component - e.g.

const SALES_TYPES = Object.freeze(['bricks', 'mortars']);

Then that list can be referenced within the computed properties.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.