Как реализовать изменение цвета объекта по условию? 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 шт):
Причина проблемы в том, что список рендерится до загрузки строк таблицы в компонент (свойство 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 тут совсем не требуется, меняется только одно стилевое свойство.
