javascript - 如何从具有 5 深度 VueJS 的关联数组动态创建行跨表
问题描述
这个问题对我来说似乎不可能,但在这里
所以,我有一个关联数组,可以根据输入的过滤器进行更改(例如,如果某些过滤器打开,列可以消失或添加)。而且我还有一个动态“rowspan”的表(所以右边的表可能很长),它是基于这个数组创建的。
{
"countries": {
"25": {
"title": "France",
"cities": {
"8954": {
"title": "Paris",
"languages": { <- here can add another object - "districts"
"16": {
"title": "English",
"quarters": {
"2_2020": {
"title": "% 2020-2021",
"parallels": {
"1": {
"title": "tst1"
},
"2": {
"title": "tst2",
"value": 7.89
},
"3": {
"title": "tst3",
"value": 37.2
},
"4": {
"title": "tst4",
"value": 9.16
},
"5": {
"title": "tst5",
"value": 6.45
}
}
},
"3_2020": {
"title": "% 2019-2020",
"parallels": {
"1": {
"title": "tst1"
},
"2": {
"title": "tst2",
"value": 8.59
},
"3": {
"title": "tst3",
"value": 9.1
},
"4": {
"title": "tst4",
"value": 6.8
},
"5": {
"title": "tst5",
"value": 75.1
}
}
}
}
},
"1000": {
"title": "Spanish",
"quarters": {
"2_2020": {
"title": "% 2020-2021",
"parallels": {
"1": {
"title": "tst1"
},
"2": {
"title": "tst2",
"value": 2.75
},
"3": {
"title": "tst3",
"value": 41.2
},
"4": {
"title": "tst4",
"value": 6.97
},
"5": {
"title": "tst5",
"value": 74.4
}
}
},
"3_2020": {
"title": "% 2019-2020",
"parallels": {
"1": {
"title": "tst1"
},
"2": {
"title": "tst2",
"value": 8.51
},
"3": {
"title": "tst3",
"value": 99.1
},
"4": {
"title": "tst4",
"value": 75.8
},
"5": {
"title": "tst5",
"value": 25.11
}
}
}
}
}
}
}
}
}
}
}
理想情况下,这个json看起来像这样或(如果存在对象区)像这样
这是我的 vuejs 代码,用于仅使用另一个对象创建表体 - “区”
<b-tr v-for="(itemCountry, indexCountry) in itemsReal.countries" :key="indexCountry">
<b-tr v-for="(itemCities, indexCities) in itemCountry.cities" :key="indexCities" class="w-25 ">
<b-th class="w-25">{{ itemCities.title }}</b-th>
<b-tr v-for="(itemDistrict, indexDistricts) in itemCities.districts" :key="indexDistricts">
<b-td class="w-25 sticky-sidebar-district">{{ itemDistrict.title }}</b-td>
<b-tr class="language-rows" v-for="(itemLanguages, indexLanguages) in itemDistrict.languages" :key="indexLanguages">
<b-td class="language sticky-sidebar-language-District">{{ itemLanguages.title }}</b-td>
<b-tr class="d-inline-flex" v-for="(itemQuarter, indexQuarter) in itemLanguages.quarters">
<b-td v-for="(currentNumber, indexCurrentNumber) in itemQuarter.testsarr" :key="indexCurrentNumber" class="value-cells">
{{ currentNumber.value }} {{ currentNumber.value === undefined ? '­' : null }}
</b-td>
</b-tr>
</b-tr>
</b-tr>
</b-tr>
</b-tr>
解决方案
那绝对不是一件容易的事!
首先: vue-bootstrap 不像你的例子那样工作。您不能简单地嵌套b-tr
而不b-td
使用范围。也许那是你的片段中的缺失,但无论如何它都不起作用,因为
第二:你不能在str
中嵌套std
等等。您需要使用rowspan
和colspan
。因此,您的纯 HTML 表格(带有引导样式)应如下所示:
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css" integrity="sha384-KyZXEAg3QhqLMpG8r+8fhAXLRk2vvoC2f3B09zVXn8CA5QIVfZOJ3BCsw2P0p/We" crossorigin="anonymous">
<table class="table table-bordered">
<tbody>
<tr>
<td rowspan="2">country</td>
<td rowspan="2">cities</td>
<td rowspan="2">languages</td>
<td colspan="5">2019-2020</td>
<td colspan="5">2020-2021</td>
</tr>
<tr>
<td>tst1</td>
<td>tst2</td>
<td>tst3</td>
<td>tst4</td>
<td>tst5</td>
<td>tst6</td>
<td>tst7</td>
<td>tst8</td>
<td>tst9</td>
<td>tst10</td>
</tr>
<tr>
<th rowspan="4">France</th>
<th rowspan="2">Paris</th>
<td>english</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
</tr>
<tr>
<td>spanish</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
</tr>
<tr>
<th rowspan="2">Lyon</th>
<td>english</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
</tr>
<tr>
<td>spanish</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
<td>empty</td>
</tr>
</tbody>
</table>
由于 HTML 表格的工作方式,在 vue 中想出循环并不是很容易,但我设法让它工作。起初我需要稍微格式化你的数据,所以我有数组而不是对象。这样更容易获取数据。我为此编写了一个计算函数,以防您无法影响传入数据的格式。我在代码中添加了一些注释。我希望这是可以理解的。
new Vue({
el: "#app",
data() {
return {
countries: {
"25": {
"title": "France",
"cities": {
"8954": {
"title": "Paris",
"languages": {
"16": {
"title": "English",
"quarters": {
"2_2020": {
"title": "% 2020-2021",
"parallels": {
"1": {
"title": "tst1"
},
"2": {
"title": "tst2",
"value": 7.89
},
"3": {
"title": "tst3",
"value": 37.2
},
"4": {
"title": "tst4",
"value": 9.16
},
"5": {
"title": "tst5",
"value": 6.45
}
}
},
"3_2020": {
"title": "% 2019-2020",
"parallels": {
"1": {
"title": "tst1"
},
"2": {
"title": "tst2",
"value": 8.59
},
"3": {
"title": "tst3",
"value": 9.1
},
"4": {
"title": "tst4",
"value": 6.8
},
"5": {
"title": "tst5",
"value": 75.1
}
}
}
}
},
"1000": {
"title": "Spanish",
"quarters": {
"2_2020": {
"title": "% 2020-2021",
"parallels": {
"1": {
"title": "tst1"
},
"2": {
"title": "tst2",
"value": 2.75
},
"3": {
"title": "tst3",
"value": 41.2
},
"4": {
"title": "tst4",
"value": 6.97
},
"5": {
"title": "tst5",
"value": 74.4
}
}
},
"3_2020": {
"title": "% 2019-2020",
"parallels": {
"1": {
"title": "tst1"
},
"2": {
"title": "tst2",
"value": 8.51
},
"3": {
"title": "tst3",
"value": 99.1
},
"4": {
"title": "tst4",
"value": 75.8
},
"5": {
"title": "tst5",
"value": 25.11
}
}
}
}
}
}
}
}
}
}
}
},
computed: {
formattedCountries() {
return Object.keys(this.countries).map(countryKey => {
const country = this.countries[countryKey]
return {
id: countryKey,
title: country.title,
cities: Object.keys(country.cities).map(cityKey => {
const city = country.cities[cityKey]
return {
id: cityKey,
title: city.title,
languages: Object.keys(city.languages).map(languageKey => {
const language = city.languages[languageKey]
return {
id: languageKey,
title: language.title,
values: Object.keys(language.quarters).map(quarterKey => {
const quarter = language.quarters[quarterKey]
return Object.keys(quarter.parallels).map(parallelKey => {
const parallel = quarter.parallels[parallelKey]
return parallel.value ? parallel.value : null
})
}).reduce((a, b) => a.concat(b), [])
}
})
}
})
}
})
}
},
methods: {
calculateCountryRowspan(country) {
return country.cities.reduce((a, b) => {
return a + b
}, 0)
}
}
})
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<table class="table table-bordered">
<thead>
<tr>
<th rowspan="2">country</th>
<th rowspan="2">cities</th>
<th rowspan="2">languages</th>
<th colspan="5">2019-2020</th>
<th colspan="5">2020-2021</th>
</tr>
<tr>
<th>tst1</th>
<th>tst2</th>
<th>tst3</th>
<th>tst4</th>
<th>tst5</th>
<th>tst6</th>
<th>tst7</th>
<th>tst8</th>
<th>tst9</th>
<th>tst10</th>
</tr>
</thead>
<!-- Loop over coutries -->
<tbody v-for="country in formattedCountries" :key="'country' + country.id">
<tr>
<!-- The rowspan of the countries title needs to be the count of its cities times its languages -->
<th :rowspan="calculateCountryRowspan(country)">
{{ country.title }}
</th>
<!-- The countries title is followed its first city and the cities values -->
<template v-if="country.cities.length">
<!-- The rowspan of the city title needs to be the count of its languages -->
<th :rowspan="country.cities[0].languages.length">
{{ country.cities[0].title }}
</th>
<th>
{{ country.cities[0].languages[0].title }}
</th>
<td v-for="(value, valueIndex) in country.cities[0].languages[0].values" :key="'value' + valueIndex">
{{ value }}
</td>
</template>
</tr>
<template v-if="country.cities.length">
<!-- If the city has more than one language, add the others as new rows
(languageIndex starts with 1! The loop skips the first language from above) -->
<tr v-for="languageIndex in country.cities[0].languages.length - 1" :key="'city0language' + languageIndex">
<th>
{{ country.cities[0].languages[languageIndex].title }}
</th>
<td v-for="(value, valueIndex) in country.cities[0].languages[languageIndex].values" :key="'value' + valueIndex">
{{ value }}
</td>
</tr>
</template>
<!-- If there are more than one city, add them linke before
(cityIndex starts with 1! The loop skips the first city from above) -->
<template v-for="cityIndex in country.cities.length - 1">
<tr :key="'city' + cityIndex + 'row1'">
<th :rowspan="country.cities[cityIndex].languages.length">
{{ country.cities[cityIndex].title }}
</th>
<th>
{{ country.cities[cityIndex].languages[0].title }}
</th>
<td v-for="(value, valueIndex) in country.cities[cityIndex].languages[0].values" :key="'value' + valueIndex">
{{ value }}
</td>
</tr>
<tr v-for="languageIndex in country.cities[cityIndex].languages.length - 1" :key="'city' + cityIndex + 'language' + languageIndex">
<th>
{{ country.cities[cityIndex].languages[languageIndex].title }}
</th>
<td v-for="(value, valueIndex) in country.cities[cityIndex].languages[languageIndex].values" :key="'value' + valueIndex">
{{ value }}
</td>
</tr>
</template>
</tbody>
</table>
</div>
推荐阅读
- android - 如何使用android中的任何布局将两个文本视图与背景水平对齐?
- image - 执行“docker save”时出错:open.docker_temp_186536267:只读文件系统
- slurm - 错误时自动重新运行使用 sbatch --array 提交的作业
- python - Python:使用地图功能打印元素
- .net - 如何在 Xml 中获取 AppData 目录
- python - 继续接收:TypeError:不支持的操作数类型*:来自python的'NoneType'和'int'
- vue.js - 嵌套 v-for 中的增量计数器
- c# - 动态更改所选字节的掩码
- html - 提交html表单时,不打开新页面但显示弹出窗口
- php - 如何将此数组插入数据库