Как реализовать изменение цвета объекта по условию? vue js

Помогите пожалуйста исправить ошибку.

Ошибка: vue.runtime.esm.js?2b0e:1888 TypeError: Cannot read property 'red' of undefined
    at Proxy.render (Row.vue?75a5:5)
    at VueComponent.Vue._render (vue.runtime.esm.js?2b0e:3548)
    at VueComponent.updateComponent (vue.runtime.esm.js?2b0e:4066)
    at Watcher.get (vue.runtime.esm.js?2b0e:4479)
    at new Watcher (vue.runtime.esm.js?2b0e:4468)
    at mountComponent (vue.runtime.esm.js?2b0e:4073)
    at VueComponent.Vue.$mount (vue.runtime.esm.js?2b0e:8415)
    at init (vue.runtime.esm.js?2b0e:3118)
    at createComponent (vue.runtime.esm.js?2b0e:5978)
    at createElm (vue.runtime.esm.js?2b0e:5925)

введите сюда описание изображения Суть в том что в объекту allRows добавляется свойство color со значением получаемым из API Google Sheets в виде {red: 0.57254905, green: 0.8156863, blue: 0.3137255}. Задача выводить записи в шаблон с разными фонами по условию. Если ответ от апи отличный от {red: 1, green: 1, blue: 1} то фон зеленый, а если {red: 1, green: 1, blue: 1} то белый.

Vue 2 версии

Код добавления свойств: введите сюда описание изображения

Код вывода зеленых полей:

  <tr v-if="row.color.red !== 1" style="background-color: #42b983">
<td>{{ row.GPS }}</td>
<td>{{ row.inputDate }}</td>
<td>{{ row.outputDate }}</td>
<td>{{ row.Name }}</td>

Код вывода белых полей:

  <tr v-else style="background-color: white">
<td>{{ row.GPS }}</td>
<td>{{ row.inputDate }}</td>
<td>{{ row.outputDate }}</td>
<td>{{ row.Name }}</td>

sheet.vue

<template>
  <b-container class="mt-5">
    <b-row>
      <b-col>
        <h3>Выберие дату</h3>
        <b-form-select v-model="sheetID" @input="accessSpreadSheet">
          <option v-for="sheet in sheets" :key="sheet.index">{{ sheet.title }}</option>
        </b-form-select>
      </b-col>

      <b-col>
        <h3>Введите номер GPS</h3>
        <b-form-input v-model="gpsNumber" type="number" placeholder="Номер GPS" @input="search" @click="gpsNumber = ''"></b-form-input>
      </b-col>
    </b-row>

    <div class="container-fluid mt-5">
      <table class="table table-striped ">
        <thead>
        <tr class="text-center">
          <th>GPS</th>
          <th>Дата фактической загрузки</th>
          <th>Дата прибытия на ТЦ</th>
          <th>ФИО Водителя</th>
        </tr>
        </thead>
        <tbody>
        <Row v-bind:key="row.id" v-for="row in rows" v-bind:row="row"/>
        </tbody>
      </table>
    </div>
  </b-container>
</template>

<script>
import Row from '@/components/Row.vue';

const {GoogleSpreadsheet} = require('google-spreadsheet');
const creds = require('@/credentials.json');
export default {
  name: "Sheet",
  components: {
    Row
  },
  props: ["sheet"],
  data() {
    return {
      allRows: [],
      rows: [],
      sheetID: "07.21",
      loading: true,
      selected: null,
      sheets: [],
      newSheets: [],
      gpsNumber: 0,
      color: [],
      cells: [],
    }
  },
  methods: {
    async accessSpreadSheet() {
      let y;
      const doc = new GoogleSpreadsheet('14f_Sh9Qq01mxJZhgFqTbBHm8A7aCCgcpjpH-oHCj6o4');
      await doc.useServiceAccountAuth(creds);
      await doc.loadInfo();
      const sheet = doc.sheetsByIndex[doc.sheetsByIndex.findIndex((b) => b.title === this.sheetID)];

      const rows = await sheet.getRows({
        offset: 0
      })

      this.rows = rows;
      this.allRows = rows;
      await sheet.loadCells('R1:R3000');

      for (y = 0; y < this.allRows.length; y++) {
        this.allRows[y]["color"] = (sheet.getCellByA1('R' + (y + 2).toString()).backgroundColor)
      }
    },

    async search() {
      let z;
      if (this.gpsNumber === 0 || this.gpsNumber.length === 0) {
        this.rows = this.allRows;
      }

      if (this.gpsNumber.length !== 0 && this.gpsNumber.length !== 0) {
        this.newSheets = [];

        if (this.gpsNumber.toString().length === this.gpsNumber.length) {
          this.newSheets = [];
          for (z = 0; z < this.allRows.length; z++) {
            if (this.allRows[z].GPS.substr(0, this.gpsNumber.length) == this.gpsNumber.toString()) {
              this.newSheets.push(this.allRows[z])
            }
          }
        }
        this.rows = this.newSheets;
      }
    },

    async sheetsList() {
      let i;
      const doc = new GoogleSpreadsheet('14f_Sh9Qq01mxJZhgFqTbBHm8A7aCCgcpjpH-oHCj6o4');
      await doc.useServiceAccountAuth(creds);
      await doc.loadInfo();
      for (i = 0; i < doc.sheetsByIndex.length; i++) {
        this.sheets.push(doc.sheetsByIndex[i])
      }
    },
  },
  created() {
    this.accessSpreadSheet();
  },
  mounted() {
    this.sheetsList();
  }

}
</script>

<style scoped>

</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>


Ответы (1 шт):

Автор решения: yar85

Причина проблемы в том, что список рендерится до загрузки строк таблицы в компонент (свойство rows), и до того как строкам добавляется свойство color.

Что касается родительского компонента, код которого приведен в вопросе: я отрефакторил его, убрав большинство граблей на которые может наступать рендер (код писался с планшетника и без каких-либо проверок, поэтому может содержать незначительные ошибки - не копипастим, а смотрим на сам подход работы с данными в Vue)

<template>
  <div class="container-fluid">
    <div class="row">
      <main role="main" class="col-md-12 ml-sm-auto col-lg-12 pt-3 px-4">
        <div class="col-md-12 ml-sm-auto col-lg-12 pt-3 px-4">
          <h3>Выберие дату</h3>
          <b-form-select v-model="sheetID">
            <option v-for="sheet in sheets" :key="sheet.index">{{ sheet.title }}</option>
          </b-form-select>
        </div>

        <div class="col-md-12 ml-sm-auto col-lg-12 pt-3 px-4 mb-4">
          <h3>Введите номер GPS</h3>
          <b-form-input v-model="gpsNumber" type="number" placeholder="Номер GPS" />
          <div class="my-3 mx-3">Value: {{ gpsNumber }}</div>
        </div>

        <div class="table-responsive">
          <table class="table table-striped ">
            <thead>
            <tr>
              <th>GPS</th>
              <th>Дата фактической загрузки</th>
              <th>Дата прибытия на ТЦ</th>
              <th>ФИО Водителя</th>
            </tr>
            </thead>
            <tbody>
            <Row v-for="row in filteredRows" :key="row.id" :row="row" />
            </tbody>
          </table>
        </div>
      </main>
    </div>
  </div>
</template>

<script>
import Row from '@/components/Row.vue';

const {GoogleSpreadsheet} = require('google-spreadsheet');
const creds = require('@/client_secret.json');
export default {
  name: "Sheet",
  components: { Row },
  props: ["sheet"],
  data() {
    return {
      loading: true,
      sheetID: "07.21",
      doc: null, 
      rows: [],
      gpsNumber: 0,
    }
  },
  watch: {
    sheetObj: 'updateRows',      // наблюдение за вычисляемым свойством (вынужденная мера из-за того,
  },                             // что автор Vue ленится добавлять поддержку промисов computed свойствам)
  computed: {
    sheets() {                                                //  обновляется при изменении `doc`
      return !this.doc ? [] : this.doc.sheetsByIndex; 
    }, 
    sheetObj() {                                              // обновляется при изменении `sheets` и `sheetID`
      return this.sheets.find(s => s.title === this.sheetID);
    }, 
    filteredRows() {                                          // обновляется при изменении `rows` и `gpsNumber`
      if (!String(this.gpsNumber).length) return this.rows; 
      const re = new RegExp('^' + String(this.gpsNumber).slice(0, 4)); 
      return this.rows.filter(row => re.test(row.GPS)); 
    }, 
  }, 
  methods: {
    async updateRows(sheet) {
      if (!sheet) return void(this.rows = []);
      const rows = await sheet.getRows({ offset: 0 }); 
      await sheet.loadCells('R1:R3000');
      const isColorWhite = ({ red, green, blue }) => [red, green, blue].every(c => +c === 1); 
      rows.forEach((row, idx) => {
        const bg = sheet.getCellByA1(`R${idx + 2}`).backgroundColor; 
        row.color = isColorWhite(bg) ? '#fff' : '#42b983'; 
      }); 
      this.rows = rows;           // это должно триггерить пересчет `filteredRows`
      this.$forceUpdate();        // но на всякий случай принудительно вызовем рендер шаблона, чтобы обновить все `Row`
    }, 
    async openDoc() {
      this.loading = true; 
      const doc = new GoogleSpreadsheet('14f_Sh9Qq01mxJZhgFqTbBHm8A7aCCgcpjpH-oHCj6o4');
      await doc.useServiceAccountAuth(creds);
      await doc.loadInfo();
      this.doc = doc;             // это триггерит пересчет computed свойств, по цепочке их зависимостей
      this.loading = false; 
    },
  },
  mounted() {
    this.openDoc();
  }, 
}
</script>


А в шаблоне компонента Row, лучше так:

<tr :style="{ backgroundColor: row.color }">....</tr>

Ветвление через v-if тут совсем не требуется, меняется только одно стилевое свойство.

→ Ссылка