3
\$\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 Answers 2

3
\$\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
    Commented Dec 1, 2021 at 15:55
1
\$\begingroup\$

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?

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

A computed property for sales type could be added:

salesType() {
  return this.$route?.query?.salesType || '';
}

Notice it used the optional chaining operator ?. and logical OR || to gracefully fall back to an empty string in the event the query string does not contain that key, though maybe it should be handled differently.

Then that can be used to simplify the other methods. For example:

 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;
   }
 }

Can be simplified to this:

filteredSalesAgreementItems() {
  if (!['bricks', 'mortars'].includes(this.salesType)) {
    return this.salesAgreementItems;
  }
  return this.salesAgreementItems.filter(
    obj => obj.department === this.salesType
  );
}

Similarly for subTitle:

subTitle() {
  if (!['bricks', 'mortars'].includes(this.salesType)) {
    return 'All';
  }
  return this.salesType.charAt(0).toUpperCase() + this.salesType.subString(1);
},

and for title:

title() {
  if (!['bricks', 'mortars'].includes(this.salesType)) {
    return 'Bricks & Mortars';
  }
  return this.salesType.charAt(0).toUpperCase() + this.salesType.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.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 and acknowledge you have read our privacy policy.

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