TerryBear
TerryBear

Reputation: 19

How to change text color of td in Vue conditionally

I am consuming data from a json file like this:

{"USD": {"7d": 32053.72, "30d": 33194.68, "24h": 31370.42}, "AUD": {"7d": 43134.11, "30d": 44219.00, "24h": 42701.11}, "RUB": {"7d": 2451451.45, "30d": 2465896.74, "24h": 2398589.80},  "JPY": {"7d": 3537735.55, "30d": 3664620.47, "24h": 3472632.46}, "BRL": {"7d": 167555.18, "30d": 169473.27, "24h": 163054.93}, "ILS": {"7d": 108658.72, "30d": 111663.67, "24h": 106988.58}, "GBP": {"7d": 23257.66, "30d": 23838.55, "24h": 22923.17}, "PLN": {"7d": 124869.61, "30d": 127872.57, "24h": 122668.16}, "CAD": {"7d": 40425.62, "30d": 41444.76, "24h": 39827.13}, "EUR": {"7d": 27187.74, "30d": 27955.81, "24h": 26659.79}}

And I am displaying it in a table. I calculate percentage change for 7 and 30 days and whether that percentage change is negative or positive. If the change is negative I want to color the text red, if it's positive I want to color it green, this is how I check if a change is positive or negative:

 // Checking whether the change is positive or negative
          
          if(item['7d'] > item['30d']) {
            this.isPositive = true
          } else if (item['7d'] < item['30d']) {
           this.isNegative = true
          }

So far my logic using a ternary operator does not work and I don't get my td's text colored red or green. Why and how can I achieve this? Here is my entire code:

<template>
<div>
 <v-data-table
      :headers="headers"
      :items="bitcoinInfo"
      :hide-default-footer="true"
      class="primary"
    >
      <template #item.thirtyDaysDiff="{ item }">
        <td :class="isPositive ? 'positive' : 'negative' ? isNegative : 'negative'">{{ calculateThirtyDayDifference(item) }}%</td>
      </template>
      <template #item.sevenDaysDifference="{ item }">
       <td :class="isPositive ? 'positive' : 'negative' ? isNegative : 'negative'">{{ calculateThirtyDayDifference(item) }}%</td>
      </template>
    </v-data-table>
</div>

</template>

<script>
import axios from 'axios';

  export default {
    data () {
      return {
        bitcoinInfo: [],
        isNegative: false,
        isPositive: false,
        headers: [
          {
            text: 'Currency',
            align: 'start',
            value: 'currency',
          },
          
          { text: '30 Days Ago', value: '30d' },
          { text: '30 Day Diff %', value: 'thirtyDaysDiff'},
          { text: '7 Days Ago', value: '7d' },
          { text: '7 Day Diff %', value: 'sevenDaysDifference' },
          { text: '24 Hours Ago', value: '24h' },
        ],
      }
    },

    methods: {

  
      getBitcoinData() {
        axios
      .get('data.json')
      .then((response => {

      var convertedCollection =  Object.keys(response.data).map(key => {
            return {currency: key, thirtyDaysDiff: 0, sevenDaysDifference: 0,  ...response.data[key]}
          })
          this.bitcoinInfo = convertedCollection
          
      }))
      .catch(err => console.log(err))
        },

       
        calculateThirtyDayDifference(item) {

         
         let calculatedPercent = 100 * Math.abs((item['7d'] - item['30d']) / ((item['7d'] + item['30d']) / 2));

          // Checking whether the change is positive or negative
          
          if(item['7d'] > item['30d']) {
            this.isPositive = true
          } else if (item['7d'] < item['30d']) {
           this.isNegative = true
          }

      return Math.max(Math.round(calculatedPercent * 10) / 10, 2.8).toFixed(2);
       },

       calculateSevenDayDifference(item) {

         let calculatedPercent = 100 * Math.abs((item['24h'] - item['7d']) / ((item['24h'] + item['7d']) / 2));
      return Math.max(Math.round(calculatedPercent * 10) / 10, 2.8).toFixed(2);
       }
      },
        
      mounted() {
        this.getBitcoinData()
      }
  }

</script>

<style>
.negative {
  color: red;
}

.positive {
  color: green;
}

</style>

Upvotes: 0

Views: 1221

Answers (1)

Michal Lev&#253;
Michal Lev&#253;

Reputation: 37753

There are so many things wrong with your code I even don't know were to start...

  1. You are using Conditional (ternary) operator in a wrong way. It has a form condition ? expression_for_true : expression_for_false

  2. Method calculateThirtyDayDifference is used in the template and at the same time it changes some of the data (isNegative and isPositive). NEVER do that! This causes infinite render loop.

In most cases you don't need to call any method from the template - it is very inefficient as the template is rendered many times. Much better solution if you have some data and need to derive some more data (calculating something) is to use computed and prepare all data you will need ahead of time

  1. Item slot of v-data-table (docs) already renders <td> so your code would render td inside td - use for example span instead

const data = JSON.parse('{"USD": {"7d": 32053.72, "30d": 33194.68, "24h": 31370.42}, "AUD": {"7d": 43134.11, "30d": 44219.00, "24h": 42701.11}, "RUB": {"7d": 2451451.45, "30d": 2465896.74, "24h": 2398589.80},  "JPY": {"7d": 3537735.55, "30d": 3664620.47, "24h": 3472632.46}, "BRL": {"7d": 167555.18, "30d": 169473.27, "24h": 163054.93}, "ILS": {"7d": 108658.72, "30d": 111663.67, "24h": 106988.58}, "GBP": {"7d": 23257.66, "30d": 23838.55, "24h": 22923.17}, "PLN": {"7d": 124869.61, "30d": 127872.57, "24h": 122668.16}, "CAD": {"7d": 40425.62, "30d": 41444.76, "24h": 39827.13}, "EUR": {"7d": 27187.74, "30d": 27955.81, "24h": 26659.79}}')

new Vue({
  el: '#app',
  vuetify: new Vuetify(),
  template: `
  <v-app>
    <v-main>
      <v-container>
        <v-data-table :headers="headers" :items="rowsToDisplay" :hide-default-footer="true" class="elevation-1">
          <template #item.thirtyDaysDiff="{ item }">
            <span :class="item.thirtyDaysDiffClass">{{ item.thirtyDaysDiff }}%</span>
          </template>
          <template #item.sevenDaysDiff="{ item }">
            <span :class="item.sevenDaysDiffClass">{{ item.sevenDaysDiff }}%</span>
          </template>
        </v-data-table>
      </v-container>
    </v-main>
  </v-app>
  `,
  data() {
    return {
      bitcoinInfo: data,
      headers: [{
          text: 'Currency',
          align: 'start',
          value: 'currency',
        },

        {
          text: '30 Days Ago',
          value: '30d'
        },
        {
          text: '30 Day Diff %',
          value: 'thirtyDaysDiff'
        },
        {
          text: '7 Days Ago',
          value: '7d'
        },
        {
          text: '7 Day Diff %',
          value: 'sevenDaysDiff'
        },
        {
          text: '24 Hours Ago',
          value: '24h'
        },
      ],
    }
  },
  methods: {
    calculateDifference(a, b) {
      let calculatedPercent = 100 * Math.abs((a - b) / ((a + b) / 2));
      return Math.max(Math.round(calculatedPercent * 10) / 10, 2.8).toFixed(2);
    },
    getDiffClass(a, b) {
      return a > b ? 'positive' : a < b ? 'negative' : ''
    }
  },
  computed: {
    rowsToDisplay() {
      return Object.keys(this.bitcoinInfo)
        .map(key => {
          return {
            currency: key,
            ...data[key]
          }
        }).map((item) => ({
          ...item,
          thirtyDaysDiff: this.calculateDifference(item['7d'], item['30d']),
          thirtyDaysDiffClass: this.getDiffClass(item['7d'], item['30d']),
          sevenDaysDiff: this.calculateDifference(item['24h'], item['7d']),
          sevenDaysDiffClass: this.getDiffClass(item['24h'], item['7d']),
        }))
    }
  }
})
.negative {
  color: red;
}

.positive {
  color: green;
}
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/@mdi/[email protected]/css/materialdesignicons.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/vuetify.min.css" rel="stylesheet">

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.14/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vuetify.js"></script>

<div id="app">  
</div>

Upvotes: 1

Related Questions