How to use dynamic tailwind classes with JS switch statement and pass them correctly in Vue?

Viewed 669

I am a beginner to Vue JS and I'm trying to create a function for assigning corresponding colours to the order statuses. I would like to use switch-statement to achieve this, that would grab the value of order status and pass it to the getStatusColour function(), like this:

const getStatusColour = (orderStatus) => {
    let statusColour = "";
    switch (orderStatus) {
        case "new": 
            statusColour = "bg-green-100 text-green-900";
            break;
        case "preparing": 
            statusColour =  "bg-yellow-400 text-yellow-900";
            break;
        case "ready":
            statusColour = "bg-blue-200 text-blue-800";
            break;
        case "delivered":
            statusColour = "bg-green-300 text-green-800";
            break;
        case "failed": 
            statusColour = "bg-red-400 text-red-900";
            break;
        default:
            statusColour = "bg-gray-100 text-gray-800"
    }
    return statusColour;
}

Then in the Index.vue file I have export default { getStatusColour }, I guess that's should be a mistake here.

And then in the template I call it like this:

<span :class="getStatusColour(order.status)">{{ order.status }}</span>

But I keep getting Uncaught (in promise) TypeError: _ctx.getStatusColour is not a function error. I'll appreciate any help here.

2 Answers

As shown in the MDN docs: https://developer.mozilla.org/en-US/docs/web/javascript/reference/statements/export#using_named_exports


Here is the whole github codebase.

utils/test.js

const getStatusColour = (orderStatus) => {
  let statusColour = ''
  switch (orderStatus) {
    case 'new':
      statusColour = 'bg-green-100 text-green-900'
      break
    case 'preparing':
      statusColour = 'bg-yellow-400 text-yellow-900'
      break
    case 'ready':
      statusColour = 'bg-blue-200 text-blue-800'
      break
    case 'delivered':
      statusColour = 'bg-green-300 text-green-800'
      break
    case 'failed':
      statusColour = 'bg-red-400 text-red-900'
      break
    default:
      statusColour = 'bg-gray-100 text-gray-800'
  }
  return statusColour
}

export { getStatusColour }

App.vue

<template>
  <div>
    <div :class="getStatusColour(order.status)">
      This div do have the correct: `bg-green-100 text-green-900` on it
    </div>
  </div>
</template>

<script>
import { getStatusColour } from './utils/test'

export default {
  data() {
    return {
      order: {
        status: 'new',
      },
    }
  },
  methods: {
    getStatusColour,
  },
}
</script>

Here is a github repo to show that your code is working great so far: https://github.com/kissu/so-compute-exported-function


How I do personally handle this kind of flow

callToAction.vue

<button
  class="flex items-center w-auto p-4 text-center ..."
  :class="[
    callToAction.types[color][variant],
    { 'opacity-50 cursor-not-allowed shadow-none': disabled },
  ]"
>
  Nice flexible button
</button>

<script>
export default {
props: {
  color: {
    type: String,
    default: 'primary',
  },
  variant: {
    type: String,
    default: 'enabled',
  },
  disabled: {
    type: Boolean,
    default: false,
  },
},

data() {
  return {
    callToAction: {
      types: {
        primary: {
          enabled: 'disabled:bg-primary-500 hover:bg-primary-700 bg-primary-500 text-primary-500',
          outlined: 'hover:bg-primary-a12 text-primary-500',
          reversed: 'text-primary-500',
        },
        secondary: {
          enabled: 'disabled:bg-secondary-500 hover:bg-secondary-700 bg-secondary-500 text-secondary-500',
          outlined: 'hover:bg-secondary-a12 text-secondary-500',
          reversed: 'text-secondary-500',
        },
        tertiary: {
          enabled: 'disabled:bg-tertiary-500 hover:bg-tertiary-700 bg-tertiary-500 text-tertiary-500',
          outlined: 'hover:bg-tertiary-a12 text-tertiary-500',
          reversed: 'text-tertiary-500',
        },
        bluegray: {
          enabled: 'disabled:bg-bluegray-500 hover:bg-bluegray-700 bg-bluegray-500 text-bluegray-500',
          outlined: 'hover:bg-bluegray-a12 text-bluegray-500',
          reversed: 'text-bluegray-500',
        },
        error: {
          enabled: 'disabled:bg-error-500 hover:bg-error-700 bg-error-500 text-error-500',
          outlined: 'hover:bg-error-a12 text-error-500',
          reversed: 'text-error-500',
        },
      },
    },
  }
}
</script>

Then, call it in other pages/components with something like:

<call-to-action color="tertiary" variant="reversed"></call-to-action>

Or let the defaults do it's job with color="primary", variant="enabled" and so on...

PS: adding validators to props could be nice, will be more friendly for the other developers working on the project, figuring out all the possible values that can be passed.

The answer of kissu should fix your problem, but I would recomend use computed properties. This way you can write less code and also you can create a default state which handles it when the input of color or variant is invalid.

<button
  class="flex items-center w-auto p-4 text-center ..."
  :class="[
    class_type,
    { 'opacity-50 cursor-not-allowed shadow-none': disabled },
  ]"
>

<script>
export default {
props: {
  color: {
    type: String,
    default: 'primary',
  },
  variant: {
    type: String,
    default: 'enabled',
  },
  disabled: {
    type: Boolean,
    default: false,
  },
},
computed: {
  class_type: function(){
    switch(this.variant)
    case "enabled": 
      return `disabled:bg-${this.color}-500 hover:bg-${this.color}-700 bg-${this.color}-500 text-${this.color}-500`
    case "outlined":
      return `hover:bg-${this.color}-a12 text-${this.color}-500`
    case "reversed": 
      return `text-${this.color}-500`
    default: 
      console.error("you didn't put a proper variant to this component")
  }
}
</script>
Related