Search code examples
javascriptvue.jsswitch-statementtailwind-css

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


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.


Solution

  • 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.