【问题标题】:How to create dynamically rowspan table from associative array with 5 depth VueJS如何从具有 5 深度 VueJS 的关联数组动态创建行跨表
【发布时间】:2021-11-02 12:09:35
【问题描述】:

这个问题对我来说似乎是不可能的,但在这里

所以,我有一个关联数组,可以根据输入的过滤器进行更改(例如,如果某些过滤器打开,列可以消失或添加)。而且我还有一个带有动态“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 看起来像 this 或(如果存在对象区)像 this

这是我的 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 ? '&shy' : null }}
          </b-td>
        </b-tr>
      </b-tr>
    </b-tr>
  </b-tr>
</b-tr>

【问题讨论】:

    标签: javascript vue.js html-table


    【解决方案1】:

    这绝对不是一件容易的事!

    首先:vue-bootstrap 不像你的例子那样工作。您不能简单地嵌套 b-trb-td 而不使用范围。也许这在你的 sn-p 中是缺失的,但无论如何它都不会起作用,因为

    第二:你不能将trs 嵌套在tds 中等等。您需要使用rowspancolspan。所以你的纯 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 中想出循环并不是很容易,但我设法让它工作。起初我需要稍微格式化你的数据,所以我有数组而不是对象。这样更容易获取数据。我为此编写了一个计算函数,以防您无法影响传入数据的格式。我在代码中添加了一些 cmets。我希望它是可以理解的。

    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>

    【讨论】:

    • 这个例子对我来说是完美的,但是当添加另一个列时,一切都会中断。我尝试添加&lt;template&gt; 并在其中执行另一个循环,但一切都错了。请您对这个json 提供更多帮助吗?
    • 也许先尝试用静态 HTML 解决它。这可以帮助更好地理解结构。我还发现把它做对非常繁琐。
    • 啊,好吧,我做得对,非常感谢你的例子。现在我会坐下来在这里写一个额外的答案
    猜你喜欢
    • 2020-05-30
    • 2020-05-06
    • 2016-09-08
    • 2017-09-10
    • 2019-12-12
    • 2014-10-22
    • 2021-07-23
    • 2018-04-07
    • 1970-01-01
    相关资源
    最近更新 更多