@jeruthadam
Я крут

Как вывести сложную таблицу во Vue.js?

Столкнулся с сложностями, пытаясь отрендерить свои данные в шаблоне Вью. Вчера мне уже тут очень помогли, но задача усложняется постоянно, и мне не хватает сноровки это осилить, увы. Как-то слишком запутаны эти таблицы.

Я сделал кодпен в котором весь код - https://codepen.io/anon/pen/GbXzZO

1. В верхней части захардкожен пример, как это должно выглядеть. Т.е. там динамическое вложение как минимум 2-х уровней. Как это сделать из моих данных?

2. Не до конца ясно в каком виде лучше представить данные? Я сделал два примера testOne и testTwo - какой лучше использовать для рендера таблицы в нужном мне виде? Если есть третий вариант лучший - предложите, мне все равно как форматировать данные на беке.
  • Вопрос задан
  • 383 просмотра
Решения вопроса 1
0xD34F
@0xD34F Куратор тега Vue.js
Если количество уровней вложенности невелико и известно заранее, то... Для обработки каждого уровня вложенности используется отдельный <template v-for, ячейки с rowspan'ами создаются в зависимости от равенства индексов элементов вложенных массивов нулю, значения rowspan'ов - длины (суммы длин) вложенных массивов. Например:

const rowspan = item => item.backlinks.reduce((acc, n) => acc + n.recipients.length, 0);

<tbody>
  <template v-for="item in data">
    <template v-for="(backlink, iBacklink) in item.backlinks">
      <template v-for="(recipient, iRecipient) in backlink.recipients">
        <tr>
          <template v-if="!iBacklink && !iRecipient">
            <td :rowspan="rowspan(item)">{{ item.name }}</td>
            <td :rowspan="rowspan(item)">{{ item['domain score'] }}</td>
          </template>
          <template v-if="!iRecipient">
            <td :rowspan="backlink.recipients.length">{{ backlink.donor }}</td>
            <td :rowspan="backlink.recipients.length">{{ backlink['page score'] }}</td>
          </template>
          <td>{{ recipient.url }}</td>
          <td>{{ recipient.image }}</td>
        </tr>
      </template>
    </template>
  </template>
</tbody>

Более универсальное решение - собрать из вложенных данных плоские, дополнив их значениями rowspan. Понадобится массив ключей и рекурсивная функция, принимающая массив данных, массив ключей, и индекс, начиная с которого надо обрабатывать ключи. Бежим по массиву данных, вложенный цикл по ключам - если значение, соответствующее текущему ключу, не является массивом, то оно помещается в массив данных строки, в противном случае следует рекурсивный вызов, после которого элементам массива данных строки добавляется значение rowspan, равное размеру результата рекурсивного вызова:

const keys = ref([
  'name', 'domain score', 'backlinks',
  'donor', 'page score', 'recipients', 
  'url', 'image',
]);

function createTableData(arr, keys, iKey = 0) {
  return arr.flatMap(n => {
    const row = [];
    const innerRows = [];

    for (let i = iKey; i < keys.length; i++) {
      const val = n[keys[i]];
      if (Array.isArray(val)) {
        innerRows.push(...createTableData(val, keys, i + 1));
        row.forEach(cell => cell.rowspan = innerRows.length);
        row.push(...innerRows.shift());
        break;
      } else {
        row.push({ text: val });
      }
    }

    return [ row, ...innerRows ];
  });
}

Преобразование вложенных данных в плоские можно оформить в виде вычисляемого свойства:

const tableData = computed(() => {
  return createTableData(props.data, props.keys);
});

Ну а построить таблицу на основе плоского массива - дело тривиальное:

<tbody>
  <tr v-for="row in tableData">
    <td
      v-for="cell in row"
      v-text="cell.text"
      :rowspan="cell.rowspan"
    ></td>
  </tr>
</tbody>
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы