Home > Software engineering >  Apply CSS conditionally in a loop - VueJS
Apply CSS conditionally in a loop - VueJS

Time:05-27

I have a JSON list of items that I import in my vue component, I loop though that file in order to show them. Each item belong to a specific 'group' :

See IMG

E.g. :

{
"type": "Simple list",
"title": "Simple list",
"id": 1,
"group": "list-component",
"properties": "lorem lipsum"
},

I would like to apply a CSS 'border-top-color' to each item according to its group.

I was trying to apply the conditions when mouted(){} but I'm not sure if I'm doing it right. Here's my atempt :

The template (I'm using VueDraggable, don't mind it) :

          <div  :key="element" :style="[{ 'border-top-color': 'brdrTpClr' }]">
            {{ element.title }}
            <div  :key="index">
              <i @click="$emit('pushNewElt', element.id)"></i>
            </div>
          </div>

The script :

data() {
    return {
      dragItems: dragItemsList,
      brdrTpClr: "",
    };
  },
  mounted() {
    for (let i = 0; i <= 15; i  ) {
      if (this.dragItems[i].group == "list-component") {
         // I'm not sure how to do it
        // the color I want to apply : #00A3A1b
      } else if (this.dragItems[i].group == "location-media-component") {
        // #005EB8
      } else if (this.dragItems[i].group == "container-component") {
        // #0091DA
      } else if (this.dragItems[i].group == "UI-component") {
        // #6D2077
      } else if (this.dragItems[i].group == "reader-scanner-component") {
        // #470A68
      }
    }
  },

I'm using i<=15 instead of i<=this.dragItems.length because of a bug, don't mind it too.

CodePudding user response:

You can map colors and use method to apply them:

const app = Vue.createApp({
  data() {
    return {
      dragItems: [{"type": "Simple list", "title": "Simple list", "id": 1, "group": "list-component", "properties": "lorem lipsum"}, {"type": "other", "title": "other", "id": 2, "group": "UI-component", "properties": "lorem lipsum"}, {"type": "other", "title": "other", "id": 3, "group": "container-component", "properties": "lorem lipsum"}],
      mapColors: [{id: 'list-component', color: '#00A3A1'}, {id: 'location-media-component', color: '#005EB8'}, {id: 'container-component', color: '#0091DA'}, {id: 'UI-component', color: '#6D2077'}, {id: 'reader-scanner-component', color: '#470A68'}]
    };
  },
  methods: {
    brdrTpClr(group) {
      return this.mapColors.find(c => c.id === group).color
    }
  },

})
app.mount('#demo')
.item {
  border: 2px solid transparent;
}
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
<div id="demo">
  <div v-for="element in dragItems">
    <div  :key="element" :style="`border-top-color: ${brdrTpClr(element.group)}`">
      {{ element.title }}
      <div  :key="index">
        <i  @click="$emit('pushNewElt', element.id)"></i>
      </div>
    </div>
  </div>
</div>

CodePudding user response:

I would make a method called getBorderColor(item) which returns the colour based on the group, and then dynamically bind it using Vue.

<div 
   
  :style="[{ 'border-top-color': getBorderColor(element) }]"
>
  {{ element.title }}
  // Add icon etc.
</div>
getBorderColor(element) {
  // Can use a switch statement, but here's a simple ternary if/else as an example
  return element.group === "list-component" ? `#00A3A1b`
  : element.group === "location-media-component" ? `#005EB8`
  : element.group === "container-component" ? `#0091DA`
  : element.group === "UI-component" ? `#6D2077`
  : element.group === "reader-scanner-component" ? `#470A68`
  : `#000000`; // Default colour
}

or for a cleaner option you can have an object with your groups as keys and colours as values in data e.g.

return {
  dragItems: dragItemsList,
  brdrTpClr: "",
  colors: {
    "list-component": `#00A3A1b`,
    "location-media-component": `#005EB8`,
    // etc.
  },
};
getBorderColor(element) {
  return this.colors[element.group] || `#000`;
}

CodePudding user response:

Probably the most efficient (performance wise) and the most readable solution would be to declare a constant colorMap, outside the component, and then return the correct value or a fallback, using a method:

<script>
const colorMap = {
  "list-component": '#00A3A1b',
  "location-media-component": '#005EB8',
  "container-component": '#0091DA',
  "UI-component": '#6D2077',
  "reader-scanner-component": '#470A68'
}
export default { 
  //...
  methods: {
    borderColor(group) {
      return colorMap[group] || '#000'
    }
  }
}
</script>
<template>
  ...
  <div :style="{borderColor: borderColor(element.group)}">
    content...
  </div>
</template>

As a general rule, you want to take anything more complicated than a simple ternary outside of the template and provide it via either computed or methods.

Side note: the above method can also be written as computed:

   computed: {
     borderColor: group => colorMap[group] || '#000'
   }

If you find yourself needing the colorMap in more than one component, export it from a constants.(js|ts) file and import everywhere needed. I typically name that file helpers, as it typically also contains static functions or maps (anything I reuse across multiple components/modules).


Important: you're currently passing an array to :style. You should be passing an object.

  • Related